吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4771|回复: 6
收起左侧

[C&C++ 转载] 【c++】x64dbg插件开发,摸索前进:第二章第一个正式插件

[复制链接]
only998 发表于 2021-12-14 07:43
第一章环境搭建请移步:https://www.52pojie.cn/thread-1560376-1-1.html
正式开始前先吐槽下x64dbg的手册,万能不更新,至今停留在0.1版,想要点关键内容,没有,摊手。。

首先说下x64dbg的插件工作方式:
启动后搜索 plugins 目录下以 .dp32 或者 .dp64结尾的文件(分别对应x86和x64版本,之后不提x64了,模式都是一样的)
载入dp32文件后,检索导出函数 pluginit 并且调用,调用成功后,再调用 plugsetup 函数。插件卸载或者程序退出时调用 plugstop 函数

跟其他GUI程序一样,x64dbg同样存在事件回调,有以下几类(英文直译,建议大家看手册,其实能知道发生的时机,只不过英文里写了很多其他提示,拗口,难懂)
CBINITDEBUG  调试器载入某个exe或者dll时,或者采用“附加”的方式,则触发事件
CBSTOPDEBUG  调试器退出exe或者dll或者脱离附加时,通常进行变量重置,或者
CBCREATEPROCESS 创建进程、初始化符号句柄(这个不太懂?)、数据库文件并且在TLS回调的入口处设置了断点(也不太懂?),触发事件
CBEXITPROCESS   退出进程、符号句柄清理之前,触发事件
CBCREATETHREAD  在线程创建之后,线程添加到内部线程列表之后,“before breaking the debugger on thread creation and after setting breakpoints on the 线程入口”后面这句有点怪,我猜是指如果在线程入口添加了断点,那么在调试中断之前就已经触发了 CBCREATETHREAD事件
CBEXITTHREAD    在线程终止后、从内部线程列表中删除线程之前、同样,如果你在线程结束处设置了断点,CBEXITTHREAD 会在断点触发前触发
CBSYSTEMBREAKPOINT  系统断点时触发
CBLOADDLL        DLL加载时
CBUNLOADDLL      DLL卸载时
CBOUTPUTDEBUGSTRING 字符串存储到系统日志之前触发
CBEXCEPTION      发生异常时触发,限定条件after setting the continue status, after locking the debugger to pause:
CBBREAKPOINT     遇到普通/内存/硬件断点时,并且调试器已经暂停,触发事件
CBPAUSEDEBUG     调试器暂停时触发,并且早于任何其他的与调试器暂停有关的回调函数
CBRESUMEDEBUG    调试器从暂停状态切换到继续调试时触发
CBSTEPPED        单步时触发
CBATTACH         附加到进程时触发
CBDETACH         脱离进程时触发
CBDEBUGEVENT     任何与调试有关的事件都会触发此回调,即使是内部处理的事件。避免在此处执行耗时的操作,这将大大降低调试器的速度!
CBMENUENTRY      单击插件创建的菜单项时调用,当此回调返回时,GUI将恢复
CBWINEVENT       windows事件在TranslateMessage和DispatchMessage之前触发此事件(预译信息)。注意不要在这里调用user32函数,如果不采取措施会造成递归调用,然后boom~
CBWINEVENTGLOBAL  跟CBWINEVENT一样,不过这个是全局的,因此它还捕获热键(请参见Qt帮助文档). 在Qt5中,这个事件几乎不会被调用,请改用PLUG_CB_WINEVENT。
CBLOADSAVEDB      数据加载或者保存到数据库中时,以JSON格式检索数据,参考_plugin.h文件
CBFILTERSYMBOL    在将符号添加到标签之前触发,如果要过滤符号,请将retval设置为false
CBTRACEEXECUTE    在条件跟踪期间触发,将stop设置为true结束跟踪

举个例子,调试器载入某个exe时会触发 CBINITDEBUG 事件,这时候调试器会在插件中寻找名为 CBINITDEBUG 的导出函数,找到了就调用它。
每个一个事件的原型都有不同的调用函数,建议大家多看下手册,我这里就不一一列出了。

有回调事件就有正常的功能支持,直接 #include "_plugins.h" 就可以启用所有的功能操作。
操作函数分为三种,一种是gui相关的,均以Gui****开头;一种是调试相关的,均以Dbg*****开头;还有一种是Bridge函数,均以Bridge*****开头。
除此之外还有一种_plugin函数,用于插件注册,信息输出,等

了解了x64dbg的插件工作模式,其实用易语言都能写出满足要求的插件,首先你必须实现 pluginit 函数,有菜单的话需实现 plugsetup 函数,
事件回调你可以直接实现对应的事件回调函数,也可以通过 _plugin_registercallback 注册满足要求的回调函数。其次,导入x32_bridge.dll中对应的操作函数,参考手册就可以跟x64dbg交互了。不过x64dbg的手册稀烂,还是c++方面调试和查看函数原型。

基本情况介绍到这,看下我的第一个插件要实现的功能:我希望可以快速将dll的某个地址转换为 相对dll载入基址的地址,方便在后期编程爆破中直接操作。
为此我需要实现以下函数:
pluginit  这个是每个插件必须的,设置插件版本和名称
plugsetup 注册菜单,没有菜单怎么行
CBMENUENTRY 菜单被单击时回调,以便我做操作

初始化没啥好说的,注意所有x64dbg中的字符串你都应该使用utf8编码
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct);
//初始化插件
bool pluginit(PLUG_INITSTRUCT* initStruct) {
    initStruct->sdkVersion = PLUG_SDKVERSION;
    initStruct->pluginVersion = 1;
    const char *name = "LoliTool";
    memset(initStruct->pluginName, 0, 128);
    memcpy(initStruct->pluginName, name, strlen(name));
    return true;
}


plugsetup注册菜单,注意不要使用GuiMenuAdd和GuiMenuAddEntry这两坑爹函数,注册出来的菜单不能触发回调,得用_plugin_menuaddentry,第二个参数是你给菜单的编码,在回调事件里会用到
[C++] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
//初始化菜单
void plugsetup(PLUG_SETUPSTRUCT* setupStruct) {
    //“相对基址”,这里直接采用utf8编码
    char name1[] = { 0xe7,0x9b,0xb8,0xe5,0xaf,0xb9,0xe5,0x9f,0xba,0xe5,0x9d,0x80,0 };
    //注意添加的是二级菜单,一级的名称默认跟插件名称一致
    _plugin_menuaddentry(setupStruct->hMenuDisasm, 2, name1);
}


注意setupStruct有以下几个成员:
    HWND hwndDlg;      //x64dbg的窗口句柄,想自绘的可以用
    int hMenu;                //菜单栏的“插件”
    int hMenuDisasm;     //反汇编右键菜单
    int hMenuDump;     //内存窗口右键菜单
    int hMenuStack;       //堆栈窗口右键菜单
    int hMenuGraph;     // 流程图窗口右键菜单
    int hMenuMemmap;     //内存布局窗口右键菜单
    int hMenuSymmod;      // //符号窗口右键菜单
要注册哪个菜单就使用对应的变量

CBMENUENTRY 回调事件
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info);
void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info) {
    SELECTIONDATA sel;
    if(GuiSelectionGet(GUI_DISASSEMBLY, &sel)){ //选择位置指的是字节位置
        char *moduleName1 = new char[128];
        char *moduleName2 = new char[128];
        char *out = new char[128];
        int outlen;
 
        DbgGetModuleAt(sel.start, moduleName1);  //获得选区起始位置的模块名
        duint baseAdress = DbgModBaseFromName(moduleName1);  //取得模块的基址
 
        BASIC_INSTRUCTION_INFO asminfo;
        for (duint ii = sel.start; ii <= sel.end; ii += asminfo.size) {
            outlen = 0;
            memset(out, 0, 128);
 
            //获取选区本行的反汇编信息
            DbgDisasmFastAt(ii, &asminfo);
            //分别是当前地址,jmp/call地址,内存读写地址
            duint dstAdress[] = { ii, asminfo.value.value , asminfo.memory.value};
            //提取出所有属于模块内的地址
            for (int nn = 0; nn < 3; nn++) {
                if (dstAdress[nn] == 0) continue;
                DbgGetModuleAt(dstAdress[nn], moduleName2);
                if (strcmp(moduleName1, moduleName2) == 0) {
                    outlen = sprintf(out + outlen, "[0x%x];", dstAdress[nn] - baseAdress);
                }
            }
 
            //结果写到注释里
            DbgSetCommentAt(ii, out);
        }
 
        delete[] moduleName1;
        delete[] moduleName2;
        delete[] out;
    }
}


GuiSelectionGet:获取指定视窗的选区,第一个参数可以选择
    GUI_DISASSEMBLY,  值为0,下面依次递增1, 反汇编窗口
    GUI_DUMP,    内存窗口
    GUI_STACK,    堆栈窗口
    GUI_GRAPH,  流程图窗口
    GUI_MEMMAP,  内存布局窗口
    GUI_SYMMOD,  符号窗口
参数二为SELECTIONDATA变量

DbgGetModuleAt:获取指定地址的模块名称
DbgDisasmFastAt:获取指定地址的反汇编信息,产生的信息包括是否 jmp/call 内存操作 跳转操作等等
DbgSetCommentAt:添加注释

插件成果如下:
0000.png

0001.png

最后吐槽下,按第一章搭建的环境,不能从vs的快速启动界面中启动,必须从文件夹中打开对应的工程。

免费评分

参与人数 2吾爱币 +11 热心值 +2 收起 理由
冥界3大法王 + 4 + 1 我烤,一群瞎子不识真香玉。。。
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

列明 发表于 2021-12-14 11:56
工具名字可爱
zobook 发表于 2021-12-26 19:28
冥界3大法王 发表于 2021-12-26 19:41
@only998
帮助给的很混蛋;复制一下都费劲。很多都没有实例;
汉化的版的到处是错误;新版更新了内容,而有人却错把汉化版当宝。
 楼主| only998 发表于 2021-12-26 22:05
冥界3大法王 发表于 2021-12-26 19:41
@only998
帮助给的很混蛋;复制一下都费劲。很多都没有实例;
汉化的版的到处是错误;新版更新了内容,而 ...

真相了,很多函数的功能都要靠名字猜,虽然一共也没多少个函数,但是总感觉心累。我现在转到C#写插件了,c++虽然直接但是搞gui费劲,c#就比较完美了
normalfriend 发表于 2022-2-14 10:39
你好 大佬 如果我想在x64dbg 插件菜单中 显示自己插件 应该怎么做
 楼主| only998 发表于 2022-2-14 17:47
normalfriend 发表于 2022-2-14 10:39
你好 大佬 如果我想在x64dbg 插件菜单中 显示自己插件 应该怎么做

[C++] 纯文本查看 复制代码
1
2
3
4
5
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
//初始化菜单
void plugsetup(PLUG_SETUPSTRUCT* setupStruct) {
    _plugin_menuaddentry(setupStruct->hMenu, 你给插件的ID整数, utf8的const char*);
}
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-21 16:21

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表