吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3640|回复: 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++] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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++] 纯文本查看 复制代码
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, 2024-5-6 13:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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