首页
论坛
专栏
课程

WinDbg插件编写――基础篇

2007-1-29 00:43 15996

WinDbg插件编写――基础篇

Lvg
5
2007-1-29 00:43
15996
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,这一点是其他调试器所不具备的吧。

2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最新回复 (10)
笨笨雄 14 2007-1-29 08:56
2
0
支持楼主出插件。。。先出个ANTI DEBUG的啊。。
prince 16 2007-1-29 11:02
3
0
强烈期待楼主继续~
阿健 5 2007-1-29 11:34
4
0
高人层出不穷呀
Lvg 5 2007-1-29 16:12
5
0
谢谢楼上几位

加了个简单的例子。PS:编程的学习要多看看代码
dhlduda 2009-2-24 16:43
6
0
Lvg,我想问一下,编写windbg插件时,能用win32的api吗?我怕有些功能windbg的api不能完成
xnop 2009-4-4 03:29
7
0
怎么老编译不过去呢
fengjl 1 2009-6-9 10:11
8
0
用xp ddk编译,会找不到jvc.exe命令?没有java的东西怎么会这个?奇怪,难道非要2003以上版本的ddk吗?
lijingli 2013-3-6 22:00
9
0
实验了下
        hUser32 = LoadLibrary("User32.dll");
        lMsgBox = GetProcAddress(hUser32,"MessageBox");
不过不能掉啊。。。。
lijingli 2013-3-6 22:01
10
0
这样就不能写个有界面的插件了。。。。
透明色 2 2013-3-8 07:20
11
0
要是能把 windbg 的脚本替换掉好了
游客
登录 | 注册 方可回帖
返回