任何一个成熟的软件都会具有可扩展性,可扩展性是现代软件的一个重要特征,因为它使软件更易于维护和适应变化的需求,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

// Plugin information
#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 //_WIN64 #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) //superglobal variables
extern int pluginHandle;
extern HWND hwndDlg;
extern int hMenu;
extern int hMenuDisasm;
extern int hMenuDump;
extern int hMenuStack; //functions
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)
{
// 返回false以取消加载插件。
return true;
} // 在此处取消初始化插件数据。
void pluginStop()
{
} // 在这里做GUI/菜单相关的事情。
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至此即可看到插件已经被加载成功。

1.5 为x64dbg编写插件的更多相关文章

  1. jquery编写插件的方法

     版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三种方式 1.添加新的全局函数 2 ...

  2. 再谈:jquery编写插件的方法

    版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三种方式 1.添加新的全局函数 2. ...

  3. jQuery自己编写插件()

    引言: 在项目中不同页面经常要用到已经写好的交互,比如弹窗,比如下拉菜单,比如选项卡,比如删除... 此时如果每次都把代码copy一份无疑是一件比较麻烦并且无趣的事情,而且个人认为有些low了,我们可 ...

  4. 【原】jQuery编写插件

    分享一下编写设置和获取颜色的插件,首先我将插件的名字命名为jquery.color.js.该插件用来实现以下两个功能1.设置元素的颜色.2.获取元素的颜色. 先在搭建好如下编写插件的框架: ;(fun ...

  5. jquery编写插件

    jquery编写插件的方法    版权声明:作者原创,转载请注明出处! 编写插件的两种方式: 1.类级别开发插件(1%) 2.对象级别开发(99%) 类级别的静态开发就是给jquery添加静态方法,三 ...

  6. Qt中如何 编写插件 加载插件 卸载插件

    Qt中如何 编写插件 加载插件 卸载插件是本文要介绍的内容.Qt提供了一个类QPluginLoader来加载静态库和动态库,在Qt中,Qt把动态库和静态库都看成是一个插件,使用QPluginLoade ...

  7. vscode编写插件

    vscode编写插件详细过程 前言 之前编写了一个vscode插件用vscode写博客和发布,然后有园友要求写一篇来介绍如何开发一个vscode扩展插件,或者说介绍开发这个插件的过程.然而文章还没有写 ...

  8. saltstack主机管理项目:编写插件基类-获取主机列表-提取yaml配置文件(四)

    一.编写插件基类 1.目录结构 1.我是如何获知我有多少种系统? 当客户端第一连接过来的时候,我就已经把这些文件存下来了 ,存在到哪里了?存到数据库了 每次对主机发送命令的动作时,我从库里把数据取出来 ...

  9. javascript&&jquery编写插件模板

    javascrpt插件编写模板 这里不分享如何编写插件,只留一个框架模板,使用面向对象的形式进行编写,方便管理 ;(function(window,document){ function FnName ...

  10. 13 Maven 编写插件

    Maven 编写插件 Maven 的任何行为都是由插件完成的,包括项目的清理.绵编译.测试以及打包等操作都有其对应的 Maven 插件.每个插件拥有一个或者多个目标,用户可以直接从命令行运行这些插件目 ...

随机推荐

  1. 一个IT老码农的创业和生活(一)抑郁和自杀

    2019年2月18日,离我的38周岁生日还差两个月,我的编程培训班"源栈"开张了.   在此之前,从我2002年大学毕业,6年时间,我先后做过英语教师.实习律师.公司法务.装修公司 ...

  2. 干掉 LaTeX !用BookDown写本书

    Bookdown是著名R包作者谢益辉开发的,支持采用Rmarkdown (R代码可以运行)或普通markdown编写文档,然后编译成HTML, WORD, PDF, Epub等格式. bookdown ...

  3. 深入浅出 WebRTC AEC(声学回声消除)

    前言:近年来,音视频会议产品提升着工作协同的效率,在线教育产品突破着传统教育形式的种种限制,娱乐互动直播产品丰富着生活社交的多样性,背后都离不开音视频通信技术的优化与创新,其中音频信息内容传递的流畅性 ...

  4. 2018年蓝桥杯B组C/C++国赛题解

    1.换零钞 x星球的钞票的面额只有:100元,5元,2元,1元,共4种. 小明去x星旅游,他手里只有2张100元的x星币,太不方便,恰好路过x星银行就去换零钱. 小明有点强迫症,他坚持要求200元换出 ...

  5. 2013年 第四届蓝桥杯C/C++ B组(省赛)

    第一题:高斯日记 大数学家高斯有个好习惯:无论如何都要记日记. 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们知道,那个整数就是日期,它表示那一天是高斯出生 ...

  6. the server responded with a status of 413 (Request Entity Too Large) 解决

    前端上传文件,本地测试好的,放到服务器上出现了这个错误:the server responded with a status of 413 (Request Entity Too Large) 问题原 ...

  7. uniapp H5扫码解决方案

    JS通过webView调用本地相机扫码二维码,然后webView把二维码数据传给JS 网上找了一堆资料,都是JS传值给webView的,这里自己看了下代码,通过两个步骤实现此功能1,通过JS调用web ...

  8. C语言基础之第一个C程序

    前言 在开始学习 C 语言的基础知识之前,我们需要学习如何编写.编译和运行第一个 C 程序. 要编写第一个 C 程序,打开 C 控制台并编写以下代码,我这里直接使用vs2022进行代码的编写: #in ...

  9. RL 基础 | 讲的很好的 TRPO 博客

    特意存档: 知乎 | 如何看懂TRPO里所有的数学推导细节? 感觉把 idea 讲的很清楚(虽然没有特别仔细看-

  10. Web API接口返回实现类集合的姿势了解

    大家好,我是沙漠尽头的狼. 一. 问题描述 如下图,定义两个子类Student和Employ,都继承自抽象类PersonBase: public abstract class PersonBase { ...