<2017年12月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

文章分类

导航

订阅

在VC中编译WinDBG的扩展模块

WinDBG的命令有多少?答案是很多。到底有多少,不确定,因为扩展命令的个数是没有限制的,谁都可以写出一批来!

话虽然这么说,实际要写一个WinDBG扩展命令也不是那么的容易。

完整安装WinDBG后,安装好的目录下便有一个名为sdk的子目录,里面放的就都是用来写扩展命令的东东。以前曾经介绍过扩展命令的工作原理,没有必要再重复,模仿新书《格蠹汇编》的做法,今天给大家举个例子,越发觉得举例子是学习的一种好方法。

sdk子目录下有四个子目录,简介如下:

  • help - 介绍调试辅助库dbghelp.dll的帮助文件,与WinDBG扩展命令的关系不是非常直接,直接的帮助就是在WinDBG的帮助文件中
  • inc - 编写扩展命令所需的头文件,很重要,注意一定要用这个目录下的头文件,VC也附带同名的头文件,但是可能太老了
  • lib - 库文件
  • sampls - 示例程序,很有价值

samples目录下的示例程序很值得仔细学习,但有个小问题是,这些例子需要使用make方式来编译,官方的说法是使用DDK。这对于只做应用程序开发的朋友来说不太方便,如果能使用VC来编译就好了。本文就教你使用VC来构建的方法。

使用哪个版本的VC呢?就用我喜欢的VC6吧,十几年的老朋友,用起来真顺手。当然更高版本的VC也可以的,就本文讨论的问题应该没什么差异。

第1步:在VC中创建一个类型为Win32 Dynamic-Link Library新的项目,随便选什么目录和项目名(比如WdbgExt),跟随向导向前,在自动生成的文件选项中,选择A Dll that exports some symbols,完成向导后,VC便帮我们生成了几个文件,这些文件可以编译出一个DLL。

第2步:打开自动生成的WdbgExt.cpp,在末尾增加如下代码:

#include <ntverp.h>

//
// globals
//
EXT_API_VERSION         ApiVersion = { (VER_PRODUCTVERSION_W >> 8), (VER_PRODUCTVERSION_W & 0xff), EXT_API_VERSION_NUMBER64, 0 };
WINDBG_EXTENSION_APIS   ExtensionApis;
ULONG SavedMajorVersion;

VOID
WinDbgExtensionDllInit(
    PWINDBG_EXTENSION_APIS lpExtensionApis,
    USHORT MajorVersion,
    USHORT MinorVersion
    )
{
    ExtensionApis = *lpExtensionApis;

    SavedMajorVersion = MajorVersion;
    SavedMinorVersion = MinorVersion;

    return;
}

LPEXT_API_VERSION
ExtensionApiVersion(
    VOID
    )
{
    //
    // ExtensionApiVersion should return EXT_API_VERSION_NUMBER64 in order for APIs
    // to recognize 64 bit addresses.  KDEXT_64BIT also has to be defined before including
    // wdbgexts.h to get 64 bit headers for WINDBG_EXTENSION_APIS
    //
    return &ApiVersion;
}

//
// Routine called by debugger after load
//
VOID
CheckVersion(
    VOID
    )
{
    return;
}
WinDBG支持多种类型的扩展接口,我们现在使用的是最“古老的”的简单接口,上面几个函数是需要导出的函数,第一个函数特别重要,是用来接收“接口函数表”,当WinDBG加载这个扩展DLL时,WinDBG的引擎会调用这个函数,把一组接口函数的地址传给我们,我们将其保存在一个全局变量中,即:

    ExtensionApis = *lpExtensionApis;   

上面的代码来自sdk\samples\simplext\simple.c中,大家可以从中copy-paste

第3步:编译,会得到错误,因为还没有包含重要的wdbgexts.h头文件,在stdafx.h中加入如下代码:

//
// Define KDEXT_64BIT to make all wdbgexts APIs recognize 64 bit addresses
// It is recommended for extensions to use 64 bit headers from wdbgexts so
// the extensions could support 64 bit targets.
//
#define KDEXT_64BIT
#include <wdbgexts.h>

再次编译,可能还有错误,因为VC使用的还不是WinDBG下的头文件,如何设置呢?

在项目设置(Settings)中的C/C++ > Preprocessors页的Additional include directories中加入:

c:\windbg\sdk\inc

确定后再次编译,顺利的应该可以编译通过了。

第4步,加入如下代码实现两个命令:

//
// Extension to say hello
//
DECLARE_API ( hello )
{
     dprintf("Hi %s, WinDBG is great! %s\n", args[0], args[1]);
}

/*
  A built-in help for the extension dll
*/

DECLARE_API ( help )
{
    dprintf("Simple Wdbg Extension by Raymond\n" 

            "   hello              - say hello\n"
            "   help               - Shows this help\n"
            );

}

编译,应该没什么问题。

第5步,在WinDBG中加载测试。启动WinDBG,随便开始一个调试会话,然后执行如下命令:

.load c:\dbglabs\wdbgext\debug\wdbgext.dll

其中的路径需要更换成具体的路径,如果没有错误提示,就是加载成功了 :-)

执行.chain命令可以观看:

    c:\dbglabs\wdbgext\debug\wdbgext.dll: built Mon Mar 04 20:53:00 2013
        [path: c:\dbglabs\wdbgext\debug\wdbgext.dll]  

接下来尝试一下执行其中的命令,执行

!wdbgext.help

得到一个错误:

c:\dbglabs\wdbgext\debug\wdbgext.dll has no help export

哦,原来函数导出有问题,执行如下命令卸载:

 .unload wdbgext.dll

第6步:在项目中新增一个文本文件,加入如下内容:

;--------------------------------------------------------------------
; These are the extensions exported by dll
;--------------------------------------------------------------------
    help
    hello

;--------------------------------------------------------------------
;
; these are the extension service functions provided for the debugger
;
;--------------------------------------------------------------------


    CheckVersion
    WinDbgExtensionDllInit
    ExtensionApiVersion

保存为WdbgExt.def,然后再编译和链接,重复第5步:

0:000> !help
Help for WdbgExt.dll
   hello              - say hello
   help               - Shows this help

0:000> !wdbgext.hello 1
Hi, WinDBG is great, no 1!

这些成功了。但实际上虽然表面看似没问题了,其实还隐藏着一个很大的问题,可能把WinDBG崩溃掉的问题,这个问题是关于函数调用协议的。也就是以下三个函数的调用协议:

CheckVersion
WinDbgExtensionDllInit
ExtensionApiVersion

在以上代码中,我们没有声明它们的调用协议名。其实WinDBG是要求这几个函数使用标准调用协议的,也就是WINAPI(即__stdcall), 如果使用DDK编译,默认的调用协议就是这个没有问题。但是我们现在是使用VC编译,默认的调用协议是C协议。

两个协议的最大差别是前者是被调用者(子函数)清理栈上的参数,后者是调用者(父函数)清理参数。这个差异会导致栈不平衡而让WinDBG出现非法访问而崩溃。事实上,笔者之所以写这个博客,也是因为我在工作中遇到了这样的问题。

如何解决呢?在函数声明中加入WINAPI就可以了,即下面这样:

VOID WINAPI
CheckVersion(
    VOID
    )

这下应该彻底没有问题了,想亲自试一下么?下载源代码和项目文件的压缩包就可以操练一把了。

 

 

posted on 2013年3月4日 20:34 由 Raymond

Powered by Community Server Powered by CnForums.Net