x64dbg 插件开发指南:使用 GuiDumpAtN 将第 N 个内存转储窗口定位到指定虚拟地址
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
GuiDumpAtN是 x64dbg 桥接(Bridge)层提供给插件与脚本的 GUI 控制函数之一,用于将指定的第 N 个内存转储(Dump)窗口跳转到给定的虚拟地址va,从而在该窗口中展示该地址处的内存数据。它与单窗口版本的 GuiDumpAt 的区别在于:多了一个index参数,可精确控制目标 Dump 标签页。读完本文,你将掌握该函数的完整调用契约、在桥接层与 Qt GUI 层的完整消息传递链路,以及它在dump命令中的实际用法,从而在自己的插件中实现"把数据定位到指定 Dump 窗口"的能力。
函数签名与核心语义
原文档给出的函数原型如下:
void GuiDumpAtN(duint va, int index)参数说明
| 参数 | 类型 | 含义 |
|---|---|---|
va | duint | 要转储(dump)的目标虚拟地址。duint是 x64dbg 定义的无符号整数类型,在 32 位构建下为 32 位、64 位构建下为 64 位,因此该函数在 x32dbg 与 x64dbg 中均可正常工作 |
index | int | 目标内存转储窗口的索引编号,即 CPU 视图标签页中 "Dump N" 的N |
返回值
该函数不返回任何值(void),调用方无法通过返回值获知目标窗口是否存在或操作是否成功;若需要确认结果,只能结合后续的读取操作或日志自行判断。
与 GuiDumpAt 的分工
- GuiDumpAt:不带索引,把当前活动的Dump 窗口定位到
va; GuiDumpAtN:带索引index,把指定的第 N 个 Dump 窗口定位到va,并会将该标签页切换为当前页。
调用示例
原文档的示例为占位性质,这里给出可直接编译使用的完整示例。插件在获得PLUG_EXPORT导出的插件实例后,即可在任何上下文(包括调试回调中)调用:
#include "bridgemain.h" // 将第 2 个 Dump 窗口定位到当前 EIP/RIP 指向的地址 void DemoDumpAtCurrentIp() { duint ip = 0; // 获取当前活动线程的程序计数器(x32dbg 中为 EIP,x64dbg 中为 RIP) if(DbgGetRegDumpEx("cip", &ip, sizeof(ip))) { GuiDumpAtN(ip, 2); // 第 2 个 Dump 窗口跳转到当前指令地址 GuiShowCpu(); // 确保 CPU 视图可见 GuiFocusView(GUI_DUMP); // 聚焦到 Dump 视图 } }va既可以是具体的绝对虚拟地址,也可以来自表达式计算结果(如DbgEval返回的模块基址、变量值等)。因为duint是原生指针宽度,向该函数传递任何合法用户态地址都是安全的。
桥接层实现:一条 GUI 控制消息的诞生
GuiDumpAtN的声明位于桥接头文件 src/bridge/bridgemain.h,实现位于 src/bridge/bridgemain.cpp:
BRIDGE_IMPEXP void GuiDumpAtN(duint va, int index) { _gui_sendmessage(GUI_DUMP_AT_N, (void*)va, (void*)(duint)index); }可以看到,这个函数本身并不直接操作任何窗口控件,而是把请求封装成一条GUI 消息:消息类型为GUI_DUMP_AT_N,param1携带虚拟地址va,param2携带窗口索引index,通过_gui_sendmessage异步投递给 GUI 线程。这正是 x64dbg 调试器核心(dbg 模块)与 GUI(Qt 界面)解耦的典型模式:dbg 侧的任何线程都能安全地发起 GUI 操作,而不必关心 Qt 控件与线程亲和性。
消息 ID 与参数类型的对应关系定义在桥接宏中(src/bridge/bridgemain.h):
msg(GUI_DUMP_AT_N, int index, duint va)该宏声明了消息槽的参数形态,并在桥接层负责把(duint va, int index)的调用参数与消息的两个void*载荷互相转换。
GUI 侧处理:从信号到 Dump 标签页的完整链路
消息到达 GUI 线程后,由 Qt 侧的桥接对象接收。在 src/gui/Src/Bridge/Bridge.cpp 中:
case GUI_DUMP_AT_N: emit dumpAtN((duint)param1, (int)(duint)param2); break;Bridge类将消息转换为 Qt 信号dumpAtN(duint va, int index)(信号声明见 src/gui/Src/Bridge/Bridge.h)。该信号在 CPU 多转储视图组件中被连接(src/gui/Src/Gui/CPUMultiDump.cpp):
connect(Bridge::getBridge(), SIGNAL(dumpAtN(duint, int)), this, SLOT(printDumpAtNSlot(duint, int)));最终的槽函数printDumpAtNSlot(src/gui/Src/Gui/CPUMultiDump.cpp)完成了窗口定位动作:
void CPUMultiDump::printDumpAtNSlot(duint va, int index) { int tabindex = GetDumpWindowIndex(index); if(tabindex == 2147483647) return; CPUDump* current = qobject_cast<CPUDump*>(widget(tabindex)); if(!current) return; setCurrentIndex(tabindex); current->printDumpAt(va); current->mHistory.addVaToHistory(va); }其关键行为可以归纳为四点:
- 按名称定位标签页:
GetDumpWindowIndex(index)(src/gui/Src/Gui/CPUMultiDump.cpp)遍历标签页,寻找 native name 恰好等于"Dump " + index的页签。这说明index语义上与界面标签页标题 "Dump 1"、"Dump 2" 中的数字一一对应; - 静默失败:若找不到对应窗口(返回
2147483647,即INT_MAX哨兵值),或对应页签不是CPUDump类型,函数直接返回,不做任何 UI 变更,也不会报错; - 切换当前页:
setCurrentIndex(tabindex)会先把目标 Dump 标签页设为当前显示页,因此调用后用户会直接看到该窗口被激活; - 刷新与历史记录:
current->printDumpAt(va)让该窗口滚动到新地址并重新渲染内存字节,mHistory.addVaToHistory(va)则把该地址记入窗口的导航历史,用户可以用回退/前进功能在地址之间往返。
实际入口:dump命令如何复用 GuiDumpAtN
GuiDumpAtN不仅是插件 API,也是内置命令dump的底层实现之一。在 src/dbg/commands/cmd-gui.cpp 的cbDebugDump中:
bool cbDebugDump(int argc, char* argv[]) { if(IsArgumentsLessThan(argc, 2)) return false; duint addr = 0; if(!valfromstring(argv[1], &addr)) { dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid address \"%s\"!\n"), argv[1]); return false; } if(argc > 2) { duint index = 0; if(!valfromstring(argv[2], &index)) { dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid address \"%s\"!\n"), argv[2]); return false; } GuiDumpAtN(addr, int(index)); } else { ACTIVEVIEW activeView; GuiGetActiveView(&activeView); int dumpIndex; if(sscanf_s(activeView.title, "Dump %d", &dumpIndex) == 1) GuiDumpAtN(addr, dumpIndex); else GuiDumpAt(addr); } GuiShowCpu(); GuiFocusView(GUI_DUMP); return true; }由此可以梳理出dump命令(命令文档)的两种分支行为:
- 带第三个参数:
dump <addr> <index>直接把第index个 Dump 窗口定位到addr,内部调用GuiDumpAtN; - 不带第三个参数:先读取当前活动视图,若活动视图标题形如
"Dump N"(即当前正停留在某个 Dump 标签页),则把该窗口编号提取出来交给GuiDumpAtN,从而在当前Dump 窗口中定位;否则回退到GuiDumpAt。
addr与index都通过valfromstring解析,因此地址支持 x64dbg 表达式语法(如module.entry、寄存器名、变量等),index也接受表达式计算结果。
与其他相关 API 的配合
在实际插件场景中,GuiDumpAtN常与以下桥接函数配合使用,形成完整的"定位并聚焦"体验:
- GuiDumpAt:无索引版本,作用于当前 Dump 窗口,适合只需操作一个转储视图的场景;
GuiShowCpu():确保 CPU 视图(含 Dump 标签页)处于可见状态;GuiFocusView(GUI_DUMP):把焦点切换到 Dump 视图,GUI_DUMP常量在桥接头文件中定义。
一个实用的组合思路:插件在分析堆、对象或字符串时,先通过DbgMemFindBaseAddr等内存 API 拿到数据所在页基址,再用GuiDumpAtN(base, 1)把主 Dump 窗口定位到该页,配合GuiFocusView(GUI_DUMP)让用户立刻看到数据 —— 整个过程与dump命令的内部路径完全一致,确保插件行为与内置命令保持统一。
注意事项与适用前提
- 索引从 1 开始:Dump 标签页的命名是 "Dump 1"、"Dump 2" 等,
index应传入对应数字;传入不存在或未打开的索引时,GUI 侧会静默返回,不会产生错误提示,插件侧应避免依赖返回值; - 线程安全:
GuiDumpAtN通过桥接消息机制跨线程投递,可从调试回调(如插件命令、断点回调)中直接调用,无需手动切到 GUI 线程; - 32/64 位一致性:
duint按构建位数自适应,无需在插件中区分 x32dbg 与 x64dbg; - 行为可预期:调用后目标窗口不仅刷新内容,还会被设为当前标签页并记录导航历史,这一点与用户手动在 GUI 中操作 Dump 窗口的体验一致。
如需了解桥接函数的完整清单,可参阅 开发文档索引 与 GuiDumpAt 的互链关系。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考