x64dbg 断点控制命令全解析:软件/硬件/内存/DLL/异常五类断点的用法与源码实现
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
x64dbg 的命令行断点控制系统覆盖软件断点(INT3/UD2)、硬件断点(调试寄存器)、内存断点(Guard Page)、DLL 加载断点与异常断点五大门类,并提供bpgoto、bplist、SetBPXOptions等辅助命令。本文以 breakpoint-control 文档目录 为骨架,逐条讲解 25 个命令的参数、默认值、别名与典型用法,并结合 cmd-breakpoint-control.cpp 与 breakpoint.h 的源码说明其底层原理,帮助你完全掌握 x64dbg 的脚本化断点操作能力。
断点控制命令总览与命令注册机制
x64dbg 的断点控制命令统一在调试引擎启动时注册,注册入口位于 x64dbg.cpp,每个命令通过dbgcmdnew("命令名,别名1,别名2", 回调函数, 是否可在运行时执行)注册,例如:
dbgcmdnew("SetBPX,bp,bpx", cbDebugSetBPX, true); //软件断点 dbgcmdnew("SetHardwareBreakpoint,bph,bphws", cbDebugSetHardwareBreakpoint, true); dbgcmdnew("SetMemoryBPX,membp,bpm", cbDebugSetMemoryBpx, true); dbgcmdnew("LibrarianSetBreakpoint,bpdll", cbDebugBpDll, true); dbgcmdnew("SetExceptionBPX", cbDebugSetExceptionBPX, true); dbgcmdnew("bplist", cbDebugBplist, true); dbgcmdnew("SetBPXOptions,bptype", cbDebugSetBPXOptions, false);全部命令按断点类型可分为五组,加上三个辅助命令:
| 类别 | 命令(含别名) | 底层机制 |
|---|---|---|
| 软件断点 | SetBPX/bp/bpx、DeleteBPX/bpc/bc、EnableBPX/bpe/be、DisableBPX/bpd/bd | 改写指令字节为 INT3/UD2 |
| 硬件断点 | SetHardwareBreakpoint/bph/bphws、DeleteHardwareBreakpoint/bphc/bphwc、EnableHardwareBreakpoint/bphe/bphwe、DisableHardwareBreakpoint/bphd/bphwd | x86 调试寄存器 DR0-DR3 |
| 内存断点 | SetMemoryBPX/membp/bpm、SetMemoryRangeBPX/memrangebp/bpmrange、DeleteMemoryBPX/membpc/bpmc、EnableMemoryBreakpoint/membpe/bpme、DisableMemoryBreakpoint/membpd/bpmd | Guard Page 内存保护页 |
| DLL 断点 | LibrarianSetBreakpoint/bpdll、LibrarianRemoveBreakpoint/bcdll、LibrarianEnableBreakpoint/bpedll、LibrarianDisableBreakpoint/bpddll | LdrLoadDll 回调拦截 |
| 异常断点 | SetExceptionBPX、DeleteExceptionBPX、EnableExceptionBPX、DisableExceptionBPX | 调试事件筛选 |
| 辅助命令 | bpgoto、bplist、SetBPXOptions/bptype | 断点跳转、列表与默认类型 |
在引擎内部,所有断点统一由 breakpoint.h 中的BREAKPOINT结构体描述,其type字段对应BP_TYPE枚举:
enum BP_TYPE { BPNORMAL = 0, //软件断点 BPHARDWARE = 1, //硬件断点 BPMEMORY = 2, //内存断点 BPDLL = 3, //DLL 断点 BPEXCEPTION = 4 //异常断点 };结构体中的enabled(启用状态)、singleshoot(命中一次即删除)、oldbytes(软件断点被改写前的原始字节)、titantype(传给 TitanEngine 的底层类型标志)等字段,正是下文各命令所操作的核心状态。而 breakpoint.cpp 提供的BpNew、BpGet、BpDelete、BpEnable、BpEnumAll等 API 则是所有断点命令共用的数据层接口。
软件断点:SetBPX 与 bpc/bpe/bpd
SetBPX(bp / bpx):设置软件断点
SetBPX用于在指定地址写入 INT3(short/long)或 UD2 指令,并可选地为断点命名,是最常用的下断命令。它的参数设计非常灵活:
arg1:断点地址,可以是表达式或API 名称(如kernel32.CreateFileW),这是对系统 API 下断的关键能力;[arg2]:断点名称,含空格时需用引号包裹。该名称可被EnableBPX、DisableBPX、DeleteBPX当作别名使用,但主要用途是为命中断点提供一行可读信息。当 arg2 恰好等于一个合法的断点类型(arg3 的取值)时,它会被当作类型解析,arg2 被忽略;[arg3]:断点类型,可随机序组合以下关键字:ss:single shot,命中一次后自动删除;long:长 INT3,机器码CD03;ud2:UD2 指令,机器码0F0B;short:短 INT3,机器码CC(默认)。
类型参数支持ss与其它类型组合成一个字符串。例如文档给出的经典示例:
SetBPX 00401000,"entrypoint",ssud2即在00401000处设置一个名为 "entrypoint"、命中一次即删除的 UD2 单次断点。当不指定类型、或只指定ss时,使用默认类型;默认类型为short(CC),可通过SetBPXOptions命令修改。
从源码看,cbDebugSetBPX 的实现流程为:先用valfromstring解析地址 → 判断 arg2 是否为合法类型(scmp(argname, "ss"/"long"/"ud2"))→ 用strstr从类型字符串中提取ss(置UE_SINGLESHOOT)与long/ud2/short(分别映射为UE_BREAKPOINT_TYPE_LONG_INT3/UE_BREAKPOINT_TYPE_UD2/UE_BREAKPOINT_TYPE_INT3)→ 读取并保存原字节 → 调用BpNew登记断点 → 调用 TitanEngine 的SetBPX真正改写内存。若地址上已有同名/同址断点,命令会提示 "Breakpoint already set!";若断点存在但处于禁用态,则会自动转为bpe启用。
DeleteBPX(bpc / bc):删除软件断点
DeleteBPX [arg1]删除用SetBPX设置的断点:
arg1可以是断点名称或地址;- 不指定 arg1 时删除全部软件断点。
其实现(cbDebugDeleteBPX)先尝试按名称查找(BpGet(0, BPNORMAL, name, &found)),失败再按地址解析;删除前会检查断点是否启用,若启用则调用DeleteBPX还原被改写的内存字节。
EnableBPX(bpe / be)与 DisableBPX(bpd / bd)
EnableBPX [arg1]与DisableBPX [arg1]用于启用/禁用软件断点,参数语义与删除命令一致:传名称或地址操作单个断点,不传参数则操作全部("All breakpoints enabled/disabled")。启用时通过SetBPX重新写回断点字节,禁用时通过DeleteBPX移除断点字节但保留断点记录。需要注意的是,禁用/启用的语义与"删除"不同——被禁用的断点仍保留名称、条件等元信息,可在断点窗口看到灰色状态。
硬件断点:bph 系列与调试寄存器
SetHardwareBreakpoint(bph / bphws)
硬件断点使用 CPU 的调试寄存器(DR0-DR3),不修改内存中的任何字节,因此对自校验代码、压缩壳等场景特别有效。参数如下:
arg1:断点地址;[arg2]:断点类型,r(read/write,读写均触发)、w(write,仅写入触发)、x(execute,仅执行触发),默认x;[arg3]:监视大小,1、2、4或8(8 仅 x64 可用),默认1。地址必须按该大小对齐,否则报错 "Address not aligned"。
例如,监视 4 字节数据的写入:
bph 00401000,w,4源码 cbDebugSetHardwareBreakpoint 展示了几个关键约束:
- 类型通过
switch(*argv[2])解析为UE_HARDWARE_READWRITE/UE_HARDWARE_WRITE/UE_HARDWARE_EXECUTE; - 大小在
_WIN64下才允许8(UE_HARDWARE_SIZE_8); - 设断前调用
GetUnusedHardwareBreakPointRegister寻找空闲调试寄存器,若已用满 4 个,会提示"You can only set 4 hardware breakpoints"——这是 x86 架构 DR0-DR3 的物理上限; - 通过
TITANSETDRX/TITANSETTYPE/TITANSETSIZE组装titantype,再交给 TitanEngine 的SetHardwareBreakPoint写入调试寄存器。
DeleteHardwareBreakpoint(bphc / bphwc)、EnableHardwareBreakpoint(bphe / bphwe)、DisableHardwareBreakpoint(bphd / bphwd)
这三个命令与软件断点对应命令用法一致:arg1为名称或地址,不传则操作全部。一个值得注意的差异是EnableHardwareBreakpoint不带参数时的语义——文档明确为 "as many as possible hardware breakpoints will be enabled",即尽可能多地启用(受空闲调试寄存器数量限制);而删除/禁用不带参数时是全部操作。
内存断点:bpm 系列与 Guard Page
SetMemoryBPX(membp / bpm)
SetMemoryBPX通过设置 Guard Page 保护属性,对指定地址所在的内存区域整体(从区域基址开始)进行监视:
arg1:区域内的任意地址,命令会自动定位到该内存区域的基址;[arg2]:1/0,表示命中后是否恢复(继续保留)该断点。当该值既不是1也不是0时,它会被当作 arg3 处理,即bpm eax,r等价于bpm eax,0,r;[arg3]:监视类型,a(读+写+执行)、r(读)、w(写)、x(执行),默认a。
源码 cbDebugSetMemoryBpx 的实现要点是:调用MemFindBaseAddr(addr, &size, true)自动求出区域基址与大小,然后通过BpNew(base, ..., BPMEMORY, type, 0, size)登记断点,最后调用SetMemoryBPXEx真正设置 Guard Page。从代码看,未指定 arg2 时restore默认为 0,即命中后不自动恢复(single-shot 语义);指定1则断点会一直保留,直到手动删除。监视类型解析为UE_MEMORY(a)/UE_MEMORY_READ(r)/UE_MEMORY_WRITE(w)/UE_MEMORY_EXECUTE(x)。
SetMemoryRangeBPX(memrangebp / bpmrange)
与bpm的"整块区域"不同,bpmrange允许精确指定范围:
start:范围起始地址;size:范围大小;[type]:监视类型a/r/w/x,默认a;在类型后追加ss表示 single-shot(只命中一次),等价于使用 SetMemoryBreakpointSingleshoot 命令设置单次语义。
例如:
memrangebp 00401000,100,rss表示对00401000起始的 0x100 字节做只读监视,命中一次即失效。实现 cbDebugSetMemoryRangeBpx 通过strstr(argv[3], "ss")识别单次标志,并直接以start/size调用BpNew与SetMemoryBPXEx,无需再计算区域基址。
DeleteMemoryBPX(membpc / bpmc)、EnableMemoryBreakpoint(membpe / bpme)、DisableMemoryBreakpoint(membpd / bpmd)
与前述类别一致:arg1为名称或(基)地址,不带参数则全部删除/启用/禁用。实现上,启用通过SetMemoryBPXEx重新设置 Guard Page(见 cbEnableAllMemoryBreakpoints),禁用通过RemoveMemoryBPX移除保护属性。
DLL 断点:bpdll 系列
DLL 断点用于在指定模块加载/卸载时自动中断,对跟踪插件加载顺序、监控模块注入非常实用。
LibrarianSetBreakpoint(bpdll)
arg1:要监视的 DLL 名称(不区分大小写);[arg2]:触发时机,a表示加载与卸载都中断,l仅加载,u仅卸载,默认a;[arg3]:只要出现该参数即为 single-shot,命中一次后断点自动移除;不指定则持续生效。
例如:
bpdll user32.dll bpdll myplugin.dll,l,ss第一个命令在user32.dll加载/卸载时都中断;第二个命令在myplugin.dll加载时中断一次即失效。从实现 cbDebugBpDll 看,命令会先剥离路径(strrchr(argv[1], '\\'))并转小写,再映射a/l/u为UE_ON_LIB_ALL/UE_ON_LIB_LOAD/UE_ON_LIB_UNLOAD,依次调用BpNewDll登记和dbgsetdllbreakpoint挂接底层回调。
LibrarianRemoveBreakpoint(bcdll)、LibrarianEnableBreakpoint(bpedll)、LibrarianDisableBreakpoint(bpddll)
三个命令均以 DLL 名称为参数,不带参数则作用于全部 DLL 断点(bpedll/bpddll的文档原文如此,虽然描述文字沿用了 SetBreakpoint 的说法,但语义为启用/禁用 DLL 断点)。注意 DLL 断点的查找方式与其它类型不同——它按module匹配而非地址(见BpGetAny(BPDLL, argv[1], &bp))。
异常断点:SetExceptionBPX 系列
SetExceptionBPX
异常断点把指定异常变成断点事件:只要该异常断点处于激活状态,所有相同 chance(首次/二次机会)与代码的异常都会被捕获为断点事件,不再走默认的异常处理策略。
arg1:异常名称或代码,如ACCESS_VIOLATION、80000003(INT3 断点异常代码)等;[arg2]:捕获时机(chance):first或1:仅首次机会异常(默认);second或2:仅二次机会异常;all或3:所有异常。
从实现 cbDebugSetExceptionBPX 看,地址解析支持valfromstring(数值)与ExceptionNameToCode(名称)两条路径;chance 被映射为ex_firstchance/ex_secondchance/ex_all并存入BpNew(ExceptionCode, true, false, 0, BPEXCEPTION, extype, "")。注意异常断点的"地址"字段实际存放的是异常代码,这是它与其它断点类型最本质的区别。
DeleteExceptionBPX / EnableExceptionBPX / DisableExceptionBPX
三者参数语义一致:arg1为名称、异常名或代码;不带参数则删除/启用/禁用全部异常断点。删除与查找时同样支持名称与ExceptionNameToCode两种匹配方式(cbDebugDeleteExceptionBPX)。
辅助命令:bpgoto、bplist、SetBPXOptions
bpgoto:命中断点后跳转
bpgoto arg1,arg2把断点配置成"到达 arg1 时,将程序流导向 arg2"。文档明确给出了它的等价展开——以下四条命令:
SetBreakpointCondition arg1, 0 SetBreakpointCommand arg1, "CIP=arg2" SetBreakpointCommandCondition arg1, 1 SetBreakpointFastResume arg1, 0即:条件恒为假(不真正停止)、命中后执行CIP=arg2改写指令指针、命令条件恒真、不快速恢复(保证命令被执行)。源码 cbDebugSetBPGoto 正是通过cmddirectexec依次执行这四条命令实现的。这本质上是一种"断点即补丁"技巧,常用于跳过反调试代码或某段耗时逻辑。
bplist:列出全部断点
bplist无参数,输出所有断点的状态、类型、地址与可选名称。每行格式为:
STATE:TYPE:ADDRESS[:NAME]STATE:0表示禁用,1表示启用。注意文档说明:只有 single-shot 与普通断点可以被禁用(言下之意部分类型状态固定);TYPE:BP(普通软件断点,SetBPX设置)、SS(single-shot 断点)、HW(硬件断点)、GP(Guard Page,即内存断点);ADDRESS:断点地址,x32/x64 调试器分别以 32/64 位宽度输出;NAME:断点名称(若有)。
从 cbBreakpointList 的实现看,实际输出还额外支持DLL与EX两种类型(对应 DLL 断点与异常断点),且 DLL 断点的行格式为STATE:TYPE:"模块名":"名称",异常断点的"地址"列输出的是异常代码。因此bplist也是脚本里枚举断点、做自动化断点管理的基础工具。
SetBPXOptions(bptype):设置默认断点类型
SetBPXOptions arg1修改SetBPX的默认类型,可选值:
short:CC(短 INT3,默认);long:CD03(长 INT3);ud2:0F0B(UD2)。
文档强调该默认类型同时影响普通断点与 single-shot 断点。实现 cbDebugSetBPXOptions 会把选择持久化到配置文件:BridgeSettingSetUint("Engine", "BreakpointType", setting_type)(0=short、1=long、2=ud2),下次启动依然生效。UD2 断点对反调试检测(如检测 CC 断点字节的完整性校验)有天然对抗优势,是逆向常用技巧。
实战:组合使用与脚本化建议
将上述命令串联即可完成复杂的调试自动化,例如:
bpx kernel32.CreateFileW,"CreateFile" //对 API 下带名断点 bph 00401234,w,4 //监视 4 字节写操作 bpm eax,r //对 eax 所在内存区域做读监视 bpdll plugin.dll,l,ss //插件加载时中断一次 SetExceptionBPX ACCESS_VIOLATION,first //捕获首违例异常 bplist //查看全部断点状态 bpgoto 00401337,00401400 //命中 00401337 时跳转 bptype ud2 //把默认软件断点类型改为 UD2所有这些命令均可在命令栏、脚本文件(配合 script 命令)以及条件断点的命令回调中使用,断点命中后的条件、日志、命令执行等进阶能力则属于 conditional-breakpoint-control 章节的范畴(例如上文提到的 SetMemoryBreakpointSingleshoot)。掌握本节的 25 个命令与 BREAKPOINT 结构 的字段语义,即可对 x64dbg 的断点体系形成完整认知,写出精确、可复用的调试脚本。
小结
x64dbg 的断点控制命令在设计上高度一致——每个断点类别都配套"设置/删除/启用/禁用"四个命令,且都支持"不传参数即操作全部"的批量语义,这降低了记忆成本;而每种断点的底层机制(字节改写、调试寄存器、Guard Page、加载回调、异常筛选)又各有特性与限制,例如硬件断点上限 4 个、内存断点作用于整个区域、异常断点按代码而非地址登记等。结合 cmd-breakpoint-control.cpp 的源码逐条对照,可以更准确地预判每条命令在边界情况下的行为,让断点成为逆向与恶意软件分析中最可靠的控制手段。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考