任何一个成熟的软件都会具有可扩展性,可扩展性是现代软件的一个重要特征,因为它使软件更易于维护和适应变化的需求,x64dbg
也不例外其可通过开发插件的方式扩展其自身功能,x64dbg
提供了多种插件接口,包括脚本插件、DLL插件、Python插件和.NET插件等。此外,x64dbg
还支持用户自定义命令和快捷键。这使得用户可以自由地扩展和自定义软件的功能,从而更好地适应开发需求。
我们以C/C++
语言为开发模板,x64dbg
插件表现出来的其实也是一个DLL
文件,他里面导出了x64dbg
所需要的几个函数,从而可以在x64dbg
启动时被加载,除去所必须的导出函数外,其他功能的实现与DLL
基本一致。
(1)开发前的准备工作
在开发x64dbg
插件时,首先需要配置插件的开发工具包,请读者将x64dbg插件包中的pluginsdk
解压到任意路径下,该包内就是开发所必须要用到的SDK库。
并打开Visual Studio 2013
并新建一个DLL
空项目,此时请读者打开”调试”->”属性页”并在配置属性页,VC++目录中引入pluginsdk
库,读者只需要配置包含目录
及库目录
即可,具体配置参数如下图所示;
当读者正确引入后,那么下一步则是新建配置文件,x64dbg官方针对配置方法给予了一个模板文件,读者可以自行去下载该模板使用。
在开发插件时,至少需要导出两个函数,函数plugsetup
以及pluginit
这两个函数是插件的标识,x64dbg将会通过这两个函数来判断是否可被加载,所以在开发时这两个插件是必须要存在的。
以下是 void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
函数的原型和解释:
void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
|
plugsetup 函数是一个可选的插件函数,用于初始化插件和设置其参数。该函数在插件加载时由主程序调用。
该函数的参数是一个指向 PLUG_SETUPSTRUCT
结构的指针,该结构包含了一些与插件有关的信息和设置。通过读取和设置 PLUG_SETUPSTRUCT
结构中的字段,插件可以初始化其自身并与主程序进行通信。具体来说,PLUG_SETUPSTRUCT
结构包含以下字段:
- StructSize: 结构体大小,用于指示传递给插件的结构的大小。插件应该使用 sizeof 运算符来设置此字段的值。
- PluginHandle: 插件的句柄,由主程序分配并传递给插件,用于标识该插件。
- hwndDlg: 插件界面的句柄,如果插件具有界面,则应该将该字段设置为其窗口句柄。
- hMenu: 插件的菜单句柄,如果插件具有菜单,则应该将该字段设置为其菜单句柄。
- hMenuDisasm: 反汇编窗口的菜单句柄,如果插件需要访问反汇编窗口的菜单,则应该将该字段设置为反汇编窗口的菜单句柄。
- hMenuDump: 转储窗口的菜单句柄,如果插件需要访问转储窗口的菜单,则应该将该字段设置为转储窗口的菜单句柄。
插件应该使用 plugsetup
函数来完成其自身的初始化和参数设置,以便在主程序中正确地运行。
以下是 bool pluginit(PLUG_INITSTRUCT* initStruct)
函数的原型和解释:
bool pluginit(PLUG_INITSTRUCT* initStruct);
|
pluginit 函数是一个必需的插件函数,用于初始化插件和注册插件的命令。该函数在插件加载时由主程序调用。
该函数的参数是一个指向 PLUG_INITSTRUCT
结构的指针,该结构包含了一些与插件有关的信息和设置。通过读取和设置 PLUG_INITSTRUCT
结构中的字段,插件可以初始化其自身并注册其命令。具体来说,PLUG_INITSTRUCT
结构包含以下字段:
- StructSize: 结构体大小,用于指示传递给插件的结构的大小。插件应该使用 sizeof 运算符来设置此字段的值。
- PluginHandle: 插件的句柄,由主程序分配并传递给插件,用于标识该插件。
- sdkVersion: 主程序的 SDK 版本号,由主程序传递给插件。
- pluginVersion: 插件的版本号,由插件设置并传递给主程序。
- PluginName: 插件的名称,由插件设置并传递给主程序。
- PluginAuthor: 插件的作者,由插件设置并传递给主程序。
- PluginDescription: 插件的描述,由插件设置并传递给主程序。
插件应该使用 pluginit
函数来完成其自身的初始化和命令注册。该函数应该返回 true
表示初始化成功,或 false
表示初始化失败。如果返回 false
,主程序将卸载该插件并显示错误信息。
如上函数解释,我们可知pluginit
适用于初始化插件的,例如增加插件菜单栏,设置插件功能等,我们以如下代码为例做一个简单的解释;
PLUG_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct) { initStruct->pluginVersion = PLUGIN_VERSION; initStruct->sdkVersion = PLUG_SDKVERSION; strncpy_s(initStruct->pluginName, PLUGIN_NAME, _TRUNCATE); pluginHandle = initStruct->pluginHandle;
initStruct->sdkVersion = PLUG_SDKVERSION; initStruct->pluginVersion = 1; const char *name = "CheckME -->"; memset(initStruct->pluginName, 0, 128); memcpy(initStruct->pluginName, name, strlen(name));
return pluginInit(initStruct); }
|
代码中通过initStruct->pluginVersion
设置了插件版本,通过initStruct->sdkVersion
设置了SDK版本,并在initStruct->pluginName
中设置了插件名称,最后调用了pluginInit(initStruct)
初始化了插件,至此该插件即创建完毕了;
而plugsetup
函数则是用于在初始化时在setupStruct->hMenu
也就是菜单顶部增加一个菜单栏,标题为PowerBy LyShark
的列表。
PLUG_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct) { hwndDlg = setupStruct->hwndDlg; hMenu = setupStruct->hMenu; hMenuDisasm = setupStruct->hMenuDisasm; hMenuDump = setupStruct->hMenuDump; hMenuStack = setupStruct->hMenuStack;
char sub_menu[] = { "PowerBy LyShark" }; _plugin_menuaddentry(setupStruct->hMenu, 2, sub_menu);
pluginSetup(); }
|
当菜单被点击是则会触发CBMENUENTRY
函数,执行该函数内的流程,如上就是插件的加载流程。
(2)开发插件实战
在本节,笔者将带领大家实现一个简单的x64dbg插件,该插件的功能很简单,当用户点击菜单栏中的选项是,我们让其弹出一个消息框,此处为了开发方便,我做了精简化,你可以直接使用我的方法来新建文件,建出来的文件只有两个非常简洁。
首先在头文件部分新建一个pluginmain.h
并增加PLUGIN_NAME
替换成自己项目的名字。
#pragma once
#define PLUGIN_NAME "LySharkBlog" #define PLUGIN_VERSION 1
#include "./bridgemain.h" #include "./_plugins.h"
#include "./_scriptapi_argument.h" #include "./_scriptapi_assembler.h" #include "./_scriptapi_bookmark.h" #include "./_scriptapi_comment.h" #include "./_scriptapi_debug.h" #include "./_scriptapi_flag.h" #include "./_scriptapi_function.h" #include "./_scriptapi_gui.h" #include "./_scriptapi_label.h" #include "./_scriptapi_memory.h" #include "./_scriptapi_misc.h" #include "./_scriptapi_module.h" #include "./_scriptapi_pattern.h" #include "./_scriptapi_register.h" #include "./_scriptapi_stack.h" #include "./_scriptapi_symbol.h"
#include "./DeviceNameResolver/DeviceNameResolver.h" #include "./jansson/jansson.h" #include "./lz4/lz4file.h" #include "./TitanEngine/TitanEngine.h" #include "./XEDParse/XEDParse.h"
#ifdef _WIN64 #pragma comment(lib, "./x64dbg.lib") #pragma comment(lib, "./x64bridge.lib") #pragma comment(lib, "./DeviceNameResolver/DeviceNameResolver_x64.lib") #pragma comment(lib, "./jansson/jansson_x64.lib") #pragma comment(lib, "./lz4/lz4_x64.lib") #pragma comment(lib, "./TitanEngine/TitanEngine_x64.lib") #pragma comment(lib, "./XEDParse/XEDParse_x64.lib") #else #pragma comment(lib, "./x32dbg.lib") #pragma comment(lib, "./x32bridge.lib") #pragma comment(lib, "./DeviceNameResolver/DeviceNameResolver_x86.lib") #pragma comment(lib, "./jansson/jansson_x86.lib") #pragma comment(lib, "./lz4/lz4_x86.lib") #pragma comment(lib, "./TitanEngine/TitanEngine_x86.lib") #pragma comment(lib, "./XEDParse/XEDParse_x86.lib") #endif
#define Cmd(x) DbgCmdExecDirect(x) #define Eval(x) DbgValFromString(x) #define dprintf(x, ...) _plugin_logprintf("[" PLUGIN_NAME "] " x, __VA_ARGS__) #define dputs(x) _plugin_logprintf("[" PLUGIN_NAME "] %s\n", x) #define PLUG_EXPORT extern "C" __declspec(dllexport)
extern int pluginHandle; extern HWND hwndDlg; extern int hMenu; extern int hMenuDisasm; extern int hMenuDump; extern int hMenuStack;
bool pluginInit(PLUG_INITSTRUCT* initStruct); void pluginStop(); void pluginSetup();
|
其次新建一个实现文件pluginmain.cpp
并写入以下代码,多数情况下我为了方便调试会使用这段代码,当我们点击菜单时会触发菜单功能,以此可以快速测试特定函数是否正常。
#include "pluginmain.h" #include <Windows.h> #include <process.h>
int pluginHandle; HWND hwndDlg; int hMenu; int hMenuDisasm; int hMenuDump; int hMenuStack;
extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info); extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct); extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct);
bool pluginInit(PLUG_INITSTRUCT* initStruct) { return true; }
void pluginStop() { }
void pluginSetup() { }
void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info) { MessageBox(0, L"hello lyshark", 0, 0); }
PLUG_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct) { initStruct->pluginVersion = PLUGIN_VERSION; initStruct->sdkVersion = PLUG_SDKVERSION; strncpy_s(initStruct->pluginName, PLUGIN_NAME, _TRUNCATE); pluginHandle = initStruct->pluginHandle;
initStruct->sdkVersion = PLUG_SDKVERSION; initStruct->pluginVersion = 1; const char *name = "CheckME -->"; memset(initStruct->pluginName, 0, 128); memcpy(initStruct->pluginName, name, strlen(name));
return pluginInit(initStruct); }
PLUG_EXPORT bool plugstop() { pluginStop(); return true; }
PLUG_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct) { hwndDlg = setupStruct->hwndDlg; hMenu = setupStruct->hMenu; hMenuDisasm = setupStruct->hMenuDisasm; hMenuDump = setupStruct->hMenuDump; hMenuStack = setupStruct->hMenuStack;
char sub_menu[] = { "PowerBy LyShark" }; _plugin_menuaddentry(setupStruct->hMenu, 2, sub_menu);
pluginSetup(); }
|
通过使用Visual Studio 2013
编译这段程序,其默认会生成一个*.dll
的文件,根据不同版本需要将其更改为*.dp32
或者*.dp64
以此来代表这是一个插件,并将更改好的插件放入到x32/plugins
目录下,重启x64dbg
至此即可看到插件已经被加载成功。