WinDbg没有显示寄存器值

Mic*_*sky 8 debugging windbg

基本上,这是在这里提出的问题.

当使用WinDbg 6.2及更高版本对运行Windows 7或更早版本的计算机执行内核调试时,调试器不会在寄存器窗口中显示任何内容.按下Customize...按钮会显示一个读取的消息框Registers are not yet known.

同时,发出r命令会导致打印出完全有效的寄存器值.

这种行为的原因是什么,可以修复吗?

Mic*_*sky 15

TL; DR:我写了一个修复bug的扩展DLL.可在这里.

问题

要理解这个问题,我们首先需要了解WinDbg基本上只是微软Windows符号调试器引擎的一个前端,在内部实现dbgeng.dll.其他前端包括命令行kd.exe(内核调试器)和cdb.exe(用户模式调试器).

引擎实现了我们对调试器的所有期望:使用符号文件,读写内存和寄存器,设置breakpoitns等.引擎然后通过类似COM的接口公开所有这些功能(它们实现IUnknown但不是注册组件) .例如,这允许我们编写自己的调试器(就像这个人一样).

有了这些知识,我们现在可以对WinDbg如何获取目标机器上的寄存器值做出有根据的猜测.

引擎公开了IDebugRegisters用于操作寄存器的接口.该接口声明了GetValues一次性检索多个寄存器的值的方法.但WinDbg如何知道有多少寄存器?这就是为什么我们有这个GetNumberRegisters方法.

因此,要检索目标上所有寄存器的值,我们必须执行以下操作:

  1. 调用IDebugRegisters::GetNumberRegisters以获取寄存器总数.
  2. 调用IDebugRegisters::GetValuesCount设定为寄存器的总数,则参数Indices参数设置为NULL,将Start设置为参数0.

但是,一个小问题是:第二次调用失败了E_INVALIDARG.

嗯,对不起?怎么会失败?特别令人费解的是这个返回值的文档:

其中一个寄存器的索引值大于目标机器上的寄存器数.

但我刚刚问你有多少个寄存器,那么该值怎么会超出范围呢?好吧,不管怎么说,让我们继续阅读文档,也许有些事情会变得清晰:

如果返回值不是S_OK,则仍可能已读取某些寄存器.如果目标不可访问,则返回类型为E_UNEXPECTED且值不变; 否则,值将包含部分结果,无法读取的寄存器将具有DEBUG_VALUE_INVALID类型.

(强调我的.)

啊哈!所以也许引擎无法读取其中一个寄存器!但是哪一个?结果发动机在xcr0寄存器上扼流圈.来自Intel 64和IA-32架构软件开发人员手册:

扩展控制寄存器XCR0包含一个状态组件位图,用于指定软件已启用XSAVE要管理的功能集的用户状态组件.如果清除了对应于状态组件的位,则无论指令掩码的值如何,特征集中的XCR0指令XSAVE都不会对该状态组件进行操作.

好的,寄存器控制XSAVE指令的操作,这样可以保存CPU扩展功能的状态(如XMM和AVX).据对最后评论页面,这个指令需要从操作系统的一些支持.虽然评论声明Windows 7(我正在测试的VM正在运行)确实支持此指令,但似乎手头的问题无论如何都与操作系统有关,因为当目标是Windows 8时,一切正常.

实际上,不清楚这个错误是否在调试器引擎内,它报告的寄存器数量多于它可以检索的值,或者在WinDbg中,如果引擎无法生成所有这些错误,它将拒绝显示任何值.

解决方案

当然,我们可以咬紧牙关,只需使用旧版本的WinDbg来调试旧的Windows版本.但那里的挑战在哪里?

相反,我向您展示了一个解决此问题的调试器扩展.它通过挂钩(在此库的帮助下)相关的调试器引擎方法并返回S_OK失败的唯一寄存器来实现xcr0.否则,它传播失败.该扩展支持运行时卸载,因此如果遇到问题,您可以随时禁用挂钩.

就是这样,玩得开心!