伪寄存器语法

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

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

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