news 2026/9/19 18:40:12

x64dbg 命令详解:GetExceptionBreakpointHitCount —— 读取异常断点命中计数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
x64dbg 命令详解:GetExceptionBreakpointHitCount —— 读取异常断点命中计数

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用于定位目标异常断点,官方文档明确支持三种形式:

  1. 断点名称(name):通过SetExceptionBreakpointName为异常断点设置的自定义名称(同名命令族文档见 SetExceptionBreakpointName.md)。
  2. 异常名称(exception name):如EXCEPTION_ACCESS_VIOLATIONEXCEPTION_BREAKPOINT等系统/已知异常名。
  3. 异常代码(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,实际查找顺序可归纳为:

  1. 先将arg1当作断点名称精确匹配;
  2. 再将arg1当作数值表达式valfromstring)解析为地址/代码进行匹配;
  3. 若类型为异常断点(BPEXCEPTION),再尝试将arg1当作异常名称ExceptionNameToCode转为异常码后匹配。

因此在实践中,即使异常断点没有设置自定义名称,直接传EXCEPTION_ACCESS_VIOLATIONC0000005也能正确命中同一断点——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反映的是"断点被触发次数",而非"断点真正暂停次数"。
  • 伴生变量:每次命中后,调试器还会把同一计数写入内置变量$breakpointcountervarset第三个参数为true表示全局变量)。这意味着在断点的条件表达式、日志表达式或断点命令中,可以直接引用$breakpointcounter来基于命中次数做逻辑判断;而GetExceptionBreakpointHitCount则适合在脚本的任意时刻主动查询当前计数。
  • 存储位置:命中计数持久保存在断点对象(BREAKPOINT结构体,见 breakpoint.h)中,随断点一起被数据库(database)保存/加载,因此关闭并重新打开被调试程序后计数依然保留(除非断点被删除或显式重置)。

实战示例

1. 基本用法:查询异常断点计数

// 按异常名称查询 GetExceptionBreakpointHitCount EXCEPTION_ACCESS_VIOLATION log $result // 按异常代码查询(等价) GetExceptionBreakpointHitCount C0000005 log $result // 按自定义断点名称查询 GetExceptionBreakpointHitCount myAVBp log $result

2. 嵌入脚本:按异常出现次数触发动作

下面的 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
异常断点入口cbDebugGetBPXExceptionHitCountsrc/dbg/commands/cmd-conditional-breakpoint-control.cpp
公共实现cbDebugGetBPXHitCountCommonsrc/dbg/commands/cmd-conditional-breakpoint-control.cpp
断点查找BpGetAny/BpGetsrc/dbg/breakpoint.cpp / src/dbg/breakpoint.cpp
命中计数存储字段src/dbg/breakpoint.h
命中时计数递增与$breakpointcountersrc/dbg/debugger.cpp
异常名转异常码ExceptionNameToCodesrc/dbg/exception.cpp
官方命令文档docs/commands/conditional-breakpoint-control/GetExceptionBreakpointHitCount.md
同类命令(重置计数)docs/commands/conditional-breakpoint-control/ResetExceptionBreakpointHitCount.md

该命令与ResetExceptionBreakpointHitCountSetExceptionBreakpointConditionSetExceptionBreakpointCommand等共同构成完整的异常断点条件控制体系(完整命令清单见 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 18:39:53

安居客租房数据分析与可视化:从爬虫到PDF报告生成全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 18:39:21

机械拆装与结构分析PPT自动化生成:从流程拆解到python-pptx实践

简介&#xff1a;《机械拆装与结构分析》是一份面向机械工程专业学生与实训教师的PPT课件&#xff0c;聚焦减速器拆装与结构分析实验&#xff0c;适用于机械设计、维修及管理等方向的教学实操场景。课件围绕JZQ-250型二级展开式圆柱齿轮减速器和教学用单级圆柱齿轮减速器&#…

作者头像 李华
网站建设 2026/9/19 18:36:41

阀室无人值守BBRTU控制系统:从选型到调试的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 18:35:56

安踏终端培训验收试卷:零售一线能力落地的校准器

简介&#xff1a;本资源为安踏终端销售岗位专项培训验收试卷&#xff08;第一套&#xff09;&#xff0c;面向一线零售人员、新员工入职考核及区域督导复盘使用&#xff0c;旨在系统检验员工对公司历史、品牌战略、销售方法论及基础业务术语的掌握程度。试卷涵盖10道核心题型&a…

作者头像 李华
网站建设 2026/9/19 18:34:56

Arduino避障循迹小车:嵌入式控制闭环系统实战

简介&#xff1a;本资源是一份面向电子工程初学者与嵌入式实践者的Arduino避障循迹小车完整课程报告&#xff0c;聚焦自动化控制基础应用&#xff0c;解决小型智能车体的路径识别与动态避障两大核心问题。文档以Arduino UNO为主控&#xff0c;系统讲解L298N电机驱动、HC-SR04超…

作者头像 李华