【问题标题】:In WinDbg commands, how can I refer to a register with the same name as a global variable?在 WinDbg 命令中,如何引用与全局变量同名的寄存器?
【发布时间】:2016-06-09 05:17:12
【问题描述】:

假设我想使用 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 代码中声明的全局变量,并向我展示了 &amp;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 是的,我真的打算读取寄存器?如果我想写那个寄存器怎么办?我这里一定有一个简单的东西。

【问题讨论】:

  • if dwo(@rip) == xx 做其他事情 if qwo (@rcx) != foobar!rcx 做其他事情 .else gotosleep() @rcx = 注册 rcx 而 foobar!rcx或者只是 rcx 是一个全局的

标签: debugging windbg


【解决方案1】:

正如another question 所提到的,您可以在寄存器名称前放置一个 at 符号 (@) 以强制将其解释为寄存器或伪寄存器,绕过将其解析为的尝试十六进制数或符号。

Registers and Pseudo-Registers in MASM Expressions

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

【讨论】:

  • 我会更进一步说,因为它在 MASM 语法中更快(并且这可以明显更快,例如,如果您可以跳过在所有模块中搜索符号,其中之一是 Chrome 的 900MB PDB) 并且在 C++ 语法中是强制性的,因此总是在寄存器名称前加上“@”是一种很好的做法。我现在几乎是自动执行此操作,每次我忘记它并盯着*BUSY* 消息时(即使只是多出几秒钟),我都会为此而自责。
  • @conio 绝对。 @ 对我来说也是本能的。
猜你喜欢
  • 1970-01-01
  • 2014-04-24
  • 2011-08-15
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 2016-04-06
  • 2016-04-08
  • 2020-02-07
相关资源
最近更新 更多