编写windbg调试器扩展 进阶篇1

本文介绍了Windbg调试器扩展的基础知识,包括调试器扩展的初始化与卸载过程、调试器通知事件及其处理函数,以及如何利用Windbg提供的API进行调试操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    作为windbg调试器扩展的第三篇,这篇文章将结合代码,梳理调试器扩展的骨架脉络。

    先附上代码片段:

#include "dbgexts.h"
extern "C" HRESULT CALLBACK
DebugExtensionInitialize(PULONG Version, PULONG Flags) {
  *Version = DEBUG_EXTENSION_VERSION(2, 11);
  *Flags = 0;  // Reserved for future use.
  return S_OK;
}
extern "C" void CALLBACK
DebugExtensionNotify(ULONG Notify, ULONG64 Argument) {
  UNREFERENCED_PARAMETER(Argument);
  switch (Notify) {
    // A debugging session is active. The session may not necessarily be suspended.
    case DEBUG_NOTIFY_SESSION_ACTIVE:
      break;
    // No debugging session is active.
    case DEBUG_NOTIFY_SESSION_INACTIVE:
      break;
    // The debugging session has suspended and is now accessible.
    case DEBUG_NOTIFY_SESSION_ACCESSIBLE:
      break;
    // The debugging session has started running and is now inaccessible.
    case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
      break;
  }
  return;
}
extern "C" void CALLBACK
DebugExtensionUninitialize(void) {
  return;
}

1.入口和出口

    DebugExtensionInitialize和DebugExtensionUnitialize是每个调试器扩展必须导出给windbg的接口,二者缺一不可。DebugExtensionInitialize接口在调试器扩展首次被windbg加载时被调用,这个接口除了负责做初始化相关的工作,没什么特别的作用。唯一需要注意的点是:必须用DEBUG_EXTENSION_VERSION宏初始化该调试器扩展的版本号,当运行.chain命令时,windbg会列出该调试器扩展的版本号。前面示例代码段中设置版本号为2.11,让我们看下运行.chain命令后的输出:

0:004> .chain
Extension DLL search Path:
    C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll ..., API 2.11.0, ...
        [path: C:\WinDDK\7600.16385.1\Debuggers\sdk\samples\exts\objchk_win7_x86\i386\dbgexts.dll]
    

2.调试器通知事件

    众所周知,调试状态至少可以分为两种:a.调试器连续运行状态(如用户键入F5运行);b.调试器中断,接受用户输入状态(调试器遇到断点,中断到交互界面)。windbg为调试器扩展提供了4种状态以及通知扩展关于状态转换的接口,这个接口就是前面代码中的DebugExtensionNotify。状态码DEBUG_NOTIFY_SESSION_ACCESSIBLE代表windbg遇到断点或者被手动中断而进入到交互界面;DEBUG_NOTIFY_SESSION_INACCESSIBLE代表用户按下F5使windbg恢复运行。

3.windbg导出的调试接口

    以一个向windbg输出HelloWorld的调试接口为例,我们来一点点介绍windbg导出的接口:
HRESULT CALLBACK 
helloworld(PDEBUG_CLIENT pDebugClient, PCSTR args) {
  UNREFERENCED_PARAMETER(args);
  IDebugControl* pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl))) {
    pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "Hello World!\n");
    pDebugControl->Release();
  }
  return S_OK;
}
    需要强调的是,调试器扩展的每个命令----也就是Dll中每个导出函数----必须小写,否则这个扩展命令会有各种奇怪的问题(至少在编写内核调试扩展时是这样)。 每个导出函数的接口形式固定为:
HRESULT CALLBACK 
ExtensionCommandName(PDEBUG_CLIENT , PCSTR );
其中参数1是指当前调试终端,简单的说就是用户输入的来源和命令输出的目标,就像Linux下的Terminal终端工具,Linux把这种用来显示和接收用户输入的交互端口定义为终端。对应到windbg上的功能就是Command输入/输出窗口和,如下图红框所示:


参数2是指跟在扩展命令后的参数:以!process扩展命令为例,我们可能会输入!process 0 0来显示当前系统中所有的进程。键入该命令后会调用KdExt.dll中的Process函数,该函数的参数args接受到的字符串就是"0 0"。
    说完了参数,我们再来聊聊函数体内部。在导出函数内部会看到大量诸如以下COM形式的语句:
    IDebugClient *DebugClient;
    PDEBUG_CONTROL DebugControl;
...
    if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
                                  (void **)&DebugControl)) == S_OK)
    只能说,这只是类似COM接口,实际只是C++多态。以IDebugControl为例,查看MSDN可知,windbg导出的IDebugControl类已经超过5个版本(IDebugControl~IDebugControl5,IDebugControl是基类,IDebugControlN继承自这个类),每个版本的windbg用的IDebugControl类可能都不同。MS使用多态性,平滑的解决了代码在不同版本windbg之间的兼容性问题。代码首先定义了一个IDebugControl基类指针,并通过QueryInterface获得当前版本windbg使用的IDebugControl子类。最后通过基类IDebugControl中的虚函数指针,来调用IDebugControl子类实现的各个函数(我获得的IDebugControl对象,其实际类是IDebugControl5)。如使用函数IDebugControl->Execute("kb");来执行windbg的栈回溯功能。在调试器扩展中,我们可以通过不断的调用QueryInterface接口获得不同功能的对象,最终实现反汇编/解析符号/设置断点等基本的调试功能,当然这部分内容不是本文重点,需要读者自己查阅MSDN相关章节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值