Pseudo-Register 语法

调试器支持多个保存特定值的伪寄存器。

调试器将 自动伪寄存器 设置为某些有用的值。 用户定义的伪寄存器 是可以写入或读取的整数变量。

所有伪寄存器都以美元符号 ($) 开头。 如果使用 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 或重复该过程。