【逆向知识】开发WinDBG扩展DLL
如何开发WinDbg扩展DLL
WinDbg扩展DLL是一组导出的回调函数,用于实现用户定义的命令。以便从内存转储中提取特定的信息。扩展dll由调试器引擎加载,可以在执行用户模式或内核模式调试时提供自动化任务的扩展功能。扩展DLL可以导出任意数量用于执行扩展命令的函数。每个函数都被显式地声明为DLL定义文件中的导出函数,.def文件和函数名必须是小写字母。
WinDbg(DbgEng)扩展DLL必须导出DebugExtensionInitialize。 这将在DLL加载时调用,以便于初始化DLL。 它可能被DLL用来初始化全局变量。
扩展DLL导出的可选函数DebugExtensionUninitialize。如果导出这个函数,那么卸载扩展时将调用这个DebugExtensionUninitialize 函数。
扩展DLL导出的DebugExtensionNotify函数。如果导出这个函数将会在会话开始或结束时调用,当目标开始或停止执行时。这些通知还提供给在客户端注册的IDebugEventCallbacks对象。
扩展DLL可以导出KnownStructOutput。如果导出这个函数将会在加载DLL时调用它。该函数返回在一行上打印的结构列表。稍后可以调用它来格式化这些结构的实例以便打印。
开发自己的Windbg扩展DLL,可以按照以下步骤:
- 1.下载并安装Windows的调试工具
http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx
- 2.使用VS 2008创建“Win32 Console Application”
- 3.选择应用程序类型为“DLL”,然后单击“完成”
图1
- 4.在项目中添加一个名为“wdbrowser”的“Module-Definition File (.def) ”。 导出扩展功能的一种方法是在.def文件的EXPORTS部分中指定函数名称。 也可以使用其他方式导出函数,如__dllexport
图2
-
5.将“Additional include Directories”配置为指向Windbg附带的头文件的目录。 x86的默认文件夹路径是“C:\Program Files\Debugging Tools for Windows (x86)\sdk\inc”
注:include Directories指include 目录
-
6.将“Additional Library Directories” 配置为指向Windbg附带库文件的目录。x86库文件的默认文件夹路径是:"C:\Program Files\Debugging Tools for Windows (x86)\sdk\lib\i386″
注:Library Directories指Library 目录
-
7.“dbgeng.lib ” & “dbgeng.dll” 具有实现调试引擎输出的功能。所以需要添加“dbgeng.lib ”到附加依赖项里
-
8.添加在步骤#3中创建的模块定义文件名称“wdbrowser.def”
-
9.在“stdafx.h”中包含以下所需的头文件
#include <windows.h> #include <imagehlp.h> #include <wdbgexts.h> #include <dbgeng.h> #include <extsfns.h>
-
10.声明以下两个全局变量
//Version.
EXT_API_VERSION g_ExtApiVersion = {1,1,EXT_API_VERSION_NUMBER,0} ;
WINDBG_EXTENSION_APIS ExtensionApis = {0};
- 11.声明以下调试引擎COM接口指针。
IDebugAdvanced2* gAdvancedDebug2=NULL;
IDebugControl4* gDebugControl4=NULL;
IDebugControl* gExecuteCmd=NULL;
IDebugClient* gDebugClient=NULL;
-
12.下一步是在DLL Main实现源文件中声明和实现WinDbgExtensionDllInit函数。 在这个例子中是“wdbrowser.cpp”。 WinDbgExntensionDllInit是第一个将被windbg调用的函数。 所以这个函数是空闲的,用于实现任何扩展特定的初始化或相关功能。
有关此功能的更多详细信息,请参阅http://msdn.microsoft.com/en-us/library/cc267872.aspx。
VOID WDBGAPI WinDbgExtensionDllInit (PWINDBG_EXTENSION_APIS lpExtensionApis, USHORT usMajorVersion, USHORT usMinorVersion)
{
ExtensionApis = *lpExtensionApis;
HRESULT hResult = S_FALSE;
if (hResult = DebugCreate(__uuidof(IDebugClient), (void**) &gDebugClient) != S_OK)
{
dprintf(“Acuqiring IDebugClient* Failled\n\n”);
return;
}
if (hResult = gDebugClient->QueryInterface(__uuidof(IDebugControl), (void**) &gExecuteCmd) != S_OK)
{
dprintf(“Acuqiring IDebugControl* Failled\n\n”);
return;
}
if (hResult = gDebugClient->QueryInterface(__uuidof(IDebugAdvanced2), (void**) &gAdvancedDebug2) != S_OK)
{
dprintf(“Acuqiring IDebugAdvanced2* Failled\n\n”);
return;
}
if (hResult = gDebugClient->QueryInterface(__uuidof(IDebugControl4), (void**) &gDebugControl4) != S_OK)
{
dprintf(“Acuqiring IDebugControl4* Failled\n\n”);
return;
}
}
-
13.声明另一个导出的函数ExtensionApiVersion,向windbg报告扩展版本。
有关此函数的详细信息,请参见http://msdn.microsoft.com/en-us/library/cc267873.aspx。
LPEXT_API_VERSION WDBGAPI ExtensionApiVersion (void)
{
return &g_ExtApiVersion;
}
- 14.定义调试引擎的接口指针,以便扩展模块可以与调试引擎交互。更多信息请参考
http://msdn.microsoft.com/en-us/library/cc265976.aspx – IDebugClient, http://msdn.microsoft.com/en-us/library/cc266102.aspx – IDebugControl
http://msdn.microsoft.com/en-us/library/cc265957.aspx – IDebugAdvanced
IDebugAdvanced2* gAdvancedDebug2=NULL;
IDebugControl4* gDebugControl4=NULL;
IDebugControl* gExecuteCmd=NULL;
IDebugClient* gDebugClient=NULL;
-
- 下一步是实现调试引擎的回调接口IDebugOutputCallbacks。调试引擎回调IDebugOutCallbacks::Output()的实现,输出扩展函数执行命令后的结果。
有关idebugoutput回调的详细信息,请参见http://msdn.microsoft.com/en-us/library/cc265716.aspx::Output()
- 16.在继承idebugoutputcallback接口的头文件中添加以下新类。
#ifndef __OUT_HPP__
#define __OUT_HPP__
#include <string>
#include <sstream>
class StdioOutputCallbacks : public IDebugOutputCallbacks
{
private:
std::string m_OutputBuffer;
//
//This buffer holds the output from the command execution.
//
CHAR m_OutPutBuffer[4096];
public:
void InitOutPutBuffer();
std::string GetOutputBuffer()
{
return m_OutputBuffer;
};
void ClearOutPutBuffer()
{
m_OutputBuffer = “”;
};
STDMETHOD(QueryInterface)(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
);
STDMETHOD_(ULONG, AddRef)(
THIS
);
STDMETHOD_(ULONG, Release)(
THIS
);
// IDebugOutputCallbacks.
STDMETHOD(Output)(
THIS_
IN ULONG Mask,
IN PCSTR Text
);
};
extern StdioOutputCallbacks g_OutputCb;
#endif // #ifndef __OUT_HPP__
- 17.添加实现IDebugOutputCallbacks接口方法的代码,特别是Output()
#include “stdafx.h”
#include <stdio.h>
#include <windows.h>
#include <dbgeng.h>
#include “OutputCallBack.h”
StdioOutputCallbacks g_OutputCb;
STDMETHODIMP
StdioOutputCallbacks::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
*Interface = NULL;
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
{
*Interface = (IDebugOutputCallbacks *)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG)
StdioOutputCallbacks::AddRef(
THIS
)
{
// This class is designed to be static so
// there’s no true refcount.
return 1;
}
STDMETHODIMP_(ULONG)
StdioOutputCallbacks::Release(
THIS
)
{
// This class is designed to be static so
// there’s no true refcount.
return 0;
}
STDMETHODIMP
StdioOutputCallbacks::Output(
THIS_
IN ULONG Mask,
IN PCSTR Text
)
{
UNREFERENCED_PARAMETER(Mask);
m_OutputBuffer += Text;
return S_OK;
}
void StdioOutputCallbacks::InitOutPutBuffer()
{
m_OutputBuffer.erase();
}
- 18.添加扩展函数的实现。在本例我们实现了一个扩展,该扩展显示当前线程第2帧中的变量名和类型。实现代码:
DECLARE_API (dvf3)
{
//
// Install output callbacks.
//
if ((gDebugClient->SetOutputCallbacks((PDEBUG_OUTPUT_CALLBACKS) &g_OutputCb))!= S_OK)
{
dprintf(“*****Error while installing Outputcallback.*****\n\n”);
return;
}
//
// Since frame count starts from 0 index, we have to pass 2 as parameter for .frame command for the frame# 2
//
//Execute command to extrac 2nd frame.
if (gExecuteCmd->Execute(DEBUG_OUTCTL_THIS_CLIENT | //Send output to only outputcallbacks
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
“.frame 2”,
DEBUG_EXECUTE_DEFAULT ) != S_OK)
{
dprintf(“Executing .frame 2 failled\n”);
return;
}
//Execute command to extrac 2nd frame.
if (gExecuteCmd->Execute(DEBUG_OUTCTL_THIS_CLIENT | //Send output to only outputcallbacks
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
“dv /i /t /v”,
DEBUG_EXECUTE_DEFAULT ) != S_OK)
{
dprintf(“Executing dv /i /t /v failled\n”);
return;
}
dprintf(“***** Extracting locals & formal params from frame 2 *****”);
dprintf(“\n%s\n”, g_OutputCb.GetOutputBuffer().c_str());
}
- 19.重建项目。将. dll从发布文件夹中复制到Windbg查找扩展dll的文件夹中。
在x86机器上,默认目录为“<Drive letter>\Program Files\ debug Tools for Windows (x86)\winext”。
- 20.扩展已经准备好用于使用或测试。
- 21.启动windbg并打开一个完整的用户模式转储。输入.load myextension和hit enter,将扩展DLL加载到Windbg进程空间中。
-
22.运行.chain命令来验证是否由WinDbg加载扩展。如果扩展被加载,将看到类似于下面输出的内容。
-
23.输入!dvf3以运行扩展函数,用于从框架2中提取和显示变量名。