【x64dbg】我是如何搭建x64dbg插件的vs开发环境(可直接运行调试)

0. 需求

今天是元宵节,祝各位元宵节快乐! 因为我在软件逆向方面有一些自己想出来的概念和思路需要去实践验证,于是想到了以x64dbg插件的形式作为基础设施的想法,因此写下了此文章来记录自己是怎么搭建x64dbg插件开发环境的 —— 2025/2/12 16:15

1. 官方地址

x64dbg 官网:https://x64dbg.com/#features

x64dbg 开发帮助手册页:https://help.x64dbg.com/en/latest/developers/index.html
https://github.com/x64dbg/x64dbg/wiki/Plugins

x64dbg插件模板github仓库:https://github.com/x64dbg/PluginTemplate

2. 下载插件模板

首先前往 https://github.com/x64dbg/PluginTemplate 去下载源码,然后解压到你自定义的一个路径里
在这里插入图片描述
在这里插入图片描述
我这边随便选择一个路径作示例
在这里插入图片描述
接着,以这个文件夹路径作为根目录,打开cmd
在这里插入图片描述
输入命令:cmake -B build -G "Visual Studio 17 2022" -A x64(你需要先安装cmake)
在这里插入图片描述
成功了之后,我们可以看到文件夹里新增了一个build文件夹
在这里插入图片描述

我们点进去,里边有个sln文件,这个就是x64dbg插件的项目文件了
在这里插入图片描述

插件的主要代码都是在PluginTemplate -> src -> plugin.cpp 这里
在这里插入图片描述
在这里插入图片描述

3. 如何编译调试

针对x64dbg插件的调试,其实就是针对dll的调试,按照dll的调试方法去配置即可。


  1. 首先,将 PluginTemplate 设置为启动项目
    在这里插入图片描述
  2. 之后先试编译一下,拿到你的编译路径
    在这里插入图片描述
    比如我这里的路径是:E:\Chrome Download\PluginTemplate-main\build\Debug\PluginTemplate.dp64

之后找到你x64dbg.exe的路径,在这个路径上找到"plugin"这个文件夹,拿到路径名
在这里插入图片描述
在这里插入图片描述
我这里是 D:\Chrome Download\x64dbg_2023_06_15\release\x64\plugins

  1. 现在,我们构造一条批处理命令:
copy "E:\Chrome Download\PluginTemplate-main\build\Debug\PluginTemplate.dp64" "D:\Chrome Download\x64dbg_2023_06_15\release\x64\plugins\PluginTemplate.dp64" /y

这条命令的意思是,复制编译好的dp64插件到x64dbg的plugin目录中,后面的/y表示复制的时候不需要重复确认

拼接好之后,我们重新回到vs,打开 PluginTemplate 的属性
在这里插入图片描述
之后打开配置属性 -> 生成事件 -> 生成后事件,在右边找到命令行
在这里插入图片描述
我们把刚刚构造的复制命令填到命令行的最上边去
在这里插入图片描述
单击确认。

  1. 之后我们再来到配置属性 -> 调试,在右边中,我们在"本地 Windows 调试器"的基础上,在命令行的右边单击浏览
    在这里插入图片描述
    在里边找到你自己安装的x64dbg.exe的路径
    在这里插入图片描述
    确认后,我们就应用退出。

以下是我的plugin.cpp的代码,我删除了原先模板的一些代码:

#include "plugin.h"

// Examples: https://github.com/x64dbg/x64dbg/wiki/Plugins
// References:
// - https://help.x64dbg.com/en/latest/developers/plugins/index.html
// - https://x64dbg.com/blog/2016/10/04/architecture-of-x64dbg.html
// - https://x64dbg.com/blog/2016/10/20/threading-model.html
// - https://x64dbg.com/blog/2016/07/30/x64dbg-plugin-sdk.html

// Command use the same signature as main in C
// argv[0] contains the full command, after that are the arguments
// NOTE: arguments are separated by a COMMA (not space like WinDbg)
static bool cbExampleCommand(int argc, char** argv) {
    auto parseExpr = [](const char* expression, duint& value){
        bool success = false;
        value = DbgEval(expression, &success);
        if (!success)
            dprintf("Invalid expression '%s'\n", expression);
        return success;
    };
    if (strcmp(argv[1], "print") == 0) {
        dprintf("Hello,World\n");
        
        dprintf("rax: %llx\n", Script::Register::GetRAX());
        dprintf("rbx: %llx\n", Script::Register::GetRBX());
        dprintf("rcx: %llx\n", Script::Register::GetRCX());
        dprintf("rdx: %llx\n", Script::Register::GetRDX());
        dprintf("rsi: %llx\n", Script::Register::GetRSI());
        dprintf("rdi: %llx\n", Script::Register::GetRDI());
        dprintf("rsp: %llx\n", Script::Register::GetRSP());
        dprintf("rbp: %llx\n", Script::Register::GetRBP());
        dprintf("rip: %llx\n", Script::Register::GetRIP());
        dprintf("r8: %llx\n", Script::Register::GetR8());
        dprintf("r8: %llx\n", Script::Register::GetR9());
        dprintf("r10: %llx\n", Script::Register::GetR10());
        dprintf("r11: %llx\n", Script::Register::GetR11());
        dprintf("r12: %llx\n", Script::Register::GetR12());
        dprintf("r13: %llx\n", Script::Register::GetR13());
        dprintf("r14: %llx\n", Script::Register::GetR14());
        dprintf("r15: %llx\n", Script::Register::GetR15());

        dprintf("zf: %d\n", Script::Flag::Get(Script::Flag::ZF));
        dprintf("of: %d\n", Script::Flag::Get(Script::Flag::OF));
        dprintf("cf: %d\n", Script::Flag::Get(Script::Flag::CF));
        dprintf("pf: %d\n", Script::Flag::Get(Script::Flag::PF));
        dprintf("sf: %d\n", Script::Flag::Get(Script::Flag::SF));
        dprintf("tf: %d\n", Script::Flag::Get(Script::Flag::TF));
        dprintf("af: %d\n", Script::Flag::Get(Script::Flag::AF));
        dprintf("df: %d\n", Script::Flag::Get(Script::Flag::DF));
        dprintf("if: %d\n", Script::Flag::Get(Script::Flag::IF));

    }

    return true;
}

// Initialize your plugin data here.
bool pluginInit(PLUG_INITSTRUCT* initStruct)
{
    dprintf("pluginInit(pluginHandle: %d)\n", pluginHandle);

    // Prefix of the functions to call here: _plugin_register
    _plugin_registercommand(pluginHandle, PLUGIN_NAME, cbExampleCommand, true);

    // Return false to cancel loading the plugin.
    return true;
}

// Deinitialize your plugin data here.
// NOTE: you are responsible for gracefully closing your GUI
// This function is not executed on the GUI thread, so you might need
// to use WaitForSingleObject or similar to wait for everything to close.
void pluginStop(){
    // Prefix of the functions to call here: _plugin_unregister

    dprintf("pluginStop(pluginHandle: %d)\n", pluginHandle);
}

// Do GUI/Menu related things here.
// This code runs on the GUI thread: GetCurrentThreadId() == GuiGetMainThreadId()
// You can get the HWND using GuiGetWindowHandle()
void pluginSetup(){
    // Prefix of the functions to call here: _plugin_menu

    dprintf("pluginSetup(pluginHandle: %d)\n", pluginHandle);
}

我们直接调试运行
在这里插入图片描述
这时候,我们的x64dbg就运行出来了,随便挂载一个程序调试下
在这里插入图片描述
然后我们在最下边的命令中输入 PluginTemplate print,并来到x64dbg的日志窗口:
在这里插入图片描述

可以看到,它输出了寄存器和标志位的信息。
而且此时,我们也可以在vs中随便设置断点进行调试
在这里插入图片描述

4. 其他功能示例代码(供参考)

#include "plugin.h"

enum {
    MENU_HELLO,
};

// Command use the same signature as main in C
// argv[0] contains the full command, after that are the arguments
// NOTE: arguments are separated by a COMMA (not space like WinDbg)
static bool cbExampleCommand(int argc, char** argv) {

    auto parseExpr = [](const char* expression, duint& value){
        bool success = false;
        value = DbgEval(expression, &success);
        if (!success) {
            dprintf("Invalid expression '%s'\n", expression);
        }
        return success;
    };
    if (strcmp(argv[1], "print") == 0) {
        dprintf("ProcessID: 0x%x\n", DbgValFromString("$pid")); // 被调试的可执行文件的进程ID
        dprintf("hProcess: 0x%x\n", DbgValFromString("$hp")); // 调试的可执行文件句柄
        dprintf("\n");

        char modname[MAX_PATH] = { 0 };
        char path[MAX_PATH] = { 0 };
        Script::Module::GetMainModuleName(modname);
        Script::Module::GetMainModulePath(path);
        
        dprintf("name: %s\n", modname);
        dprintf("base: %llx\n", Script::Module::GetMainModuleBase());
        dprintf("entry: %llx\n", Script::Module::GetMainModuleEntry());
        dprintf("path: %s\n", path);
        dprintf("size: %llx\n", Script::Module::GetMainModuleSize());

        ListInfo modSecs;
        Script::Module::GetMainModuleSectionList(&modSecs);
        Script::Module::ModuleSectionInfo* sectionInfo(static_cast<Script::Module::ModuleSectionInfo*>(modSecs.data));
        for (int i = 0; i < modSecs.count; i++){
            dprintf("section name [%d]: %s\n", i, sectionInfo[i].name);
            dprintf("section addr [%d]: %llx\n", i, sectionInfo[i].addr);
            dprintf("section size [%d]: %llx\n", i, sectionInfo[i].size);
            dprintf("\n");
        }
        BridgeFree(sectionInfo);

        Script::Module::ModuleInfo mainMod;
        Script::Module::GetMainModuleInfo(&mainMod);

        ListInfo modExports;
        ListInfo modImports;
        Script::Module::GetExports(&mainMod, &modExports);
        Script::Module::GetImports(&mainMod, &modImports);
        Script::Module::ModuleExport* ExportInfo(static_cast<Script::Module::ModuleExport*>(modExports.data));
        Script::Module::ModuleImport* ImportInfo(static_cast<Script::Module::ModuleImport*>(modImports.data));
        for (int i = 0; i < modExports.count; i++) {
            dprintf("export name [%d]: %s\n", i, ExportInfo[i].name);
            dprintf("export rva [%d]: %llx\n", i, ExportInfo[i].rva);
            dprintf("export va [%d]: %llx\n", i, ExportInfo[i].va);
            dprintf("export forwarded [%d]: %d\n", i, ExportInfo[i].forwarded);
            dprintf("export forwardName [%d]: %s\n", i, ExportInfo[i].forwardName);
            dprintf("export ordinal [%d]: %llx\n", i, ExportInfo[i].ordinal);
            dprintf("\n");
        }
        for (int i = 0; i < modImports.count; i++) {
            dprintf("import name [%d]: %s\n", i, ImportInfo[i].name);
            dprintf("import ordinal [%d]: %llx\n", i, ImportInfo[i].ordinal);
            dprintf("import rva [%d]: %llx\n", i, ImportInfo[i].iatRva);
            dprintf("import va [%d]: %llx\n", i, ImportInfo[i].iatVa);
            dprintf("\n");
        }
        BridgeFree(ExportInfo);
        BridgeFree(ImportInfo);

        ListInfo mods;
        Script::Module::GetList(&mods);
        Script::Module::ModuleInfo* modInfo(static_cast<Script::Module::ModuleInfo*>(mods.data));
        for (int i = 0; i < mods.count; i++) {
            dprintf("module name [%d]: %s\n", i, modInfo[i].name);
            dprintf("module base [%d]: %llx\n", i, modInfo[i].base);
            dprintf("module entry [%d]: %llx\n", i, modInfo[i].entry);
            dprintf("module path [%d]: %s\n", i, modInfo[i].path);
            dprintf("module sectionCount [%d]: %d\n", i, modInfo[i].sectionCount);
            dprintf("module size [%d]: %llx\n", i, modInfo[i].size);
            dprintf("\n");
        }
        BridgeFree(modInfo);
    }
    if (strcmp(argv[1], "print2") == 0) {
        dprintf("rax: %llx\n", Script::Register::Get(Script::Register::RAX));
        dprintf("rbx: %llx\n", Script::Register::Get(Script::Register::RBX));
        dprintf("rcx: %llx\n", Script::Register::Get(Script::Register::RCX));
        dprintf("rdx: %llx\n", Script::Register::Get(Script::Register::RDX));
        dprintf("rsi: %llx\n", Script::Register::Get(Script::Register::RSI));
        dprintf("rdi: %llx\n", Script::Register::Get(Script::Register::RDI));
        dprintf("rsp: %llx\n", Script::Register::Get(Script::Register::RSP));
        dprintf("rbp: %llx\n", Script::Register::Get(Script::Register::RBP));
        dprintf("rip: %llx\n", Script::Register::Get(Script::Register::RIP));
        dprintf("r8: %llx\n", Script::Register::Get(Script::Register::R8));
        dprintf("r9: %llx\n", Script::Register::Get(Script::Register::R9));
        dprintf("r10: %llx\n", Script::Register::Get(Script::Register::R10));
        dprintf("r11: %llx\n", Script::Register::Get(Script::Register::R11));
        dprintf("r12: %llx\n", Script::Register::Get(Script::Register::R12));
        dprintf("r13: %llx\n", Script::Register::Get(Script::Register::R13));
        dprintf("r14: %llx\n", Script::Register::Get(Script::Register::R14));
        dprintf("r15: %llx\n", Script::Register::Get(Script::Register::R15));

        dprintf("dr0: %llx\n", Script::Register::Get(Script::Register::DR0));
        dprintf("dr1: %llx\n", Script::Register::Get(Script::Register::DR1));
        dprintf("dr2: %llx\n", Script::Register::Get(Script::Register::DR2));
        dprintf("dr3: %llx\n", Script::Register::Get(Script::Register::DR3));
        dprintf("dr6: %llx\n", Script::Register::Get(Script::Register::DR6));
        dprintf("dr7: %llx\n", Script::Register::Get(Script::Register::DR7));

        dprintf("zf: %d\n", Script::Flag::Get(Script::Flag::ZF));
        dprintf("of: %d\n", Script::Flag::Get(Script::Flag::OF));
        dprintf("cf: %d\n", Script::Flag::Get(Script::Flag::CF));
        dprintf("pf: %d\n", Script::Flag::Get(Script::Flag::PF));
        dprintf("sf: %d\n", Script::Flag::Get(Script::Flag::SF));
        dprintf("tf: %d\n", Script::Flag::Get(Script::Flag::TF));
        dprintf("af: %d\n", Script::Flag::Get(Script::Flag::AF));
        dprintf("df: %d\n", Script::Flag::Get(Script::Flag::DF));
        dprintf("if: %d\n", Script::Flag::Get(Script::Flag::IF));
    }
    if (strcmp(argv[1], "debug") == 0) {
        if (strcmp(argv[2], "run") == 0) {
            Script::Debug::Run();
        }
        if (strcmp(argv[2], "pause") == 0) {
            Script::Debug::Pause();
        }
        if (strcmp(argv[2], "stop") == 0) {
            Script::Debug::Stop();
        }
        if (strcmp(argv[2], "wait") == 0) {
            Script::Debug::Wait();
        }
        if (strcmp(argv[2], "in") == 0) {
            Script::Debug::StepIn();
        }
        if (strcmp(argv[2], "over") == 0) {
            Script::Debug::StepOver();
        }
        if (strcmp(argv[2], "out") == 0) {
            Script::Debug::StepOut();
        }

        if (strcmp(argv[2], "setbp") == 0) {
            duint addr = 0;
            if (!parseExpr(argv[3], addr)) {
                return false;
            }
            Script::Debug::SetBreakpoint(addr);
        }
        if (strcmp(argv[2], "delbp") == 0) {
            duint addr = 0;
            if (!parseExpr(argv[3], addr)) {
                return false;
            }
            Script::Debug::DeleteBreakpoint(addr);
        }
        if (strcmp(argv[2], "disbp") == 0) {
            duint addr = 0;
            if (!parseExpr(argv[3], addr)) {
                return false;
            }
            Script::Debug::DisableBreakpoint(addr);
        }
        if (strcmp(argv[2], "sethbp") == 0) {
            duint addr = 0;
            if (!parseExpr(argv[3], addr)) {
                return false;
            }
            Script::Debug::SetHardwareBreakpoint(addr, Script::Debug::HardwareType::HardwareExecute);
        }
        if (strcmp(argv[2], "delhbp") == 0) {
            duint addr = 0;
            if (!parseExpr(argv[3], addr)) {
                return false;
            }
            Script::Debug::DeleteHardwareBreakpoint(addr);
        }

    }

    if (strcmp(argv[1], "unicorn") == 0) {

        uc_engine* uc;
        uc_err err;
        err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);

        if (err != UC_ERR_OK) {
            return false;
        }

        //uc_mem_map

        //uc_mem_map(uc, , 2 * 1024 * 1024, UC_PROT_ALL);

    }

    return true;
}

// Initialize your plugin data here.
bool pluginInit(PLUG_INITSTRUCT* initStruct){
    dprintf("pluginInit(pluginHandle: %d)\n", pluginHandle);

    // Prefix of the functions to call here: _plugin_register
    _plugin_registercommand(pluginHandle, "zxc", cbExampleCommand, true);

    // Return false to cancel loading the plugin.
    return true;
}

// Deinitialize your plugin data here.
// NOTE: you are responsible for gracefully closing your GUI
// This function is not executed on the GUI thread, so you might need
// to use WaitForSingleObject or similar to wait for everything to close.
void pluginStop(){
    // Prefix of the functions to call here: _plugin_unregister

}

PLUG_EXPORT void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info){
    switch (info->hEntry){
    case MENU_HELLO:
        MessageBoxW(NULL, L"Hello World", L"Hello", MB_OK);
        break;

    default:
        break;
    }
}

// Do GUI/Menu related things here.
// This code runs on the GUI thread: GetCurrentThreadId() == GuiGetMainThreadId()
// You can get the HWND using GuiGetWindowHandle()
void pluginSetup(){
    // Prefix of the functions to call here: _plugin_menu

    _plugin_menuaddentry(hMenu, MENU_HELLO, "Hello MessageBox");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值