调试器支持多个保存特定值的伪寄存器。
调试器将 自动伪寄存器 设置为某些有用的值。 用户定义的伪寄存器 是可以写入或读取的整数变量。
所有伪寄存器都以美元符号 ($) 开头。 如果使用 MASM 语法,可以在美元符号之前添加 At 符号(@)。 这个 @ 符号告知调试器接下来的标记是寄存器或伪寄存器,而不是符号。 如果省略了 at 符号,调试器响应速度会变慢,因为它必须搜索整个符号表。
例如,下面的两个命令生成相同的输出,但第二个命令速度更快。
0:000> ? $exp
Evaluate expression: 143 = 0000008f
0:000> ? @$exp
Evaluate expression: 143 = 0000008f
如果符号与伪寄存器同名,则必须添加 at 符号。
如果使用C++表达式语法,则始终需要 at sign ( @ )。
r (Registers) 命令是此规则的例外。 调试器始终将其第一个参数解释为寄存器或伪寄存器。 如果不需要或不允许使用 at 符号。如果 r 命令有第二个参数,则根据默认表达式语法解释。 如果默认表达式语法C++,则必须使用以下命令将 $t 2 伪寄存器复制到 $t 1 伪寄存器。
0:000> r $t1 = @$t2
自动 Pseudo-Registers
调试器会自动设置以下伪寄存器。
| 伪寄存器 | DESCRIPTION |
|---|---|
$ea |
执行的最后一个指令的有效地址。 如果此指令没有有效地址,调试器将显示“注册错误”。 如果此指令有两个有效地址,调试器将显示第一个地址。 |
$ea 2 |
执行的最后一个指令的第二个有效地址。 如果此指令没有两个有效地址,调试器将显示“寄存器错误”。 |
$exp |
最后被求值的表达式。 |
$ra |
当前位于堆栈上的返回地址。 此地址在执行命令中特别有用。 例如, g @$ra 一直持续到找到返回地址(虽然 gu (Go Up) 是当前函数“退出”的更精确的有效方法。 |
$ip |
指令指针寄存器。 基于 x86 的处理器: 与 eip 相同。 基于 Itanium 的处理器: 与 iip 相关。 (有关详细信息,请参阅下表后面的说明。)基于 x64 的处理器:与 rip相同。 |
$eventip |
当前事件时的指令指针。 此指针通常与 $ip匹配,除非切换线程或手动更改指令指针的值。 |
$previp |
上一个事件时的指令指针。 (闯入调试器算作事件。 |
$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 的断点。 数字 始终为十进制数。 如果没有断点 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_param1 |
如果发生了 bug 检查,则这是参数 1 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param2 |
如果发生了错误检查,则这是参数 2 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param3 |
如果发生了故障检查,则这是参数 3 的值。 适用于实时内核模式调试和内核故障转储。 |
$bug_param4 |
如果发生了 bug 检查,则此参数为参数 4 的值。 适用于实时内核模式调试和内核故障转储。 |
某些伪寄存器在某些调试方案中可能不可用。 例如,调试用户模式小型转储文件或某些内核模式转储文件时,不能使用 $peb、 $tid和 $tpid 。 在某些情况下,你可以从 ~(线程状态) 中学习线程信息,但不能从 $tid学习。 不能在第一个调试器事件上使用 $previp 伪寄存器。 除非进行分支跟踪,否则不能使用 $relip 伪寄存器。 如果使用不可用的伪寄存器,则会发生语法错误。
保存结构地址(例如$thread、$proc、$teb、$peb和$lastclrex)的伪寄存器将根据C++表达式计算器中的适当数据类型(而不是 MASM 表达式计算器)计算。 例如,命令 ? $teb 显示 TEB 的地址,而 命令 ?? @$teb 显示整个 TEB 结构。 有关详细信息,请参阅 “计算表达式”。
在基于 Itanium 的处理器上,iip 寄存器是 包对齐的,这意味着它指向包含当前指令的捆绑包中的槽位 0,尽管执行的可能是其他槽位。 因此 ,iip 不是完整的指令指针。 $ip伪寄存器是实际的指令指针,包括指令包和槽位。 保存地址指针($ra、$retreg、$eventip、$previp、$relip和$exentry)的其他伪寄存器在所有处理器上具有相同 $ip的结构。
可以使用 r 命令更改 $ip的值。 此更改还会自动更改相应的寄存器。 当执行恢复时,它将在新的指令指针地址处重新开始。 此寄存器是唯一可以手动更改的自动伪寄存器。
注意在 MASM 语法中,可以使用句点 (. ) 指示$ip伪寄存器。 在句点前面不要添加 at 符号 (@),也不要将句点作为 r 命令的第一个参数。 C++ 表达式中不允许使用此语法。
自动伪寄存器类似于 自动别名。 但是,可以将自动别名与别名相关的令牌(如 ${ })一起使用,并且不能将伪寄存器用于此类令牌。
User-Defined 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 或重复该过程。