伪寄存器语法
调试器支持多个包含特定值的伪寄存器。
调试器将 自动伪寄存器 设置为某些有用值。 用户定义的伪寄存器 是可以写入或读取的整数变量。
所有伪寄存器都以美元符号开头, $ () 。 如果使用 MASM 语法,则可以在美元符号前添加 at 符号 ( @ ) 。 此 at 符号告知调试器以下令牌是寄存器或伪寄存器,而不是符号。 如果省略 at 符号,调试器的响应速度会更慢,因为它必须搜索整个符号表。
例如,以下两个命令生成相同的输出,但第二个命令速度更快。
0:000> ? $exp
Evaluate expression: 143 = 0000008f
0:000> ? @$exp
Evaluate expression: 143 = 0000008f
如果存在与伪寄存器同名的符号,则必须添加 at 符号。
如果使用 C++ 表达式语法,则始终需要 at 符号 ( @ ) 。
r (Registers) 命令是此规则的例外。 调试器始终将其第一个参数解释为寄存器或伪寄存器。 (at 符号不是必需的或不允许的。) 如果 r 命令有第二个参数,则根据默认表达式语法解释该参数。 如果默认表达式语法为 C++,则必须使用以下命令将 $t 2 伪寄存器复制到 $t 1 伪寄存器。
0:000> r $t1 = @$t2
自动 Pseudo-Registers
调试器会自动设置以下伪寄存器。
伪寄存器 | 说明 |
---|---|
$ea |
执行的最后一个指令的有效地址。 如果此指令没有有效地址,调试器将显示“错误注册错误”。 如果此指令有两个有效地址,则调试器将显示第一个地址。 |
$ea 2 |
执行的最后一个指令的第二个有效地址。 如果此指令没有两个有效地址,调试器将显示“错误寄存器错误”。 |
$exp |
计算的最后一个表达式。 |
$ra |
当前位于堆栈上的返回地址。 此地址在执行命令中特别有用。 例如, g @$ra 一直持续到找到返回地址 (尽管 gu (Go Up) 是“单步执行”当前函数) 的更精确有效方法。 |
$ip |
指令指针寄存器。 基于 x86 的处理器: 与 eip 相同。 基于 Itanium 的处理器: 与 iip 相关。 (有关详细信息,请参阅下表后面的说明。) 基于 x64 的处理器: 与 rip 相同。 |
$eventip |
当前事件时的指令指针。 除非你切换了线程或手动更改了指令指针的值,否则此指针通常与 $ip匹配。 |
$previp |
上一个事件时的指令指针。 (中断调试器计为 event.) |
$relip |
与当前事件相关的指令指针。 当进行分支跟踪时,此指针是指向分支源的指针。 |
$scopeip |
当前 本地上下文 的指令指针 (也称为 范围) 。 |
$exentry |
当前进程的第一个可执行文件的入口点的地址。 |
$retreg |
主返回值寄存器。 基于 x86 的处理器: 与 eax 相同。 基于 Itanium 的处理器: 与 ret0 相同。 基于 x64 的处理器: 与 rax 相同。 |
$retreg 64 |
主返回值寄存器,采用 64 位格式。 x86 处理器: 与 edx:eax 对相同。 |
$csp |
当前调用堆栈指针。 此指针是最能代表调用堆栈深度的寄存器。 基于 x86 的处理器: 与 esp 相同。 基于 Itanium 的处理器: 与 bsp 相同。 基于 x64 的处理器: 与 rsp 相同。 |
$p |
最后 一个 d* (显示内存) 命令打印的值。 |
$proc |
当前进程 (地址,即 EPROCESS 块的地址) 。 |
$thread |
当前线程的地址。 在内核模式调试中,此地址是 ETHREAD 块的地址。 在用户模式调试中,此地址是 TEB) (线程环境块的地址。 |
$peb |
进程环境块的地址 (当前进程的 PEB) 。 |
$teb |
线程环境块的地址 (当前线程的 TEB) 。 |
$tpid |
拥有当前线程的进程的进程 ID (PID) 。 |
$tid |
当前线程的线程 ID。 |
$dtid |
|
$dpid |
|
$dsid |
|
$bp编号 |
相应断点的地址。 例如, $bp 3 (或 $bp 03) 是指断点 ID 为 3 的断点。 Number 始终为十进制数。 如果没有断点的 ID 为 Number, $bpNumber 的计算结果为零。 有关断点的详细信息,请参阅 使用断点。 |
$frame |
当前帧索引。 此索引与 .frame (Set Local Context) 命令使用的帧编号相同。 |
$dbgtime |
当前时间,根据运行调试器的计算机。 |
$callret |
.call (调用函数) 调用或用于 .fnret /s 命令的最后一个函数的返回值。 $callret 的数据类型是此返回值的数据类型。 |
$extret |
|
$extin |
|
$clrex |
|
$lastclrex |
仅限托管调试: 上次遇到的公共语言运行时 (CLR) 异常对象的地址。 |
$ptrsize |
指针的大小。 在内核模式下,此大小是目标计算机上的指针大小。 |
$pagesize |
一页内存中的字节数。 在内核模式下,此大小是目标计算机上的页面大小。 |
$pcr |
|
$pcrb |
|
$argreg |
|
$exr_chance |
当前异常记录的几率。 |
$exr_code |
当前异常记录的异常代码。 |
$exr_numparams |
当前异常记录中的参数数。 |
$exr_param0 |
当前异常记录中参数 0 的值。 |
$exr_param1 |
当前异常记录中参数 1 的值。 |
$exr_param2 |
当前异常记录中参数 2 的值。 |
$exr_param3 |
当前异常记录中参数 3 的值。 |
$exr_param4 |
当前异常记录中参数 4 的值。 |
$exr_param5 |
当前异常记录中参数 5 的值。 |
$exr_param6 |
当前异常记录中参数 6 的值。 |
$exr_param7 |
当前异常记录中参数 7 的值。 |
$exr_param8 |
当前异常记录中参数 8 的值。 |
$exr_param9 |
当前异常记录中参数 9 的值。 |
$exr_param10 |
当前异常记录中参数 10 的值。 |
$exr_param11 |
当前异常记录中参数 11 的值。 |
$exr_param12 |
当前异常记录中参数 12 的值。 |
$exr_param13 |
当前异常记录中参数 13 的值。 |
$exr_param14 |
当前异常记录中参数 14 的值。 |
$bug_code |
如果发生了 bug 检查,则这是 bug 代码。 适用于实时内核模式调试和内核故障转储。 |
$bug_param1 |
如果出现 bug 检查,则此参数为参数 1 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param2 |
如果出现 bug 检查,则此参数为参数 2 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param3 |
如果出现 bug 检查,则这是参数 3 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param4 |
如果出现 bug 检查,则此参数为参数 4 的值。 适用于实时内核模式调试和内核故障转储。 |
其中一些伪寄存器在某些调试方案中可能不可用。 例如,在调试用户模式小型转储或某些内核模式转储 文件时,不能使用$peb、 $tid和 $tpid 。 在某些情况下,你可以从 ~ (线程状态) 了解线程 信息,但不能从 $tid中了解线程信息。 不能在第一个调试器事件上使用 伪注册$previp 。 除非是分支跟踪,否则不能使用 $relip 伪注册。 如果使用不可用的伪寄存器,则会发生语法错误。
将根据 C++ 表达式计算器中的适当数据类型来计算包含结构地址的伪寄存器(如 $thread、 $proc、 $teb、 $peb 和 $lastclrex )。 例如,命令 ? $teb 显示 TEB 的地址,而命令 ?? @$teb 显示整个 TEB 结构。 有关详细信息,请参阅 计算表达式。
在基于 Itanium 的处理器上, iip 寄存器是 捆绑对齐的,这意味着它指向包含当前指令的捆绑包中的槽 0,即使正在执行不同的槽。 因此 ,iip 不是完整的指令指针。 伪寄存器$ip是实际的指令指针,包括捆绑包和槽。 保存地址指针的其他伪寄存器 ($ra、 $retreg、 $eventip、 $previp、 $relip和 $exentry) 具有与所有处理器上的 $ip 相同的结构。
可以使用 r 命令更改 $ip 的值。 此更改还会自动更改相应的寄存器。 当执行恢复时,它将在新的指令指针地址处恢复。 此寄存器是唯一可以手动更改的自动伪寄存器。
注意在 MASM 语法中,可以使用句点 ( .) 指示$ip伪注册。 不要在此句点之前添加 at 符号 (@) ,也不要将句点用作 r 命令的第一个参数。 C++ 表达式中不允许使用此语法。
自动伪寄存器类似于 自动别名。 但是,可以将自动别名与别名相关的标记一起使用, (例如 ${ }) ,并且不能将伪寄存器与此类令牌一起使用。
用户定义的 Pseudo-Registers
有 20 个用户定义的伪寄存器 ($t 0、 $t 1、... $t 19) 。 这些伪寄存器是可以通过调试器读取和写入的变量。 可以在这些伪寄存器中存储任何整数值。 它们作为循环变量特别有用。
若要写入其中一个伪寄存器,请使用 r (Registers) 命令,如以下示例所示。
0:000> r $t0 = 7
0:000> r $t1 = 128*poi(MyVar)
与所有伪寄存器一样,可以在任何表达式中使用用户定义的伪寄存器,如以下示例所示。
0:000> bp $t3
0:000> bp @$t4
0:000> ?? @$t1 + 4*@$t2
伪寄存器始终类型化为整数,除非将 ? 开关与 r 命令一起使用。 如果使用此开关,伪寄存器将获取分配给它的任何类型。 例如,以下命令将 UNICODE_STRING** 类型和0x0012FFBC值分配给 $t 15。
0:000> r? $t15 = * (UNICODE_STRING*) 0x12ffbc
启动调试器时,用户定义的伪寄存器使用零作为默认值。
注意 别名 $u 0、 $u 1、... 、$u 9 不是伪寄存器,尽管它们的外观相似。 有关这些别名的详细信息,请参阅 使用别名。
示例
以下示例设置一个断点,该断点在当前线程每次调用 NtOpenFile 时都会命中。 但是,当其他线程调用 NtOpenFile 时,不会命中此断点。
kd> bp /t @$thread nt!ntopenfile
示例
以下示例执行命令,直到寄存器保留指定值。 首先,将以下代码用于条件单步执行,并放入名为“eaxstep”的脚本文件中。
.if (@eax == 1234) { .echo 1234 } .else { t "$<eaxstep" }
接下来,发出以下命令。
t "$<eaxstep"
调试器执行一个步骤,然后运行命令。 在这种情况下,调试器运行脚本,该脚本显示 1234 或重复该过程。