前面一篇是用sources和nmake组合编译生成调试器扩展,步骤比较繁琐,我相信读者应该更倾向于用IDE来编写代码。本篇有2个主题:1.将前面的代码移植到VS上,用IDE编译;2.调试生成的扩展命令。
1.移植代码到IDE
这部分相对简单,所以放在开头。要用IDE代替nmake编译调试器扩展,只要把sources文件中的编译/链接选项复制到VS对应的选项中就可以了。
1.1.设置编译项:
工程属性-C/C++-常规-附加包含目录中添加头文件的路径:C:\WinDDK\7600.16385.1\Debuggers\sdk\inc
1.2.设置链接项:
工程属性-链接器-输入-附加依赖项中添加库文件的路径:C:\WinDDK\7600.16385.1\Debuggers\sdk\lib\$(ARCH)\dbgeng.lib。对于运行在不同架构的调试器扩展要使用不同的ARCH,我的调试器扩展用于调试x86的程序,因此最终路径为:C:\WinDDK\7600.16385.1\Debuggers\sdk\lib\i386\dbgeng.lib.
上面的截图中"将模块添加到程序集",我在这里直接将exts/目录下的windbgExt.def文件添加进来。当然,你也可以不添加这项,直接在代码中声明导出函数即可。怎么选择取决于个人~
2.调试调试扩展
写程序难免会出bug,出了bug就得通过调试器定位/解决。平时,我们都是用调试器附加到有问题的程序中调试代码;现在情况变了:windbg在加载扩展命令后变得岌岌可危,如何应对新问题?其实很简单:另起炉灶,新开一个windbg实例,attach到加载了扩展命令的有问题的windbg实例上,就能把新问题变成与调试普通代码一样了。扩展命令本身是Dll程序,通过windbg调用LoadLibrary加载,因此,调试扩展命令可以再细分为两种类型:1.调试Dll入口函数;2.调试其他导出函数,下面仍以demo程序dbgexts.dll为例分类讨论。为了加以区分,我们先做如下约定:加载扩展命令的windbg叫做调试器A,调试有问题的windbg的windbg叫做调试器B。
2.1.调试扩展命令入口函数
在调试器A加载调试器扩展前,先运行调试器B,并使调试器B attach调试器A上。然后在调试器B中启用加载Dll事件,这样一旦调试器A加载了扩展命令dbgexts,调试器B就会捕捉到调试事件并中断。
以下输出来源于调试器B:
0:002> |. ;查看当前进程
. 0 id: 2324 attach name: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\windbg.exe
0:002> sxe ld dbgexts ;启用Dll加载事件
0:007> lm ;查看调试器A中已加载的模块(至少没加载dbgexts.dll)
start end module name
012a0000 01331000 windbg (pdb symbols) C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\sym\windbg.pdb\CC6E30603ED243FBB418BC2ED0F2FDC71\windbg.pdb
60d60000 6113b000 ext (deferred)
61d40000 621bc000 dbgeng (deferred)
64f60000 6509e000 dbghelp (deferred)
67050000 67109000 exts (deferred)
...
以下输出来源于调试器A:
0:004> .chain
Extension DLL chain:
dbghelp: image 10.0.10586.15, API 10.0.6, built Fri Nov 20 12:39:22 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\dbghelp.dll]
ext: image 10.0.10586.15, API 1.0.0, built Fri Nov 20 12:39:52 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\winext\ext.dll]
exts: image 10.0.10586.15, API 1.0.0, built Fri Nov 20 12:39:08 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\WINXP\exts.dll]
uext: image 10.0.10586.15, API 1.0.0, built Fri Nov 20 12:38:53 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\winext\uext.dll]
ntsdexts: image 10.0.10586.15, API 1.0.0, built Fri Nov 20 13:08:00 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\WINXP\ntsdexts.dll]
从上面的输出可以看到目前调试器A尚未加载调试器扩展dbgExts.dll。
调试器A一旦执行了.load命令,调试器B就会中断。这是加载符号,设置断点的好时机:
以下输出来源于调试器B:
0:001> .sympath+ C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386
Symbol search path is: SRV*;C:\Users\Administrator\Desktop\studio\windbgExt\Debug;
0:001> .reload
Reloading current modules
.....................................................
0:001> bl
0 e 71981898 0001 (0001) 0:**** dbgexts!DebugExtensionInitialize+0x8
根据网上的资料介绍,调试器扩展的初始化函数原型为:
HRESULT
CALLBACK
DebugExtensionInitialize(PULONG Version, PULONG Flags)
于是我在这下了断点,并等待中断,按下F5以后很快就会中断到这个函数。
再次F5,调试器A才会从.load命令中返回。
以下输出来源于调试器A:
0:004> .load C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll
Extension dll detected a break connected to X86
00 01c9fb64 7763ec87 ntdll!DbgBreakPoint
0:004> .chain
Extension DLL chain:
C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll: image 6.1.7600.16385, API 1.0.0, built Wed Aug 09 23:24:43 2017
[path: C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll]
dbghelp: image 10.0.10586.15, API 10.0.6, built Fri Nov 20 12:39:22 2015
[path: C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\dbghelp.dll]
ext: image 10.0.10586.15, API 1.0.0, built Fri Nov 20 12:39:52 2015
对比前面调试器A运行.chain的输出,可以看到现在调试器A已经加载了dbgexts扩展。当然,这也可以从调试器B中获得信息:
以下输出来源于调试器B:
0:003> lml
start end module name
012a0000 01331000 windbg (pdb symbols) C:\Users\Administrator\Desktop
...
71980000 71989000 dbgexts (private pdb symbols) C:\Users\Administrator\Desktop\DevTools\windbg v10\x86\sym\dbgexts.pdb\C92C6D4B2699410CB2A62310F29351D41\dbgexts.pdb
2.2.调试其他导出函数
调试自定义导出函数相对简单,前面已经加载了调试符号,只要在相应函数入口加断点即可,这里不再赘述。