x64dbg插件编写基础

admin 2023年4月13日08:46:58评论66 views字数 5483阅读18分16秒阅读模式

x64dbg插件编写基础

本文为看雪论坛优秀文章

看雪论坛作者ID:st0ne


x64dbg 手册:https://help.x64dbg.com/en/latest/developers/index.html





x64dbg扩展功能的三种方式


x64dbg扩展功能的方式有三种:

① 写脚本(python或者idc)。
② 脚本DLL,就是编写一个DLL导出 AsyncStart() or Start(),然后通过命令scriptdll/dllscript来加载DLL执行代码。
③ 编写插件。


在x64dbg的文档中并没有说明 "插件" 到底是一个什么东西,只是说插件的后缀名叫dp32或者dp64,通过观察其他的插件发现其实就是DLL,只不过是导出了一些指定函数的DLL。


本文的目的就是说明编写x64dbg插件的步骤,然后编写一个简单的x64dbg插件。





环境配置


本机环境:win11 21h2、vs2022、x64dbg(snapshot_2023-01-25_11-53)


2.1 x64的SDK


要给x64dbg编写插件肯定需要x64dbg提供的sdk包,sdk包就在x64dbg的根目录之下的pluginsdk文件夹中:

├───pluginsdk│   ├───dbghelp│   ├───DeviceNameResolver│   ├───jansson│   ├───lz4│   ├───TitanEngine│   └───XEDParse└───release    ├───...


在pluginsdk中有x64dbg自己提供的头文件和lib文件,以及它使用的其他第三方库的头文件和lib文件,比如XEDParse、TitanEngine等。


2.2 配置vs工程


先创建一个VS的DLL工程,这一步不必多说。


将整个pluginsdk文件夹拷贝到工程目录之下,虽然多了很多东西,但是比少拷贝了文件去找要好。


然后配置头文件目录和lib文件的目录以及引入lib文件:

在工程属性->vc++目录->外部包含目录加入pluginsdk路径;
在工程属性->vc++目录->库目录加入pluginsdk路径;
在工程属性->链接器->输入->附加依赖项中添加 x32bridge.lib和x32dbg.lib,这也是pluginsdk下唯二的两个lib。





基本导出函数


3.1 pluginit


函数声明:

extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct);


pluginit函数是x64dbg插件必须导出的一个函数。


在整个函数中,要做的事件就是填充参数initStruct:

struct PLUG_INITSTRUCT{    [IN] int pluginHandle; //插件的句柄    [OUT] int sdkVersion; //填 PLUG_SDKVERSION 即可    [OUT] int pluginVersion; // 填插件的版本    [OUT] char pluginName[256]; // 填插件指针};


所以在pluginit中要做的事情就是填写插件的基本信息:

//必需的,插件初始化函数extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT * initStruct){    _plugin_logprintf("[%s] pluginitrn", TAG);//打印日志     memcpy_s(        initStruct->pluginName,        sizeof(initStruct->pluginName),        TAG,        strlen(TAG));    initStruct->sdkVersion = PLUG_SDKVERSION;    initStruct->pluginVersion = 1;     if (!InitPlugin())//做一些初始化动作    {        _plugin_logprintf("[%s] pluginit Failedrn", TAG);//打印日志        return false;    }    return true;}


3.2 plugstop和plugsetup


这两个导出函数不是必须的,但是可以在里面做一些事情:

plugstop:插件被移除的时候被调用,可以用来清除注册的回调和命令,清理资源。
plugsetup:当插件初始化成功的时候调用,可以在这里添加菜单、做其他的界面相关的事情。


本例在plugsetup中添加了两个子菜单,代码如下:

//非必需,插件被移除时调用extern "C" __declspec(dllexport) bool plugstop(){    _plugin_logprintf("[%s] plugstoprn", TAG);    return true;} //非必需,启动插件时调用//在这里执行UI操作,比如增加菜单extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT * setupStruct){    _plugin_logprintf("[%s] plugsetuprn", TAG);     //添加子菜单    _plugin_menuaddentry(setupStruct->hMenu, 0, "enable UEH");    _plugin_menuaddentry(setupStruct->hMenu, 1, "disable UEH");}





事件回调函数


当我们添加了插件的子菜单之后,要如何响应菜单的点击呢?


导出以CB开头的函数就可以去接收到对应的事件,比如:

extern "C" __declspec(dllexport) void CBINITDEBUG(CBTYPE cbType, PLUG_CB_INITDEBUG* info); //初始化调试extern "C" __declspec(dllexport) void CBSTOPDEBUG(CBTYPE cbType, PLUG_CB_STOPDEBUG* info); //停止调试extern "C" __declspec(dllexport) void CBEXCEPTION(CBTYPE cbType, PLUG_CB_EXCEPTION* info); //异常extern "C" __declspec(dllexport) void CBDEBUGEVENT(CBTYPE cbType, PLUG_CB_DEBUGEVENT* info); //调试事件extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info); //点击子菜单


参数中所用到的结构体可以在这里找到:https://help.x64dbg.com/en/latest/developers/plugins/Callbacks/index.html,只要是满足CB*的导出函数,且属于这里面的类型。https://help.x64dbg.com/en/latest/developers/plugins/API/registercallback.html,应该都可以注册成功,注意函数名中不要有下划线。


本例中,我们只需要简单的响应一下子菜单的点击:

//菜单响应回调extern "C" __declspec(dllexport) void CBMENUENTRY(    CBTYPE bType,    PLUG_CB_MENUENTRY * pEntry){    switch (pEntry->hEntry)    {    case 0:    //注册时填的菜单ID        EnableUeh();        break;    case 1:        DisableUeh();        break;    default:        break;    }}





库函数


在插件中,我们可以调用Dbg开头的函数来辅助功能,函数列表:https://help.x64dbg.com/en/latest/developers/functions/debug/index.html


比如读写被调试进程可以使用:

bool DbgMemRead(  duint va,  void* dest,  duint size); bool DbgMemWrite(duint va,void* dest,duint size);


其他功能就暂时还没探索,本例中用这两个就够啦。





对UnhandledExceptionFilter打补丁


本例中的插件需要实现对UnhandledExceptionFilter打补丁的功能,实现调试器可以调试UEH回调


6.1 为什么要打补丁


下面是笔者的通过测试的一点浅薄理解,没有跟踪系统的代码,如有不对的地方还请大佬们指出。


应用层派发异常的流程大致如下:

x64dbg插件编写基础

根据上面的流程图,在异常从SEH中出来的时候,根据是否有调试器,要么派发给调试器,要么派发给UEH回调,二选一,所以在调试的过程中不会之下UEH回调的代码。


为了能在调试器中调试UEH回调,我们需要改变一下系统异常分发的流程,使其走到另一个分支去。


这个分支出现在kernelbase/kernel32!UnhandledExceptionFilter 中,所以需要对其打补丁。


6.2 实现功能


剩下的步骤就很简单了,首先在插件初始化时,判断系统版本,获取UnhandledExceptionFilter 的地址,通过特征码找到需要打补丁的地址。

bool InitPlugin(){    //获取版本号    DWORD dwBuildVer = GetVerSion();     PUCHAR pFuncBegin = (PUCHAR)GetProcAddress(        GetModuleHandleA("kernelbase"),        "UnhandledExceptionFilter");    if (pFuncBegin == NULL)    {        pFuncBegin = (PUCHAR)GetProcAddress(            GetModuleHandleA("kernel32"),            "UnhandledExceptionFilter");         if (pFuncBegin == NULL)        {            _plugin_logprintf("[%s] Get kernelbase!UnhandledExceptionFilter Addr Failedrn", TAG);            bIsEnabledUeh = false;            return false;        }    }     //获取特征码    if (dwBuildVer >= 22000)    {        //win11        g_pUehSig = UehSigWin11;        _plugin_logprintf("[%s] OS Build Number: %drn", TAG, dwBuildVer);    }    else if (dwBuildVer == 7600 || dwBuildVer == 7601)    {        //win7        g_pUehSig = UehSigWin7;        _plugin_logprintf("[%s] OS Build Number: %drn", TAG, dwBuildVer);    }     g_pUehPatchPoint = FindSignatureCode(pFuncBegin, 0x100, g_pUehSig);     if (g_pUehPatchPoint == NULL)    {        _plugin_logprintf("[%s] Signatrue Not Found!rn", TAG);        bIsEnabledUeh = false;        return false;    }     return true;}


然后响应菜单事件,当点击EnableUeh时,打补丁,点击DisableUeh时,恢复补丁。

void EnableUeh(){    //保存原来的数据    DbgMemRead((duint)g_pUehPatchPoint, UehPatchRawData, strlen((const char*)g_pUehSig) / 2);     //打补丁    if (DbgMemWrite((duint)g_pUehPatchPoint, UehPathCode, sizeof(UehPathCode)))    {        _plugin_logprintf("[%s] Enable UEH Success!rn", TAG);        bIsEnabledUeh = true;    }    else    {        _plugin_logprintf("[%s] Enable UEH Failed!rn", TAG);        bIsEnabledUeh = false;    }} void DisableUeh(){    if (bIsEnabledUeh == true)    {        //Patch        if (DbgMemWrite((duint)g_pUehPatchPoint, UehPatchRawData, strlen((const char*)g_pUehSig) / 2))        {            _plugin_logprintf("[%s] Enable UEH Success!rn", TAG);            bIsEnabledUeh = false;        }        else        {            _plugin_logprintf("[%s] Enable UEH Failed!rn", TAG);        }    }}


一个简单的插件框架就这样完成了。




x64dbg插件编写基础


看雪ID:st0ne

https://bbs.kanxue.com/user-home-887003.htm

*本文由看雪论坛 st0ne 原创,转载请注明来自看雪社区

x64dbg插件编写基础

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复


x64dbg插件编写基础


x64dbg插件编写基础

球分享

x64dbg插件编写基础

球点赞

x64dbg插件编写基础

球在看


x64dbg插件编写基础

点击“阅读原文”,了解更多!

原文始发于微信公众号(看雪学苑):x64dbg插件编写基础

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月13日08:46:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   x64dbg插件编写基础http://cn-sec.com/archives/1669538.html

发表评论

匿名网友 填写信息