我博客的左侧专栏曾经转过windows下编写调试器的一系列文章,这类文章是从零打造调试器,而这篇文章是介绍如何为windbg编写调试器扩展命令。

0.前言


    windbg的命令有很多,其中以"!"开始的命令就是所谓的扩展命令。这些扩展命令由Dll以导出函数的形式提供给windbg加载。一般跟在"!"后面的字符串就是dll中导出的函数。以!htrace命令为例,查看windbg帮助文档,它提到这个命令依赖于kdexts.dll和ntsdexts.dll:


DLL
Windows 2000 Unavailable
Windows XP and later Kdexts.dll
Ntsdexts.dll


    Kdexts.dll和Ntsdexts.dll分别为内核调试和应用层调试提供扩展功能。我们可以用dependence工具查看这些dll导出的函数是否如我所述:



编写windbg调试器扩展 入门篇1_调试器



    如图,Ntsdexts.dll中确实导出了htrace函数。看来,只要我们按部就班的提供类似的dll,就能实现自己的windbg扩展命令,完成一些组合功能。


1.准备工作   


    windbg为调试人员提供了2套用于编写调试器扩展命令的C++ SDK:一套是基于WindbgExts的API接口,一套是基于DbgEng的API接口。前者好像功能有限,所以不在我的选择范围;后者虽然看着像COM接口,其实并没有这么复杂:只不过接口以多态的形式导出,所以每次使用一组接口时需要查询接口类型。但是,便捷点是:可以在DbgEng接口中获得WindbgExts接口,增强扩展命令的灵活性(我就没用到~)。


编译用到的头文件和链接用到的库文件:如果我们安装完整的windbg(百度下到的一般是6系列的阉割版)或者安装DDK时选择安装windbg,就会得到对应的头文件和库文件。以我的机器为例:我在C盘下安装了7600的DDK,则windbg目录为于路径:C:\WinDDK\7600.16385.1\Debuggers下,其中SDK目录包含了inc/,lib/个samples/ 3个目录(其实还有help/目录,这帮助文档实在没用)。



编写windbg调试器扩展 入门篇1_扩展_02



    由于网上关于编写windbg扩展命令的文章相对较少,因此,samples目录成了及其重要的参考资料,我主要参考了samples/exts做为demo。该demo程序基于DbgEng接口,功能虽少,但是框架完整,基于框架略做修改就能实现相应功能。另外,这个demo提供了makefile/sources文件,参考这两个文件,很方便的可以将工作移植到VS上(高版本的windbg直接提供了samples目录下各个工程的.sln文件,用vs2013或更高vs打开既是)。


2.编译链接exts工程


    7600自带的exts扩展只能通过build -cgZ命令来编译,这种编译方式需要用到sources文件。我贴出修改后的sources文件:


TARGETNAME=dbgexts ;生成目标的名字 dbgexts
TARGETTYPE=DYNLINK ;生成目标的类型 DYNLINK是DLL的意思

_NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WINXP)

DLLENTRY=_DllMainCRTStartup ;指定Dll入口函数

DBGSDK_INC_PATH=C:\WinDDK\7600.16385.1\Debuggers\sdk\inc ;这部分是我修改的 设置Dll的头文件路径
DBGSDK_LIB_PATH=C:\WinDDK\7600.16385.1\Debuggers\sdk\lib ;设置Dll的依赖库路径

!if "$(DBGSDK_INC_PATH)" != ""
INCLUDES = $(DBGSDK_INC_PATH);$(INCLUDES)
!endif
!if "$(DBGSDK_LIB_PATH)" == ""
DBGSDK_LIB_PATH = $(SDK_LIB_PATH)
!else
DBGSDK_LIB_PATH = $(DBGSDK_LIB_PATH)\$(TARGET_DIRECTORY)
!endif

TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib \
$(DBGSDK_LIB_PATH)\dbgeng.lib ;<------使用DbgExts接口时依赖的lib文件,后面用VS编译时需要根据这条信息设置链接参数

USE_MSVCRT=1 ;使用MSVCRT运行库

UMTYPE=windows

SOURCES= dbgexts.cpp \ ;编译用到的源文件 资源文件
exts.cpp \
dbgexts.rc

需要注意的,除了要修改sources文件,还要修改.def文件。该文件用于在链接过程中指明导出函数名,示例代码exts中.def文件内容如下:

EXPORTS

cmdsample
help
structsample
symgrptest

DebugExtensionNotify
DebugExtensionInitialize
DebugExtensionUninitialize
KnownStructOutput
_EFN_Analyze

经过编译链接后生成dbgexts.dll,查看其中的导出函数----这些函数就是将来的扩展命令:



编写windbg调试器扩展 入门篇1_调试器_03



3.测试自定义的扩展命令


.load 扩展Dll全路径 的形式加载调试扩展DLL;以 .unload 扩展Dll全路径的形式卸载调试扩展。我们试试刚才生成的调试扩展:


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 01c8f814 7727ec87 ntdll!DbgBreakPoint
Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type ".hh dbgerr005" for details.

0:004> !cmdsample ;运行dbgexts.dll中导出的扩展命令 cmdsample命令
ChildEBP RetAddr Args to Child
01c8f814 7727ec87 761e583f 00000000 00000000 ntdll!DbgBreakPoint (FPO: [0,0,0])
01c8f844 75acef8c 00000000 01c8f890 7724367a ntdll!DbgUiRemoteBreakin+0x3c (FPO: [Non-Fpo])
01c8f850 7724367a 00000000 761e58eb 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
01c8f890 7724364d 7727ec4b 00000000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
01c8f8a8 00000000 7727ec4b 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])


Debugger module list
start end module name
00d60000 00e20000 calc (pdb symbols) C:\WinDDK\7600.16385.1\Debuggers\sym\calc.pdb\971D2945E998438C847643A9DB39C88E2\calc.pdb
10000000 1008a000 kwsui (deferred)
607e0000 60873000 TaobaoProtectSE (deferred)
66690000 6669d000 sfc_os (deferred)
67000000 67003000 sfc (deferred)
6b4b0000 6b4ec000 oleacc (deferred)
... ;此处略去若干调试输出
0:004> !help ;运行dbgexts.dll中导出的扩展命令 help命令
Help for dbgexts.dll
cmdsample - This does stacktrace and lists
help = Shows this help
structsample <addr> - This dumps a struct at given address


0:004> .chain ;.chain命令用于显示所有已加载的调试器扩展dll
...
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] ;<--------------这是刚才用exts例子编译生成的调试器扩展
;这个扩展通过前面的.load命令已经加载到windbg中
C:\Users\Administrator\Desktop\studio\windbgExt\Debug\windbgExt.dll: API 1.0.0, built Tue Aug 08 22:12:15 2017
[path: C:\Users\Administrator\Desktop\studio\windbgExt\Debug\windbgExt.dll]
dbghelp: image 6.12.0002.633, API 6.1.6, built Tue Feb 02 04:08:26 2010
[path: C:\WinDDK\7600.16385.1\Debuggers\dbghelp.dll]
ext: image 6.12.0002.633, API 1.0.0, built Tue Feb 02 04:08:31 2010
[path: C:\WinDDK\7600.16385.1\Debuggers\winext\ext.dll]
exts: image 6.12.0002.633, API 1.0.0, built Tue Feb 02 04:08:24 2010
[path: C:\WinDDK\7600.16385.1\Debuggers\WINXP\exts.dll]
uext: image 6.12.0002.633, API 1.0.0, built Tue Feb 02 04:08:23 2010
[path: C:\WinDDK\7600.16385.1\Debuggers\winext\uext.dll]
ntsdexts: image 6.1.7650.0, API 1.0.0, built Tue Feb 02 04:08:08 2010
[path: C:\WinDDK\7600.16385.1\Debuggers\WINXP\ntsdexts.dll]




0:004> .unload C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll ;卸载调试扩展
Unloading C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll extension DLL


    入门篇到此结束。下一篇进阶篇会介绍DbgExts调试器扩展的框架,以及如何调试扩展。高级篇会介绍如何控制调试输出和调用windbg现有命令。最后会给出我写的一个扩展命令示例