拓冰建站拓冰建站
首页 / 资讯中心 / 正文

x64dbg 插件自定义格式化函数开发指南:_plugin_registerformatfunction 从 API 到源码实现

逆向工程调试器开发工具应用安全【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址https://gitcode.com/gh_mirrors/x6/x64dbg点击查看免费下载x64dbg 内置的字符串格式化引擎支持{type;arg1;arg2;argNexpression}语法而插件可以通过_plugin_registerformatfunction注册自定义格式化函数format function让用户在日志、表达式、条件断点等场景直接复用自定义输出逻辑。本文以 registerformatfunction.rst 为骨架结合 stringformat.cpp、formatfunctions.cpp 等源码讲解该 API 的完整签名、参数约束、回调契约、返回值语义及底层调用链并给出可直接套用的 C/C 插件示例。一、API 概览插件如何接入格式化引擎格式化函数是 x64dbg 字符串格式化系统的扩展点。内置格式化引擎的完整语法与内置类型d、u、x、a、i、f、F、mem、winerror、ntstatus、utf8、disasm等可参考 Formatting.md。插件注册的自定义格式化函数与这些内置类型共用同一套解析与调用机制语法为{type;arg1;arg2;argNexpression}其中type是插件注册的格式化函数名argN是任意字符串参数原样传给回调expression是任意合法表达式其计算结果作为value传入回调。函数签名bool _plugin_registerformatfunction( int pluginHandle, //插件句柄 const char* type, //格式化函数名称 CBPLUGINFORMATFUNCTION cbFunction, //回调函数 void* userdata //用户数据 );返回值注册成功返回true失败返回false。配套注销 API_plugin_unregisterformatfunction(int pluginHandle, const char* type)用于注销声明与实现同样位于 src/dbg/_plugins.cpp。二、参数详解与命名约束pluginHandle调用插件的句柄插件在初始化阶段通过plugin_register获得的句柄用于身份校验。在底层实现pluginformatfuncregister中会先调用findPluginName(pluginHandle, plugName)反查插件名句柄无效时直接返回false并跳过后续注册逻辑见 src/dbg/plugin_loader.cpp。type格式化函数名称关键约束type是格式化语法中;之前的那部分名称命名规则为首字符必须是_或字母a-zA-Z或下划线后续字符只能为_、.、字母或数字名称不能为空。该规则由FormatFunctions::isValidName强制执行见 src/dbg/formatfunctions.cppbool FormatFunctions::isValidName(const String name) { if(!name.length()) return false; if(!(name[0] _ || isalpha(name[0]))) return false; for(const auto ch : name) if(!(isalnum(ch) || ch _ || ch .)) return false; return true; }从源码看type相当于格式化引擎内部注册表std::unordered_mapString, FormatFunctions::Function mFunctions的键见 formatfunctions.h。Register时若名称非法或已存在同名函数都会返回false即重复注册同一名称不会被覆盖。cbFunction回调函数回调类型定义于 src/dbg/_plugins.htypedef FORMATRESULT(*CBPLUGINFORMATFUNCTION)( char* dest, size_t destCount, int argc, char* argv[], duint value, void* userdata);参数含义dest/destCount输出缓冲区及其容量字节。回调需把格式化结果以\0结尾的字符串写入dest。argc/argv格式化语法中;分隔的参数个数与字符串数组。注意argv[0]即type本身真正的参数从argv[1]开始见下文Call实现分析。valueexpression计算出的表达式值duint即指针宽度的无符号整数。userdata注册时传入的userdata指针原样回传给回调可用于携带附加信息。userdata透传用户数据注册时的void* userdata会被存入内部Function结构formatfunctions.h回调调用时再原样传回不经过任何转换适合传入插件自定义的上下文结构体指针。三、返回值语义FORMATRESULT 四态契约回调必须返回FORMATRESULT枚举定义于 src/dbg/_plugins.h枚举值含义FORMAT_ERROR通用失败不产生任何输出消息FORMAT_SUCCESS格式化成功dest中为有效结果FORMAT_ERROR_MESSAGE格式化失败但已在缓冲区中写入错误信息缓冲区至少保证有 511 字节可用FORMAT_BUFFER_TOO_SMALL缓冲区过小x64dbg 会自动扩大缓冲区并重试FORMAT_BUFFER_TOO_SMALL是最重要的状态调用方看到该返回值后会倍增缓冲区并重新调用回调直到成功或返回其他状态。这一重试机制在 formatfunctions.cpp 的FormatFunctions::Call中实现bool FormatFunctions::Call(std::vectorchar dest, const String type, std::vectorString argv, duint value) { SHARED_ACQUIRE(LockFormatFunctions); auto found mFunctions.find(type); if(found mFunctions.end()) return false; std::vectorchar* argvn(argv.size()); for(size_t i 0; i argv.size(); i) argvn[i] (char*)argv[i].c_str(); const auto f found-second; if(dest.size() 0) dest.resize(512, \0); fuckthis: auto result f.cbFunction(dest.data(), dest.size(), int(argv.size()), argvn.data(), value, f.userdata); if(result FORMAT_BUFFER_TOO_SMALL) { dest.resize(dest.size() * 2, \0); goto fuckthis; } return result ! FORMAT_ERROR; }细节解读初始缓冲区为 512 字节FORMAT_BUFFER_TOO_SMALL时按 2 倍增长循环重试回调返回FORMAT_ERROR_MESSAGE时Call返回true错误消息会直接作为输出展示返回FORMAT_ERROR时Call返回false上层stringformat.cpp 的printComplexValue最终输出[Formatting Error]回调全程在LockFormatFunctions锁保护下执行插件回调内不应再调用可能重入格式化引擎的 API以免死锁。四、注册与注销的底层链路_plugin_registerformatfunction的完整调用链为_plugin_registerformatfunction [src/dbg/_plugins.cpp#L193-L196] └─ pluginformatfuncregister [src/dbg/plugin_loader.cpp#L1403-L1421] ├─ findPluginName(pluginHandle) //句柄校验失败即返回 false ├─ FormatFunctions::Register(type, cb, userdata) [src/dbg/formatfunctions.cpp#L275-L288] │ ├─ isValidName(type) //命名规则校验 │ └─ mFunctions[type] f; //写入全局注册表查重 ├─ gPluginFormatfunctionList.push_back(...) //记录到插件级列表 └─ dprintf([PLUGIN, %s] Format function \%s\ registered!) //日志输出注册成功后日志区会输出[PLUGIN, 插件名] Format function type registered!失败如名称非法、重复注册、句柄无效则输出failed to register...。注销链路pluginformatfuncunregisterplugin_loader.cpp会从插件级列表与全局注册表中同步移除并级联注销该函数注册过的别名RegisterAlias机制见 formatfunctions.cpp。五、格式化引擎如何调用插件函数当用户输入log {myfmt;arg1;arg2eax}时格式化引擎的处理流程位于 src/dbg/stringformat.cpp解析器扫描格式串识别{...}块遇到{{、}}时按转义输出{、}进入handleFormatString进一步拆分为类型与表达式发现;分隔的复合参数后进入printComplexValueStringUtils::Split(complexArgs, ;)按;拆分出参数列表splitvalfromstring(value, valuint)计算后的表达式得到value调用FormatFunctions::Call(dest, split[0], split, valuint)——split[0]即注册的typesplit整体作为argv传入因此回调中argv[0]是类型名argv[1]起才是用户参数成功则返回结果字符串失败返回[Formatting Error]。这也解释了命名约束的根源type作为;之前的分隔片段必须能被解析器安全识别故限定为_、.、字母、数字组合。六、完整插件示例注册一个自定义格式化函数以下示例注册一个名为_hexdump的自定义格式化函数将表达式的值按指定宽度输出为十六进制字节序列演示argv、value、destCount、FORMAT_BUFFER_TOO_SMALL的完整用法#include windows.h #include stdio.h #include pluginsdk/x64dbg.h // 提供 _plugin_registerformatfunction 等导出 // 回调将 value 按小端序输出为 width 个字节的十六进制串 static FORMATRESULT cbHexDump(char* dest, size_t destCount, int argc, char* argv[], duint value, void* userdata) { int width (argc 1) ? atoi(argv[1]) : (int)sizeof(duint); if(width 0 || width (int)sizeof(duint)) { _snprintf_s(dest, destCount, _TRUNCATE, invalid width: %d, width); return FORMAT_ERROR_MESSAGE; // 写入错误信息并报告失败 } if((size_t)(width * 2 1) destCount) return FORMAT_BUFFER_TOO_SMALL; // 请求更大缓冲区引擎会自动扩容重试 const unsigned char* bytes (const unsigned char*)value; char* p dest; for(int i 0; i width; i) p sprintf_s(p, destCount - (p - dest), %02X, bytes[i]); return FORMAT_SUCCESS; } // 插件初始化时注册 static bool pluginInit(PLUG_INITSTRUCT* initStatus) { _plugin_registerlogprintf(PLUG_LOG, Example format function loaded\n); if(!_plugin_registerformatfunction(pluginHandle, _hexdump, cbHexDump, nullptr)) { _plugin_logprintf(Failed to register format function\n); return false; } return true; } // 插件卸载时注销 static bool pluginStop() { _plugin_unregisterformatfunction(pluginHandle, _hexdump); return true; }注册后在命令栏执行log {_hexdump;4eax}若eax 0x11223344将输出44332211小端字节序。参数;4对应回调的argv[1]eax的计算结果作为value传入。七、设计注意事项命名空间冲突内置格式化函数mem、utf8、winerror、disasm等已占用注册表插件不应使用同名type否则Register返回false。建议统一使用_前缀降低冲突概率也符合命名规则。缓冲区契约不要假设destCount大小写入前务必检查剩余空间空间不足返回FORMAT_BUFFER_TOO_SMALL而非截断或越界这是避免内存问题的关键。失败语义区分需要展示错误原因时返回FORMAT_ERROR_MESSAGE并把消息写入缓冲区无需展示时返回FORMAT_ERROR最终呈现为[Formatting Error]。并发与重入回调在格式化引擎持有锁的状态下执行见 formatfunctions.cpp回调体内应避免调用DbgCmdExec、log等可能再次触发格式化或命令解析的操作。生命周期管理插件退出前务必调用_plugin_unregisterformatfunction否则卸载后残留回调指针可能导致崩溃。八、小结_plugin_registerformatfunction让插件以极低成本扩展 x64dbg 的字符串格式化能力注册一个符合命名规则的类型名与回调即可在任意支持格式化字符串的场景log命令、表达式、条件断点消息等以{type;argexpr}语法使用。其底层实现名称校验、查重、缓冲区倍增重试、插件级列表管理在 src/dbg/formatfunctions.cpp 与 src/dbg/plugin_loader.cpp 中均可直接追溯是理解 x64dbg 插件体系与格式化引擎交互的优秀范例。赞分享逆向工程调试器开发工具应用安全【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址https://gitcode.com/gh_mirrors/x6/x64dbg点击查看免费下载相关推荐LongCat-Flash-Omni-FP8核心技术解密Shortcut-connected MoE与零计算专家如何实现低延迟交互LongCat Flash Omni FP8核心技术解密Shortcut connected MoE与零计算专家如何实现低延迟交互 LongCat Flash逆向工程调试器开发工具应用安全LXGW WenKai TC核心功能解析传承字形、UVS支持与多场景应用LXGW WenKai TC核心功能解析传承字形、UVS支持与多场景应用 LXGW WenKai TC霞鹜文楷TC是一款专为繁体中文用户打造的高质量开源字WasmEdge 源码构建与插件开发指南从 CMake 配置到自定义 Host 函数WasmEdge 源码构建与插件开发指南从 CMake 配置到自定义 Host 函数 WasmEdge 是一个使用 C17 编写的轻量级、高性能、可扩展语言运行时云原生本地部署边缘计算创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门