x64dbg 命令详解:GetExceptionBreakpointHitCount —— 读取异常断点命中计数
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
GetExceptionBreakpointHitCount是 x64dbg(开源 Windows 用户态调试器,面向逆向工程与恶意软件分析)内置的命令行命令,用于读取某个异常断点(Exception Breakpoint)的命中计数器(hit counter)当前值,并将结果写入脚本变量$result。该命令是 x64dbg 条件断点控制命令族(conditional-breakpoint-control)的一员,常与ResetExceptionBreakpointHitCount(重置计数)、SetExceptionBreakpointCondition(设置命中条件)等配合,用于在脚本与自动化调试中精确统计某个异常在调试会话中出现或被捕获的次数。
读完本文,你将掌握:该命令的完整语法与参数形式(断点名称 / 异常名称 / 异常代码)、$result返回值的使用方式、命中计数在调试器内核中的递增与存储原理,以及将其嵌入 x64dbg 脚本实现"按异常出现次数触发动作"的实战方案。
命令概览
命令注册于调试器命令表中,对应入口为cbDebugGetBPXExceptionHitCount,声明与注册见 x64dbg.cpp:
dbgcmdnew("GetExceptionBreakpointHitCount", cbDebugGetBPXExceptionHitCount, true); //get breakpoint hit count其中第三个参数true表示该命令为调试中命令(debugging command),即只在调试会话进行中可用。
命令的基本语义(来自原文档 GetExceptionBreakpointHitCount.md):
- 功能:获取异常断点的命中计数器(Gets the hit counter of an exception breakpoint)。
- 参数:
arg1—— 异常断点的名称(name)、异常名称(exception name)或异常代码(code)。 - 结果:
$result被设置为命中计数器的当前值。
语法
GetExceptionBreakpointHitCount <arg1>arg1必填;缺省参数时命令直接返回失败(见下文源码分析)。- 命令不打印命中值到日志,而是通过
$result变量向调用方(脚本、命令行求值)返回结果,这与 x64dbg 大量Get*命令的返回值约定一致。
参数 arg1 的三种形式与查找逻辑
arg1用于定位目标异常断点,官方文档明确支持三种形式:
- 断点名称(name):通过
SetExceptionBreakpointName为异常断点设置的自定义名称(同名命令族文档见 SetExceptionBreakpointName.md)。 - 异常名称(exception name):如
EXCEPTION_ACCESS_VIOLATION、EXCEPTION_BREAKPOINT等系统/已知异常名。 - 异常代码(code):如
C0000005(访问违例)、80000003(断点指令异常)等十六进制异常码。
三种形式并非各自独立处理,而是由断点查找函数BpGetAny依次尝试,其实现位于 breakpoint.cpp:
bool BpGetAny(BP_TYPE Type, const char* Name, BREAKPOINT* Bp) { if(BpGet(0, Type, Name, Bp)) // ① 按名称查找 return true; if(Type != BPDLL) { duint addr; if(valfromstring(Name, &addr)) // ② 按地址/数值表达式查找 if(BpGet(addr, Type, 0, Bp)) return true; if(Type == BPEXCEPTION) { addr = 0; if(ExceptionNameToCode(Name, reinterpret_cast<unsigned int*>(&addr))) // ③ 异常名 → 异常码 if(BpGet(addr, BPEXCEPTION, 0, Bp)) return true; } } return false; }结合 exception.cpp 中的ExceptionNameToCode,实际查找顺序可归纳为:
- 先将
arg1当作断点名称精确匹配; - 再将
arg1当作数值表达式(valfromstring)解析为地址/代码进行匹配; - 若类型为异常断点(
BPEXCEPTION),再尝试将arg1当作异常名称经ExceptionNameToCode转为异常码后匹配。
因此在实践中,即使异常断点没有设置自定义名称,直接传EXCEPTION_ACCESS_VIOLATION或C0000005也能正确命中同一断点——x64dbg 内部以异常码作为异常断点的寻址键值(BpGet(addr, BPEXCEPTION, 0, Bp))。
返回值 $result 与底层实现
命令的核心逻辑集中在 cmd-conditional-breakpoint-control.cpp:
bool cbDebugGetBPXExceptionHitCount(int argc, char* argv[]) { return cbDebugGetBPXHitCountCommon(BPEXCEPTION, argc, argv); }所有断点类型(普通、硬件、内存、DLL、异常)的"读取命中计数"共用同一实现cbDebugGetBPXHitCountCommon,异常断点只是传入BPEXCEPTION类型标记。公共实现见同文件 L148-L161:
static bool cbDebugGetBPXHitCountCommon(BP_TYPE Type, int argc, char* argv[]) { if(IsArgumentsLessThan(argc, 2)) return false; BREAKPOINT bp; if(!BpGetAny(Type, argv[1], &bp)) { dprintf(QT_TRANSLATE_NOOP("DBG", "No such breakpoint \"%s\"\n"), argv[1]); return false; } varset("$result", bp.hitcount, false); return true; }由此可以得出几点确定的行为:
- 参数缺失:
argc < 2时直接返回false,命令失败且不设置$result。 - 断点不存在:
BpGetAny查找失败时,向日志输出No such breakpoint "<arg1>"并返回失败;此时$result不会被修改。 - 命中计数类型:断点结构体中的
hitcount字段为uint32(见 breakpoint.h:uint32 hitcount = 0; // hit counter),因此$result得到的是一个无符号 32 位整数,超出0xFFFFFFFF会回绕(实际调试中几乎不可能达到)。 - 变量写入:
varset("$result", bp.hitcount, false)将计数写入全局变量$result;命令自身不向日志打印数值,需要显示时请配合log等命令。
读取到的计数是断点从创建(或上次重置)以来被命中的累计次数,具体递增时机见下一节。
命中计数的工作原理:何时递增、如何存储
异常断点的命中计数并不是由GetExceptionBreakpointHitCount自身维护的,它只是读取者。计数真正的递增发生在调试器命中断点的主流程中,见 debugger.cpp:
// increment hit count InterlockedIncrement((volatile long*)&bpPtr->hitcount); ... varset("$breakpointcounter", bp.hitcount, true); //save the breakpoint counter as a variable要点如下:
- 线程安全递增:使用
InterlockedIncrement原子自增,保证多线程调试场景下计数不丢失、不撕裂。 - 命中即递增:只要断点被触发(无论随后是否因为条件不满足而继续运行),计数都会先 +1,再进入条件求值、日志输出、命令执行等后续处理。因此
hitcount反映的是"断点被触发次数",而非"断点真正暂停次数"。 - 伴生变量:每次命中后,调试器还会把同一计数写入内置变量
$breakpointcounter(varset第三个参数为true表示全局变量)。这意味着在断点的条件表达式、日志表达式或断点命令中,可以直接引用$breakpointcounter来基于命中次数做逻辑判断;而GetExceptionBreakpointHitCount则适合在脚本的任意时刻主动查询当前计数。 - 存储位置:命中计数持久保存在断点对象(
BREAKPOINT结构体,见 breakpoint.h)中,随断点一起被数据库(database)保存/加载,因此关闭并重新打开被调试程序后计数依然保留(除非断点被删除或显式重置)。
实战示例
1. 基本用法:查询异常断点计数
// 按异常名称查询 GetExceptionBreakpointHitCount EXCEPTION_ACCESS_VIOLATION log $result // 按异常代码查询(等价) GetExceptionBreakpointHitCount C0000005 log $result // 按自定义断点名称查询 GetExceptionBreakpointHitCount myAVBp log $result2. 嵌入脚本:按异常出现次数触发动作
下面的 x64dbg 脚本片段演示了"当访问违例异常被命中 3 次后执行自定义处理"的典型组合用法(断点设置命令详见 SetExceptionBPX.md,命中条件设置见 SetExceptionBreakpointCondition.md):
// 设置访问违例异常断点 SetExceptionBPX EXCEPTION_ACCESS_VIOLATION // 读取当前命中次数 GetExceptionBreakpointHitCount EXCEPTION_ACCESS_VIOLATION // 依据 $result 判断 cmp $result, 3 je .handle ... .handle: log "AV hit 3 times, taking over!"也可以直接利用断点命令中的$breakpointcounter实现同等效果:
SetExceptionBPX EXCEPTION_ACCESS_VIOLATION SetExceptionBreakpointCommand EXCEPTION_ACCESS_VIOLATION "log $breakpointcounter"3. 与重置命令配合实现窗口计数
先重置、再读取,即可统计"从此刻起"的异常出现次数。重置命令为ResetExceptionBreakpointHitCount(注册见 x64dbg.cpp,实现复用cbDebugResetBPXHitCountCommon,见 cmd-conditional-breakpoint-control.cpp,并支持可选的第二个参数指定重置后的起始值):
ResetExceptionBreakpointHitCount EXCEPTION_ACCESS_VIOLATION // ... 运行一段时间 ... GetExceptionBreakpointHitCount EXCEPTION_ACCESS_VIOLATION log $result // 只包含重置后的命中次数注意事项与限制
- 必须在调试会话中使用:命令注册时标记为调试中命令,且底层
BpGet在 breakpoint.cpp 中首先检查DbgIsDebugging(),未处于调试状态时查找必然失败。 - 断点必须已存在:查询不存在的断点会得到日志错误
No such breakpoint "..."且命令返回失败,$result保持不变;不会自动创建断点。 - 参数缺省即失败:
arg1必填,遗漏参数命令直接失败。 - 计数口径:命中计数包含被条件过滤掉的命中(先递增后判条件),需要"净命中次数"时请结合条件统计或自行在断点命令中维护计数变量。
- 查找歧义:当
arg1既匹配断点名称又匹配数值/异常码时,按"名称 → 数值 → 异常名转码"的优先级取第一个命中结果(见上文BpGetAny顺序)。
源码与文档索引
| 内容 | 位置 |
|---|---|
| 命令注册 | src/dbg/x64dbg.cpp |
异常断点入口cbDebugGetBPXExceptionHitCount | src/dbg/commands/cmd-conditional-breakpoint-control.cpp |
公共实现cbDebugGetBPXHitCountCommon | src/dbg/commands/cmd-conditional-breakpoint-control.cpp |
断点查找BpGetAny/BpGet | src/dbg/breakpoint.cpp / src/dbg/breakpoint.cpp |
| 命中计数存储字段 | src/dbg/breakpoint.h |
命中时计数递增与$breakpointcounter | src/dbg/debugger.cpp |
异常名转异常码ExceptionNameToCode | src/dbg/exception.cpp |
| 官方命令文档 | docs/commands/conditional-breakpoint-control/GetExceptionBreakpointHitCount.md |
| 同类命令(重置计数) | docs/commands/conditional-breakpoint-control/ResetExceptionBreakpointHitCount.md |
该命令与ResetExceptionBreakpointHitCount、SetExceptionBreakpointCondition、SetExceptionBreakpointCommand等共同构成完整的异常断点条件控制体系(完整命令清单见 docs/commands/conditional-breakpoint-control/index.rst),是 x64dbg 脚本化、自动化逆向调试中统计与响应特定异常的常用基础能力。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考