在WinDbg命令中,如何引用与全局变量同名的寄存器



假设我想使用Windows的WinDbg、cdb或ntsd调试器来调试这个程序:

/* test.c */
#include <stdio.h>
int rip = 42;
int main(void)
{
puts("Hello world!");
return (0);
}

我为AMD64编译了这个程序,并在WinDbg下运行。我在main()中设置了一个断点,当断点命中时,我想检查RIP寄存器(程序计数器)中的值,如果该值被视为指针,则检查该值周围的内存。

我可以直接用r rip看到寄存器的值,但当我试图查看该地址周围的内存时,WinDbg会向我显示一个不同的地址!在读取了test.pdb中的符号后,WinDbg发现rip是C代码中声明的全局变量,并向我展示了&rip周围的内存。

0:000> bu test!main
0:000> g
Breakpoint 0 hit
test!main:
00007ff6`de1868d0 4883ec28        sub     rsp,28h
0:000> r rip
rip=00007ff6de1868d0
0:000> db rip
00007ff6`de1f2000  2a 00 00 00 ff ff ff ff-01 00 00 00 00 00 00 00  *...............
00007ff6`de1f2010  01 00 00 00 02 00 00 00-ff ff ff ff ff ff ff ff  ................
00007ff6`de1f2020  00 00 00 00 00 00 00 00-43 46 92 e5 1b df 00 00  ........CF......
00007ff6`de1f2030  bc b9 6d 1a e4 20 ff ff-00 00 00 00 00 00 00 00  ..m.. ..........
00007ff6`de1f2040  00 01 00 00 00 00 00 00-ca b0 1e de f6 7f 00 00  ................
00007ff6`de1f2050  00 00 00 00 00 80 00 00-00 00 00 00 00 80 00 00  ................
00007ff6`de1f2060  d0 66 fc c2 f2 01 03 00-ab 90 ec 5e 22 c0 b2 44  .f.........^"..D
00007ff6`de1f2070  a5 dd fd 71 6a 22 2a 15-00 00 00 00 00 00 00 00  ...qj"*.........
0:000> ? rip
Evaluate expression: 140698265264128 = 00007ff6`de1f2000
0:000> ? dwo(rip)
Evaluate expression: 42 = 00000000`0000002a

这真的很烦人,但只要我意识到这一点,手动读取这样的数据就不是问题。但是,如果我想使用寄存器值,例如在编写调试器脚本时,那么没有简单的解决方法:

0:000> bu test!main ".if (dwo(rip) == 0n42) { .echo Whoops! I don't want to get here! }"
0:000> g
Whoops! I don't want to get here!
test!main:
00007ff6`de1868d0 4883ec28        sub     rsp,28h

这个问题,程序中的符号隐藏了寄存器名,这让我很困难

  1. 我想在CreateFileW()上设置一个断点,这是一个非常常见的Windows API函数
  2. 由于我只关心一个特定的文件,所以我想检查RCX寄存器中传递的文件名,并继续通过断点,除非文件名与我想要的文件匹配
  3. 但我无法编写这个条件,因为程序中的另一个模块定义了一个符号foobar!rcx,而我在断点上执行的命令中对rcx的任何引用都指向该全局变量

那么我该如何告诉WinDbg是的,我真的想读寄存器?如果我想写那个寄存器呢?我在这里一定错过了一件简单的事情。

正如另一个问题附带指出的那样,您可以在寄存器名称前面放一个at符号(@),以强制将其解释为寄存器或伪寄存器,从而绕过将其解析为十六进制数或符号的尝试。

MASM表达式中的寄存器和伪寄存器

可以在MASM表达式中使用寄存器和伪寄存器您可以在所有寄存器和伪寄存器之前添加一个at符号(@)at符号使调试器能够更快地访问该值。对于最常见的基于x86的寄存器来说,这个符号是不必要的。对于其他寄存器和伪寄存器,我们建议您添加at符号,但实际上并不需要。如果省略了不太常见的寄存器的at符号,调试器会尝试将文本解析为十六进制数,然后解析为符号,最后解析为寄存器。