WinDbg插件编写――基础篇
本文只是简略的描述了一下WinDbg扩展命令的编写步骤及方法,更详细的请看他的帮助文档,起到一个抛砖引玉的作用就行了。
WinDbg是老盖自己家出的调试器,功能很强大。即可调试用户态,也可以调试核心态程序。本文主要介绍他插件(准确讲应该是扩展命令)的编写方法。WinDbg的扩展命令和OD一样是通过DLL文件导出的。
一.插件的类型
他有两种不同类型的DLL文件:
1.DbgEng类型DLL。这种类型的DLL都已“dbgeng.h” 头文件为基础。扩展命令的功能比较强大,可以用Debugger Engine API(这种调试API不依赖WinDbg,可以用来编写自己的调试器),也可以用WdbgExts API。
2.WdbgExts类型DLL。这种类型的DLL以“Wdbgexts.h” 头文件为基础,它只能用WdbgExts API.
二.两种插件的编写方法。
一般来说编写一个扩展命令需要四个文件:.c/c++文件,.def文件,sources文件和makefile文件,和写一般DLL文件一样。下面主要讲c/c++文件和def文件,应为他们在两种类型的DLL中差异比较大。
1.DbgEng类型。
(1) c/c++文件,可以用dbgeng.h标准的C++代码调试接口也可以用wdbgexts.h中的c代码接口。如果在这个类型中用wdbgexts API,需要用到wdbgexts.h和KDEXT_64BIT宏,如:
#define KDEXT_64BIT
#include wdbgexts.h
#include dbgeng.h
这个文件主要依据dbgeng.h和wdbgexts.h中的调试接口函数实现特定功能的函数代码。第一行主要是编译64位的,两个头文件中的函数都有32和64位,如ReadIoSpace64和ReadIoSpace,你可以在这种类型的扩展中完全用64位的函数,既可以在64位cpu也可以在32为cpu 运行。
(2)def文件,定义导出函数和自己的扩展命令
DebugExtensionInitialize函数必须导出,当DLL被加载时用来初始化全局变量。其他的根据实际情况而定。
2.WdbgExts类型
这种类型扩展的功能比上一种来说差一些,但一般情况下也能完成很多任务。
(1)C文件。
I.只能用wdbgexts.h中的c接口,如果用64位,简单的包含
#define KDEXT_64BIT
#include wdbgexts.h
只是编译32位的话,就无需包含第一句。
II.必须用DECLARE_API宏
这个宏定义在wdbgexts.h中,
#define DECLARE_API(s) \
CPPMOD VOID \
s( \
HANDLE hCurrentProcess, \
HANDLE hCurrentThread, \
ULONG64 dwCurrentPc, \
ULONG dwProcessor, \
PCSTR args \
)
如果是32位dwCurrentPc的类型就要换成ULONG.
(2)def文件
必须输出WinDbgExtensionDllInit.和ExtensionApiVersion两个函数。
三.编译
1.需要Windows Driver Kit(WDK),必须在windows 2003 server build environment环境下。
2.设置DBGSDK_INC_PATH和DBGSDK_LIB_PATH环境变量。
3.切换到你的原文件目录下
4.执行build ?cZMg
四.加载
把编译出的含DLL的文件夹放到WinDbg安装目录下,运行WinDbg后用.load dllname 就可以了。然后就尽情的享受你自己编写的命令把。
*********************************************************************
实例:
下来用一个主要是以wdbgexts.h API调试接口的例子。这个dll文件主要是自己实现read(读指定地址值),
edit(编辑指定地址的值),stack(取得堆栈值),help(显示以上三个命令的帮助信息).
(1)编写simple.c文件
(2)编写simple.def文件
(3)编写sources文件
(4)编写makefile文件
(5)编写rc资源文件
(6)编译并产生simple.dll
(7)加载并执行自己的扩展命令
**********************************************************************
(1)c文件
//
//头文件和宏定义
#include <windows.h>
#include "simple.h"
#define KDEXT_64BIT
#include <wdbgexts.h>
#include <ntverp.h>
//
// 全局变量
//
EXT_API_VERSION ApiVersion = { (VER_PRODUCTVERSION_W >> 8), (VER_PRODUCTVERSION_W & 0xff), EXT_API_VERSION_NUMBER64, 0 };
WINDBG_EXTENSION_APIS ExtensionApis;
ULONG SavedMajorVersion;
ULONG SavedMinorVersion;
//
//主程序
//
DllInit(
HANDLE hModule,
DWORD dwReason,
DWORD dwReserved
)
{
switch (dwReason) {
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_PROCESS_ATTACH:
break;
}
return TRUE;
} VOID
WinDbgExtensionDllInit(
PWINDBG_EXTENSION_APIS lpExtensionApis,
USHORT MajorVersion,
USHORT MinorVersion
)
{
ExtensionApis = *lpExtensionApis;
SavedMajorVersion = MajorVersion;
SavedMinorVersion = MinorVersion;
return;
}
LPEXT_API_VERSION
ExtensionApiVersion(
VOID
)
{
return &ApiVersion;
} VOID
CheckVersion(
VOID
)
{
return;
}
//
// 读目标双字值
//
DECLARE_API( read )
{
ULONG cb;
ULONG64 Address;
ULONG Buffer[4];
Address = GetExpression(args);
if (ReadMemory(Address, &Buffer, sizeof(Buffer), &cb) && cb == sizeof(Buffer)) {
dprintf("%I64lx: %08lx %08lx %08lx %08lx\n\n", Address,
Buffer[0], Buffer[1], Buffer[2], Buffer[3]);
}
}
//
// 编辑目标的双字值
//
// !edit <address> <value>
//
DECLARE_API( edit )
{
ULONG cb;
ULONG64 Address;
ULONG Value;
if (GetExpressionEx(args, &Address, &args)) {
Value = (ULONG) GetExpression( args);
} else {
dprintf("Usage: !edit <address> <value>\n");
return;
}
if (WriteMemory(Address, &Value, sizeof(Value), &cb) && cb == sizeof(Value)) {
dprintf("%I64lx: %08lx\n", Address, Value);
}
} //
// 提取堆栈值
//
DECLARE_API ( stack )
{
EXTSTACKTRACE64 stk[20];
ULONG frames, i;
CHAR Buffer[256];
ULONG64 displacement; // 在当前线程中获得堆栈祯值
frames = StackTrace( 0, 0, 0, stk, 20 );
if (!frames) {
dprintf("Stacktrace failed\n");
}
for (i=0; i<frames; i++) {
if (i==0) {
dprintf( "ChildEBP RetAddr Args to Child\n" );
}
Buffer[0] = '!';
GetSymbol(stk[i].ProgramCounter, (PUCHAR)Buffer, &displacement);
dprintf( "%08p %08p %08p %08p %08p %s",
stk[i].FramePointer,
stk[i].ReturnAddress,
stk[i].Args[0],
stk[i].Args[1],
stk[i].Args[2],
Buffer
);
if (displacement) {
dprintf( "+0x%p", displacement );
}
dprintf( "\n" );
}
}
/*
编写help扩展命令
*/
DECLARE_API ( help )
{
dprintf("Help for extension dll simple.dll\n"
"read <addr> - It reads and dumps 4 dwords at<addr>\n"
"edit <addr> <val> - It modifies a dword value to <val> at <addr>\n"
"stack - Printd current stack trace\n"
"help - Shows this help\n"
);
}
**********************************************************************
(2)def文件
;--------------------------------------------------------------------
;
;
; Module:
; simple.def
;--------------------------------------------------------------------
EXPORTS
;--------------------------------------------------------------------
; These are the extensions exported by dll
;--------------------------------------------------------------------
read
edit
help
stack
;--------------------------------------------------------------------
;
; these are the extension service functions provided for the debugger
;
;-------------------------------------------------------------------- CheckVersion
WinDbgExtensionDllInit
ExtensionApiVersion
**********************************************************************
(3)source文件
TARGETNAME=simple
TARGETPATH=obj
TARGETTYPE=DYNLINK
DLLENTRY=_DllMainCRTStartup
!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
USE_MSVCRT=1
UMTYPE=windows
SOURCES= simple.c \
simple.rc
**********************************************************************
(4)makefile文件
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the components of Windows
#
!INCLUDE $(NTMAKEENV)\makefile.def
**********************************************************************
(5)rc文件
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DLL
#define VER_FILESUBTYPE VFT2_UNKNOWN
#define VER_FILEDESCRIPTION_STR "Sample Debugger Extensions"
#define VER_INTERNALNAME_STR "simple.DLL"
#define VER_ORIGINALFILENAME_STR "simple.DLL"
#include "common.ver"
**********************************************************************
(6)编译并产生simple.dll
i.切换到windows server 2003 ddk编译环境
ii.设置windbg sdk环境变量
set DBGSDK_INC_PATH=%debuggers%\sdk\inc
set DBGSDK_LIB_PATH=%debuggers%\sdk\lib
%debuggers%要换成你自己的目录
iii.切换到包含源文件的文件夹,并执行build -cZMg
**********************************************************************
(7)加载并执行
.load %debuggers%\simple
然后就可以执行我们的扩展命令。
后记:最新版的windbg 6.6.00007.5的安装时,要选择Custom,然后手工把SDK选中才会把SDK安装上。而且关于扩展命令编写的详细方法也集成到了帮助文档中。用Windbg还可以写.net调试命令扩展,如sos.dll,这一点是其他调试器所不具备的吧。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)