x64dbg 插件开发指南:GuiDumpAt 与 GuiDumpAtN 详解(定位 Dump 窗口显示地址)
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
GuiDumpAt是 x64dbg 桥接(bridge)层提供给插件开发者的一对核心 GUI 控制函数,作用是将 Dump(内存转储)窗口的显示位置切换到指定的虚拟地址,让调试器立即在该地址处展示内存内容。本文以 GuiDumpAt.md 文档为骨架,深入讲解函数签名、参数语义、返回值约定,并结合仓库中src/bridge/bridgemain.cpp、src/gui/Src/Bridge/Bridge.cpp与src/dbg/commands/cmd-gui.cpp的源码实现,剖析其从插件调用到 GUI 刷新的完整调用链。读完本文,你将掌握如何在自己的插件中让 Dump 窗口跳转到指定地址、如何精准控制第 N 个 Dump 标签页,以及与之配套的dump命令背后的实现原理。
一、函数总览:把 Dump 窗口"指到"指定地址
在 x64dbg 的 CPU 视图中,Dump 窗口用于以十六进制字节流的形式查看任意内存区域,是逆向分析与恶意软件样本分析时最常用的窗口之一。插件开发者经常需要在定位到某个数据结构、堆块或缓冲区后,把 Dump 窗口同步跳转过去,此时就需要调用GuiDumpAt系列函数。
文档给出的核心声明如下:
void GuiDumpAt(duint va);va:要显示的虚拟地址(Virtual Address),即 Dump 窗口将要跳转到的目标地址。- 返回值:
void,该函数不返回任何值,调用后无需检查失败标志。
从 bridgemain.h 可以看到,该函数以BRIDGE_IMPEXP导出,属于 x64dbg 桥接层的公开 API,插件在包含bridgemain.h后即可直接链接调用。
与其成对出现的还有针对指定 Dump 标签页的变体 GuiDumpAtN.md:
void GuiDumpAtN(duint va, int index);va:目标虚拟地址。index:Dump 窗口的索引号,指定将地址应用到第几个 Dump 标签页。
两者的关系是:GuiDumpAt作用于"当前活动"的 Dump 窗口,而GuiDumpAtN可以精确控制多个 Dump 标签页中的任意一个——x64dbg 允许同时打开多个 Dump 标签页,分别盯住不同的内存区域,GuiDumpAtN让插件可以逐一管理它们。
二、参数语义与使用注意事项
2.1va的取值:虚拟地址而非物理地址
duint是 x64dbg 定义的与目标架构字长一致的整数类型(32 位构建下为 32 位,64 位构建下为 64 位),因此va应传入进程的虚拟地址空间中的地址,例如模块基址、堆地址或表达式计算出的结果。该地址不要求一定可读——Dump 窗口只是切换到该地址进行展示,是否有效取决于被调试进程的实际内存布局。
2.2index的取值:从 0 开始的 Dump 标签页序号
在GuiDumpAtN中,index对应 Dump 窗口标签页的序号。在桥接层实现中,这个整型值会被转换为指针宽度后随消息传递(见下文源码分析),因此从插件侧传 0 表示第一个 Dump 标签页,1 表示第二个,依此类推。
2.3 返回值:无返回、无错误码
两个函数均返回void,这意味着调用方无法通过返回值判断地址是否合法或窗口是否存在。设计上它们是"尽力而为"的 GUI 指示消息:桥接层只负责把请求投递给 GUI 线程,地址合法性验证与展示效果由 GUI 侧处理。如果插件需要确认跳转是否成功,可以配合MemIsValidReadPtr(见 memory.cpp)等内存查询接口先行校验。
三、源码级剖析:一条消息走完的完整调用链
3.1 桥接层:_gui_sendmessage投递 GUI 消息
在 bridgemain.cpp 中,GuiDumpAt的实现极为简洁:
BRIDGE_IMPEXP void GuiDumpAt(duint va) { _gui_sendmessage(GUI_DUMP_AT, (void*)va, 0); }而 GuiDumpAtN 则是:
BRIDGE_IMPEXP void GuiDumpAtN(duint va, int index) { _gui_sendmessage(GUI_DUMP_AT_N, (void*)va, (void*)(duint)index); }可以看到,二者都通过_gui_sendmessage将请求封装为一条 GUI 消息:va作为param1,index作为param2原样传递。这种"桥接消息"机制是 x64dbg 的核心架构——调试器核心(dbg 模块)与 GUI(gui 模块)运行在不同的上下文中,所有跨模块交互都通过 bridgemain.h 定义的消息常量与_gui_sendmessage完成,从而保证线程安全与解耦。
从代码结构可以推断,GuiDumpAt的消息GUI_DUMP_AT只携带一个参数,GUI 侧收到后自然作用于"当前活动"的 Dump 窗口;而GUI_DUMP_AT_N携带两个参数,可精确定位到某个 Dump 标签页。
3.2 GUI 侧:Bridge 对象转发为 Qt 信号
GUI 进程中的消息分发位于 Bridge.cpp:
case GUI_DUMP_AT: emit dumpAt((dsint)param1); break;以及:
case GUI_DUMP_AT_N: emit dumpAtN((duint)param1, (int)(duint)param2); break;(对应源码位置见 Bridge.cpp 与 Bridge.cpp。)
GUI 侧把桥接消息进一步转换为 Qt 信号dumpAt/dumpAtN,由 CPU 视图中的 Dump 控件槽函数接收,最终调用底层的数据模型与视图刷新逻辑,将 Dump 窗口滚动到va地址并重新渲染字节内容。这印证了文档中"show the dump at this address"(在该地址展示 Dump)的描述:整条链路是插件/命令 → 桥接消息 → Qt 信号 → Dump 控件刷新的单向请求流。
3.3 命令层:内置dump命令如何调用它们
GuiDumpAt系列不仅服务插件,也被 x64dbg 自身的命令系统复用。在 cmd-gui.cpp 中,dump命令的实现展示了两个函数的分工:
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 <addr> [index]的分流逻辑:
- 参数解析:第一个参数必须是可被
valfromstring解析的表达式(支持寄存器、变量、算术表达式等,详见 表达式文档),解析失败会输出 "Invalid address" 并返回false。 - 显式指定索引:若提供了第二个参数(
argc > 2),则调用GuiDumpAtN(addr, index)跳转到指定序号(index)的 Dump 标签页。 - 智能推断当前 Dump:若未提供索引,命令会通过
GuiGetActiveView获取当前活动视图,尝试从其标题解析出"Dump %d"形式的序号——如果当前活动视图恰好是某个 Dump 标签页,就对该标签页调用GuiDumpAtN,保持"就地切换";否则退化为调用GuiDumpAt作用于活动 Dump 窗口。 - 收尾动作:
GuiShowCpu()确保 CPU 视图可见,GuiFocusView(GUI_DUMP)把焦点交给 Dump 窗口,让用户立即看到切换结果。
这个实现为插件开发者提供了很好的参考范式:优先用GuiDumpAtN精确控制目标标签页,仅在无法确定索引时使用GuiDumpAt。同时它也说明,插件完全可以通过组合GuiGetActiveView、GuiShowCpu、GuiFocusView等桥接 API 复刻命令行的完整交互体验。
四、典型插件应用场景
4.1 场景一:点击反汇编行跳转 Dump
插件可以监听反汇编视图的选中事件,当用户选中一条指令时,若操作数是内存引用,插件解析出目标地址后调用GuiDumpAt(addr),让 Dump 窗口立刻跟随显示该操作数指向的内存,实现"点击即转储"的联动体验。
4.2 场景二:多标签页分工监视
在分析堆内存或跟踪多个缓冲区时,插件可预先打开多个 Dump 标签页,然后针对每个标签页调用GuiDumpAtN(va, i),例如GuiDumpAtN(heapBase, 0)显示堆头、GuiDumpAtN(buf1, 1)显示输入缓冲、GuiDumpAtN(buf2, 2)显示输出缓冲,形成固定的"多路监视面板"。
4.3 场景三:自定义命令包装
插件注册自己的命令时,可以在解析完表达式后直接复用GuiDumpAt,例如实现一个mydump <expr>命令,先通过内存有效性检查再跳转,比直接调用内置dump多一层防护。需要注意的是,valfromstring等表达式解析函数位于 dbg 模块(value.cpp),插件需按 插件 API 文档 的规范正确初始化后才能调用。
五、与相关函数的配合使用
在 x64dbg 的 GUI 桥接 API 家族中,GuiDumpAt不是孤立存在的,它与以下函数经常协同工作:
- GuiDumpAtN:定位到第 N 个 Dump 标签页,本文已详述。
- GuiShowCpu / GuiFocusView:让 CPU 视图(含 Dump 子窗口)可见并获得焦点,保证跳转结果被用户看到。
- GuiGetActiveView:查询当前活动视图及其标题,用于判断当前位于哪个 Dump 标签页(
dump命令正是利用它做智能分流)。 - GuiSelectionSet / GuiSelectionGet:读写各视图的选中范围,配合
GuiDumpAt可以做到"选中即跳转"。
需要特别强调的是,GuiDumpAt与 Dump 窗口中"跟随(follow)"操作的区别:前者是编程式跳转,由插件或命令主动发起;而窗口内部的跟随、同步滚动等行为由 GUI 自身逻辑维护。插件若需要实现持续的地址联动,应在合适的时机重复调用GuiDumpAt(例如响应调试事件回调),而非依赖 GUI 的自动同步。
六、小结
| 维度 | GuiDumpAt | GuiDumpAtN |
|---|---|---|
| 声明 | void GuiDumpAt(duint va) | void GuiDumpAtN(duint va, int index) |
| 作用目标 | 当前活动 Dump 窗口 | 指定序号(index)的 Dump 标签页 |
| 返回 | 无 | 无 |
| 桥接消息 | GUI_DUMP_AT | GUI_DUMP_AT_N |
| 典型调用方 | 插件、dump命令(无索引分支) | 插件、dump命令(显式索引分支) |
GuiDumpAt系列是 x64dbg 插件体系中最常用也最直观的 GUI 控制入口之一:插件只需一行调用即可把 Dump 窗口引导到任意虚拟地址,而桥接层(bridgemain.cpp)、GUI 信号层(Bridge.cpp)与命令层(cmd-gui.cpp)的三级实现又为理解 x64dbg "dbg 与 GUI 分离、消息驱动"的架构提供了一个绝佳的入门样例。在自己的插件中,建议遵循dump命令的成熟模式:能确定 Dump 索引就用GuiDumpAtN,否则退化为GuiDumpAt,再配合GuiShowCpu与GuiFocusView完善交互闭环。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考