资讯动态

x64dbg 一次性断点控制命令 SetBreakpointSingleshoot 详解:语法、实现原理与实战

发布时间:2026/9/19 10:27:30 来源:尧图企业网站定制
x64dbg 一次性断点控制命令 SetBreakpointSingleshoot 详解语法、实现原理与实战【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg本文基于 x64dbg 源码仓库中的命令参考文档 SetBreakpointSingleshoot.md 展开深入讲解 x64dbg 中一次性断点singleshoot breakpoint的设置命令它能让一个软件断点在首次命中后被自动删除适用于只需触发一次的调试场景如定位入口点、验证某个 API 只被调用一次。读完本文你将掌握该命令的完整语法、参数语义、在断点命中流程中的底层删除逻辑以及它与其他断点类型变体命令的对应关系可以直接在命令行或脚本中熟练使用。命令概述什么是 singleshoot 断点在 x64dbg 中断点对象BREAKPOINT内部维护了一个singleshoot布尔标志。正如命令参考文档所描述的Sets the singleshoot flag of a software breakpoint. If this flag is set the breakpoint will be removed on the first hit.当该标志被置位时断点会在第一次命中后自动从断点列表中移除后续再次执行到同一地址不会再停下来。这种一次性行为在逆向分析中非常实用例如在程序入口点EntryPoint设置断点命中后自动清理避免后续流程重复触发验证某个 API 是否只被调用一次命中即自动删除防止干扰后续分析在自动化脚本中临时插桩命中一次后自动恢复现场。x64dbg 的断点系统支持多种类型软件断点BPNORMAL、硬件断点BPHARDWARE、内存断点BPMEMORY、DLL 断点BPDLL和异常断点BPEXCEPTIONsingleshoot标志对它们同样适用。本文聚焦的SetBreakpointSingleshoot命令针对的是软件断点其余类型的变体命令见下文扩展其他断点类型的 singleshoot 变体一节。命令语法与参数命令参考文档给出了完整的参数定义SetBreakpointSingleshoot arg1 [, arg2]参数含义说明arg1断点地址目标软件断点的地址支持 x64dbg 表达式语法如模块符号、寄存器、模块名偏移等[arg2]singleshoot 标志可选。为0默认值时关闭 singleshoot非0时启用 singleshoot行为要点继承自原文档并对照源码验证arg2可省略省略时默认启用 singleshoot。这一点在源码中有直接体现见下文cbDebugSetBPXSingleshootCommon中auto singleshoot true;的初始化逻辑。arg2为0时禁用此时断点恢复为普通持续断点每次命中都会暂停。arg2非0如1时启用断点首次命中后自动删除。结果变量该命令不设置任何结果变量$result等不受影响判定成功与否需观察命令输出或返回值。命令行使用示例以下示例可在 x64dbg 命令栏或脚本中通过cmddirectexec/DbgCmdExec直接执行// 先设置一个普通软件断点 SetBPX 00401000 // 将该断点切换为一次性断点命中后自动删除 SetBreakpointSingleshoot 00401000 // 显式指定启用非 0 值 SetBreakpointSingleshoot 00401000, 1 // 关闭一次性恢复为普通断点 SetBreakpointSingleshoot 00401000, 0使用 API 符号作为地址同样可行SetBPX CreateFileW SetBreakpointSingleshoot CreateFileW, 1设置完成后可以通过 bplist 命令查看断点状态。对照源码cbBreakpointListcmd-breakpoint-control.cpp可知软件断点在列表中的类型标记会显示为SSsingleshoot而非普通的BP1:SS:00401000其中1表示断点处于启用状态SS即 singleshoot 断点00401000为断点地址。源码级实现解析命令注册与参数处理SetBreakpointSingleshoot命令在调试器初始化时注册于 x64dbg.cppdbgcmdnew(SetBreakpointSingleshoot, cbDebugSetBPXSingleshoot, true); //set breakpoint singleshoot其回调cbDebugSetBPXSingleshoot最终进入通用的cbDebugSetBPXSingleshootCommoncmd-conditional-breakpoint-control.cpp关键流程如下static bool cbDebugSetBPXSingleshootCommon(BP_TYPE Type, int argc, char* argv[]) { BREAKPOINT bp; if(IsArgumentsLessThan(argc, 2)) return false; auto singleshoot true; // 省略 arg2 时默认启用 if(argc 2) { duint value; if(!valfromstring(argv[2], value, false)) return false; // arg2 无法解析为数值则失败 singleshoot value ! 0; // 非 0 即启用 } if(!BpGetAny(Type, argv[1], bp)) { dprintf(QT_TRANSLATE_NOOP(DBG, No such breakpoint \%s\\n), argv[1]); return false; } if(!BpSetSingleshoot(bp.addr, Type, singleshoot)) { dprintf(QT_TRANSLATE_NOOP(DBG, Cant set singleshoot on breakpoint \%s\\n), argv[1]); return false; } DebugUpdateBreakpointsViewAsync(); return true; }从实现可以确认三点事实参数不足时失败缺少arg1时命令直接返回false默认启用arg2省略时singleshoot初始化为true与文档0为默认的表述并不矛盾——文档强调的是显式传0即禁用目标必须存在命令执行前先通过BpGetAny按地址查找断点若指定地址没有已设置的软件断点会输出No such breakpoint并返回失败。因此该命令只能修改已存在的断点不能凭空创建。底层状态更新BpSetSingleshoot真正的状态写入发生在 breakpoint.cpp 的BpSetSingleshoot中bool BpSetSingleshoot(duint Address, BP_TYPE Type, bool singleshoot) { ASSERT_DEBUGGING(Command function call); EXCLUSIVE_ACQUIRE(LockBreakpoints); BREAKPOINT* bpInfo BpInfoFromAddr(Type, Address); if(!bpInfo) return false; bpInfo-singleshoot singleshoot; // Update singleshoot information in TitanEngine switch(Type) { case BPNORMAL: bpInfo-titantype (bpInfo-titantype ~UE_SINGLESHOOT) | (singleshoot ? UE_SINGLESHOOT : 0); if(IsBPXEnabled(Address) bpInfo-enabled) { if(!DeleteBPX(Address)) dprintf(QT_TRANSLATE_NOOP(DBG, Delete breakpoint failed (DeleteBPX): %p\n), Address); if(!SetBPX(Address, bpInfo-titantype, cbUserBreakpoint)) dprintf(QT_TRANSLATE_NOOP(DBG, Error setting breakpoint at %p! (SetBPX)\n), Address); } break; case BPMEMORY: if(bpInfo-enabled) { if(!RemoveMemoryBPX(Address, bpInfo-memsize)) dprintf(QT_TRANSLATE_NOOP(DBG, Delete memory breakpoint failed (RemoveMemoryBPX): %p\n), Address); if(!SetMemoryBPXEx(Address, bpInfo-memsize, (TitanMemoryBreakpointType)bpInfo-titantype, !singleshoot, cbMemoryBreakpoint)) dprintf(QT_TRANSLATE_NOOP(DBG, Could not enable memory breakpoint %p (SetMemoryBPXEx)\n), Address); } break; default: break; } return true; }这里揭示了 singleshoot 的底层实现本质x64dbg 的软件断点由底层调试引擎 TitanEngine 负责实际执行通过SetBPX/DeleteBPX写INT3指令因此修改标志时需要通过位操作更新titantype中的UE_SINGLESHOOT标志位并重新下发断点先DeleteBPX再SetBPX使引擎感知变化。对于内存断点则通过SetMemoryBPXEx的!singleshoot参数告知引擎该内存断点是否为一次性。命中时的自动删除handleBreakConditionsingleshoot 断点首次命中即删除的行为实现在调试器的断点命中处理函数handleBreakConditiondebugger.cppstatic void handleBreakCondition(const BREAKPOINT bp, const void* ExceptionAddress, duint CIP, bool forceSilent) { if(bp.singleshoot) { BpDelete(bp.addr, bp.type); if(bp.type BPHARDWARE) // Remove this singleshoot hardware breakpoint { if(TITANDRXVALID(bp.titantype) !DeleteHardwareBreakPoint(TITANGETDRX(bp.titantype))) dprintf(QT_TRANSLATE_NOOP(DBG, Delete hardware breakpoint failed: %p (DeleteHardwareBreakPoint)\n), bp.addr); } } if(!forceSilent !bp.silent) { // ... 按类型打印断点命中信息 } // ... 暂停、通知插件等后续处理 }执行顺序值得注意断点命中后先执行BpDelete删除断点记录包括从内存中断点列表移除硬件断点还需额外调用DeleteHardwareBreakPoint释放 DR 寄存器再决定是否打印命中信息并暂停调试。也就是说即使这次命中会让断点消失本次命中的信息输出与暂停行为依然正常发生只是不会再有第二次。这一实现同时印证了singleshoot标志在 breakpoint.h 中的定义注释whether the breakpoint should be deleted on first hit断点是否应在首次命中时被删除。扩展其他断点类型的 singleshoot 变体SetBreakpointSingleshoot仅针对软件断点但同一套标志机制通过模板化的cbDebugSetBPXSingleshootCommon复用于其他断点类型对应的命令在 x64dbg.cpp 中注册命令目标断点类型SetBreakpointSingleshoot软件断点BPNORMALSetHardwareBreakpointSingleshoot硬件断点BPHARDWARESetMemoryBreakpointSingleshoot内存断点BPMEMORYSetLibrarianBreakpointSingleshootDLL 断点BPDLLSetExceptionBreakpointSingleshoot异常断点BPEXCEPTION它们的参数语义与本文命令完全一致arg1为断点地址arg2可选标志唯一的差异是传入cbDebugSetBPXSingleshootCommon的BP_TYPE不同。各命令的参考文档位于 docs/commands/conditional-breakpoint-control 目录例如 SetHardwareBreakpointSingleshoot.md、SetMemoryBreakpointSingleshoot.md 等。实战技巧结合其他断点命令使用方式一创建时直接指定bp 命令的 ss 类型除了事后用SetBreakpointSingleshoot修改x64dbg 还允许在创建软件断点时直接声明为一次性。参考 SetBPX.md 的文档与 cmd-breakpoint-control.cpp 的解析逻辑bp命令的第三个参数类型支持ss关键字// 在 00401000 设置一次性短断点INT3 SetBPX 00401000, ss // 设置一次性 UD2 断点 SetBPX 00401000, entrypoint, ssud2从源码可见ss关键字会被翻译为UE_SINGLESHOOT并同时置位singleshoot标志与本文命令殊途同归。方式二脚本中动态切换在 x64dbg 脚本*.txt脚本或表达式脚本中可以先用条件判断命中次数再动态决定是否关闭 singleshoot// 简化示例第一次命中后自动删除的断点 SetBPX 00401000, ss run // 断点命中后已被自动删除这里继续后续流程若需要在命中后保留断点但暂时禁用可配合 DisableBPX若需要统计命中次数而非删除则应使用 SetBreakpointCondition 配合 GetBreakpointHitCount 之类的命中计数命令。singleshoot 与这些条件断点体系的定位不同它是删除型的一次性行为而不是计数型。常见问题与注意事项断点不存在时命令失败SetBreakpointSingleshoot只修改已存在的断点。若目标地址没有软件断点会输出No such breakpoint ...并返回false。务必先执行SetBPX。0与省略的区别省略arg2表示启用显式传0表示禁用。两者语义不同不可混淆。不设置结果变量该命令不会写$result脚本中如需判断成败可依赖命令返回值DbgCmdExec系列 API 的返回值。命中即删不可恢复一旦断点因 singleshoot 被自动删除如需再次断下必须重新SetBPX。因此在对长期调试目标使用前建议先确认该断点确实只需触发一次。内存断点的特殊情况内存断点设置为 singleshoot 后SetMemoryBPXEx收到的是取反后的!singleshoot参数false即底层引擎同样会把该内存断点标记为一次性命中后由引擎与调试器协同移除避免内存页面保护属性残留影响程序运行。小结SetBreakpointSingleshoot是 x64dbg 断点控制命令族中负责一次性断点语义的核心命令通过arg1指定已有软件断点、arg2控制标志默认启用0禁用使断点在首次命中后由handleBreakCondition自动删除。其底层通过BpSetSingleshoot同步更新 TitanEngine 的UE_SINGLESHOOT位并重新下发断点同时为硬件、内存、DLL、异常断点提供了同构的变体命令。掌握该命令后配合bp ... ss的创建期快捷写法与bplist的SS标记即可在入口点跟踪、一次性 API 验证、自动化脚本插桩等场景中高效控制断点生命周期。【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价