ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

x64dbg 插件开发:深入解析 PLUG_CB_CREATEPROCESS 进程创建回调

x64dbg 插件开发:深入解析 PLUG_CB_CREATEPROCESS 进程创建回调 x64dbg 插件开发深入解析 PLUG_CB_CREATEPROCESS 进程创建回调【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbgPLUG_CB_CREATEPROCESS 是 x64dbg 插件 SDK 中最重要的调试事件回调之一它在调试目标进程被创建之后、符号与数据库初始化完成且断点已设置就绪时触发。本文结合 x64dbg 源码src/dbg/debugger.cpp、src/dbg/_plugins.h讲解该回调的触发时机、数据结构、注册方式与典型实战用法帮助读者在插件中正确捕获进程启动瞬间的关键信息进程句柄、主模块路径、入口地址等并理解其与入口断点、TLS 回调断点、系统断点等事件的确切先后关系。回调触发时机进程创建后、断点就绪前根据插件 SDK 文档docs/developers/plugins/Callbacks/plugcbcreateprocess.rst的原始定义该回调的触发时机为Called after process creation (in the debug loop), after the initialization of the symbol handler, the database file and setting breakpoints on TLS callbacks / the entry breakpoint即在调试循环debug loop中进程创建事件之后、且以下初始化工作全部完成时触发符号处理器symbol handler初始化完成数据库文件database file加载完成在 TLS 回调 / 入口断点上设置的断点已经就绪。这意味着当你的回调函数被调用时主模块已经被 x64dbg 识别并加入模块列表$hp等进程相关变量已经可用你可以安全地查询模块信息、符号信息或直接使用_plugin_logprintf等 API 输出诊断信息而不必担心初始化尚未完成。源码层面的触发链路从源码结构看该回调由 src/dbg/debugger.cpp 中的cbCreateProcess静态函数触发约 1568 行起。该函数依次完成记录活动线程句柄hActiveThread并同步到fdProcessInfo通过varset($hp, ...)设置进程句柄变量$hp通过GetFileNameFromHandle从进程文件句柄解析主模块路径失败时回退到GetFileNameFromProcessHandle并输出Process Started: %p %s日志解析并打印调试目标的命令行与argv[]参数更新内存映射与 GUI 视图MemUpdateMap、GuiUpdateMemoryView、DebugUpdateGui调用ModLoad加载主模块枚举并设置模块断点与 DLL 断点若目标为 EXE 且非附加模式根据 Events 设置TlsCallbacks、EntryBreakpoint为 TLS 回调与入口点设置一次性断点创建主线程记录并更新线程列表ThreadCreate。随后在约 1665–1696 行构造PLUG_CB_CREATEPROCESS callbackInfo并调用plugincbcall(CB_CREATEPROCESS, callbackInfo);因此插件回调返回时进程尚未真正开始执行入口断点与 TLS 回调断点还未命中这是做进程级预处理的最佳窗口。数据结构详解PLUG_CB_CREATEPROCESS结构体定义于 src/dbg/_plugins.h约 89–100 行typedef struct { CREATE_PROCESS_DEBUG_INFO* CreateProcessInfo; IMAGEHLP_MODULE64* modInfo; const char* DebugFileName; PROCESS_INFORMATION* fdProcessInfo; } PLUG_CB_CREATEPROCESS;成员类型说明CreateProcessInfoCREATE_PROCESS_DEBUG_INFO*来自 Windows 调试事件CREATE_PROCESS_DEBUG_EVENT的原始信息包含hFile映像文件句柄、hProcess、hThread、lpBaseOfImage映像基址、lpStartAddress入口点与lpThreadLocalBasemodInfoIMAGEHLP_MODULE64*主模块被调试的 EXE/DLL的模块信息BaseOfImage指向映像基址ModuleName/ImageName为模块名DebugFileNameconst char*主模块的完整路径UTF-8 编码即日志中Process Started打印的路径fdProcessInfoPROCESS_INFORMATION*与_plugin_startdebug返回值一致的进程信息结构包含hProcess与hThread句柄关于 modInfo 的源码细节值得注意的是在 src/dbg/debugger.cpp 的 1667–1693 行x64dbg 手工构造了一个IMAGEHLP_MODULE64骨架结构传给插件IMAGEHLP_MODULE64 modInfoUtf8; memset(modInfoUtf8, 0, sizeof(modInfoUtf8)); modInfoUtf8.SizeOfStruct sizeof(modInfoUtf8); modInfoUtf8.BaseOfImage base; modInfoUtf8.ImageSize 0; modInfoUtf8.NumSyms 1; modInfoUtf8.SymType SymDia; strncpy_s(modInfoUtf8.ModuleName, DebugFileName, _TRUNCATE); strncpy_s(modInfoUtf8.ImageName, DebugFileName, _TRUNCATE); ... callbackInfo.modInfo modInfoUtf8;从源码可以推断该时刻完整的符号加载信息尚不可用ImageSize、TimeDateStamp、CheckSum等字段被置零SymType被预设为SymDia模块名与映像名直接取自DebugFileName。因此不要在回调内依赖modInfo-ImageSize、LoadedPdbName等尚未填充的字段如需精确的模块尺寸与符号信息应等待后续的CB_SYSTEMBREAKPOINT或CB_LOADDLL回调或通过_plugin_getmoduleinfo等 API 主动查询。注册回调的三种方式回调函数类型为typedef void (*CBPLUGIN)(CBTYPE cbType, void* callbackInfo);其中CBTYPE枚举中对应的事件类型为CB_CREATEPROCESS见 src/dbg/_plugins.h 约 357 行。方式一_plugin_registercallback动态注册在pluginit中通过_plugin_registercallback注册签名详见 docs/developers/plugins/API/registercallback.rstextern C __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct) { initStruct-pluginVersion 1; initStruct-sdkVersion PLUG_SDKVERSION; strcpy_s(initStruct-pluginName, MyProcessWatcher); _plugin_registercallback(initStruct-pluginHandle, CB_CREATEPROCESS, cbCreateProcess); return true; }方式二导出命名回调函数根据插件开发基础文档docs/developers/plugins/basics.md也可以不调用注册 API而是直接导出一个名为CBCREATEPROCESS的CDECL导出函数导出名不含下划线x64dbg 会自动将其注册为CB_CREATEPROCESS事件的回调extern C __declspec(dllexport) void CBCREATEPROCESS(CBTYPE cbType, PLUG_CB_CREATEPROCESS* info) { // 处理进程创建事件 }方式三CBALLEVENTS全事件导出导出名为CBALLEVENTS的函数会接收所有事件的回调在具名导出注册之前生效此时需通过cbType参数区分事件类型extern C __declspec(dllexport) void CBALLEVENTS(CBTYPE cbType, void* callbackInfo) { if(cbType CB_CREATEPROCESS) { auto info (PLUG_CB_CREATEPROCESS*)callbackInfo; // ... } }分发机制plugincbcall从源码实现看事件分发由 src/dbg/plugin_loader.cpp 的plugincbcall完成约 828–837 行。它先将回调列表复制一份以保证线程安全再逐个调用每个插件注册的回调void plugincbcall(CBTYPE cbType, void* callbackInfo) { if(gPluginCallbackList[cbType].empty()) return; SHARED_ACQUIRE(LockPluginCallbackList); auto cbList gPluginCallbackList[cbType]; //copy for thread-safety reasons SHARED_RELEASE(); for(const auto currentCallback : cbList) currentCallback.cbPlugin(cbType, callbackInfo); }若没有任何插件注册该事件分发会直接返回开销极小。完整示例进程启动信息采集插件下面给出一个可直接嵌入插件项目的完整回调实现用于在进程创建时记录关键信息到 x64dbg 日志#include _plugins.h #include windows.h #include dbghelp.h static void cbCreateProcess(CBTYPE cbType, PLUG_CB_CREATEPROCESS* info) { if(!info || !info-CreateProcessInfo) return; // 1. 主模块路径UTF-8 if(info-DebugFileName) _plugin_logprintf(Process image: %s\n, info-DebugFileName); // 2. 映像基址与入口点 auto base (duint)info-CreateProcessInfo-lpBaseOfImage; auto entry (duint)info-CreateProcessInfo-lpStartAddress; _plugin_logprintf(Image base : %p\n, base); _plugin_logprintf(Entry point: %p\n, entry); // 3. 句柄与 $hp 变量一致 if(info-fdProcessInfo) { _plugin_logprintf(hProcess : %p\n, info-fdProcessInfo-hProcess); _plugin_logprintf(hThread : %p\n, info-fdProcessInfo-hThread); } // 4. modInfo 中的模块名注意此时 ImageSize 等字段尚未填充 if(info-modInfo) _plugin_logprintf(Module name: %s\n, info-modInfo-ModuleName); }注册方式任选其一// 在 pluginit 中 _plugin_registercallback(initStruct-pluginHandle, CB_CREATEPROCESS, cbCreateProcess); // 或在 plugstop 中注销 _plugin_unregistercallback(initStruct-pluginHandle, CB_CREATEPROCESS);运行效果在 x64dbg 中打开任意程序或附加进程后日志窗口会在Process Started: base path与命令行输出之后、入口断点命中之前打印上述自定义信息。典型实战场景1. 恶意软件分析中的启动快照该回调是分析加壳程序、反调试样本的关键锚点此时映像尚未执行任何指令插件可以在lpBaseOfImage处做一次性内存快照对比后续内存修改、记录原始入口点lpStartAddress为脱壳或代码还原提供基线数据。2. 自动化调试脚本的进程编排插件可在回调内读取DebugFileName与命令行x64dbg 在cbCreateProcess中已打印出argv[]据此决定后续策略例如仅当调试目标是特定程序时才通过_plugin_debugpause暂停、或通过cmddirectexec注入预设断点命令。由于入口断点尚未命中此时追加的断点会与内置入口断点一同生效。3. 集成第三方分析工具链在回调内将hProcess、映像基址、模块路径转发给外部分析库如符号解析、PE 解析工具实现在进程启动瞬间完成对主模块的离线分析。注意应避免在回调内做耗时操作如需长时间处理应把数据拷贝到自有内存后交给独立线程执行。回调时序与相邻事件的关系结合 src/dbg/debugger.cpp 中plugincbcall的调用位置约 3223–3265 行为CB_INITDEBUG/CB_ATTACH/CB_STOPDEBUG可以推断出启动阶段各回调的典型顺序CB_INITDEBUG调试会话初始化传入文件路径CB_ATTACH仅附加模式附加开始前触发CB_CREATEPROCESS本文主题进程创建完成、符号/数据库/TLS与入口断点就绪CB_LOADDLL系统 DLL 逐个加载CB_SYSTEMBREAKPOINT系统断点命中调试器完全就绪CB_BREAKPOINT入口断点/TLS 回调断点命中首次暂停。因此若插件需要在进程真正开始执行前完成初始化CB_CREATEPROCESS是最后一个安全窗口若需要等目标完全就绪再操作则应使用CB_SYSTEMBREAKPOINT。使用注意事项与最佳实践插件 SDK 总览docs/developers/plugins/Callbacks/index.rst对本类回调提出了明确约束同样适用于CB_CREATEPROCESS指针作用域限制callbackInfo以及其中的所有指针CreateProcessInfo、modInfo、DebugFileName、fdProcessInfo仅在回调函数执行期间有效指向的是调试器内部内存如需跨回调保存必须自行拷贝如将字符串复制到自己的缓冲区句柄可复制值。callbackInfo永不为 NULL但成员可能为 NULL例如modInfo、DebugFileName在极端情况下可能为空使用前应判空避免直接解引用导致插件崩溃。避免耗时操作回调运行在调试器主循环线程中阻塞会拖慢整个调试会话。耗时任务应拷贝数据后交由独立线程处理x64dbg 自带TaskThread/Threading基础设施可供参考。不要修改回调参数指向的内存这些结构由调试器提供修改其内容会影响调试器内部状态且对插件而言无意义。注意结构与对齐SDK 头文件通过#pragma pack(push, 16)x64/#pragma pack(push, 8)x86强制对齐见 src/dbg/_plugins.h 约 18–23 行插件项目应直接包含该头文件以获得正确布局避免手工重定义结构体。参考资料回调结构定义src/dbg/_plugins.h回调枚举CB_CREATEPROCESSsrc/dbg/_plugins.h触发点cbCreateProcess与plugincbcall(CB_CREATEPROCESS, ...)src/dbg/debugger.cpp回调分发机制plugincbcallsrc/dbg/plugin_loader.cpp注册函数文档docs/developers/plugins/API/registercallback.rst回调使用总则docs/developers/plugins/Callbacks/index.rst插件导出规范docs/developers/plugins/basics.md【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表