x64dbg 条件追踪命令 TraceIntoIntoTraceRecord (tiit) 详解:StepInto 回溯到已执行路径内部
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
TraceIntoIntoTraceRecord(别名TraceIntoIntoTraceCoverage/tiit)是 x64dbg 条件追踪(Conditional Tracing)体系中基于trace coverage(追踪覆盖)的自动化单步命令:它以StepInto逐指令执行,直到程序重新进入一条"此前已经执行过"的指令为止。本文以 TraceIntoIntoTraceRecord.md 为主线,结合命令注册、步进回调与 TraceRecord 底层实现,完整讲解其语法、参数默认值、执行语义、源码实现原理,以及与tibt/toit/tobt/ticnd等姊妹命令的差异和实战用法。读完本文,你将能熟练使用tiit定位"新路径"与"旧路径"的分叉点,并在 fuzz、程序行为对比等逆向场景中直接落地使用。
命令概览与语法
tiit属于 x64dbg 的 Tracing 命令族,在调试器中注册了三个等价命令名(src/dbg/x64dbg.cpp#L265):
dbgcmdnew("TraceIntoIntoTraceCoverage,TraceIntoIntoTraceRecord,tiit", cbDebugTraceIntoIntoTraceRecord, true); //Trace into into trace record完整语法如下:
TraceIntoIntoTraceCoverage / TraceIntoIntoTraceRecord / tiit [arg1] [arg2]| 参数 | 含义 | 默认值 |
|---|---|---|
[arg1] | 追踪的终止条件(break condition)。该条件被求值为非 0 时,无论当前EIP/RIP位于何处,追踪都会提前停止 | 无条件追踪(内部为"0") |
[arg2] | 调试器放弃追踪的最大步数 | 50000 |
结果变量:该命令不设置任何结果变量(result variables)。追踪结束时实际暂停的位置,需要从日志输出或cip/$tracecounter等系统变量中获取。
参数详解
arg1:追踪终止条件
arg1是一个 x64dbg 表达式。当表达式被求值为非 0 时,即使程序还没有进入 trace coverage 内部,追踪也会立即停止。不指定该参数时,追踪是无条件的——只以"是否进入 trace coverage"为唯一终止判据。
从源码看,该条件在启动追踪前由dbgsettracecondition解析并保存(src/dbg/commands/cmd-tracing.cpp#L32):
if(!dbgsettracecondition(*argv[1] ? argv[1] : "0", maxCount)) { dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid expression \"%s\"\n"), argv[1]); return false; }这里*argv[1] ? argv[1] : "0"意味着:当省略 arg1 时,实际使用的条件表达式是字面量"0"(恒为假),即"无条件追踪",完全符合文档描述。若表达式非法,命令会直接返回失败并在日志输出Invalid expression ...。
arg2:最大步数上限
arg2限制追踪的步数上限,防止死循环式的无限单步。其取值逻辑位于genericConditionalTraceCommand(src/dbg/commands/cmd-tracing.cpp#L27-L31):
duint maxCount; if(!BridgeSettingGetUint("Engine", "MaxTraceCount", &maxCount) || !maxCount) maxCount = 50000; if(argc > 2 && !valfromstring(argv[2], &maxCount, false)) return false;- 未指定
arg2时,默认值50000来自配置项Engine/MaxTraceCount;若该配置未设置或为 0,才回退到代码内置的50000。 - 显式传入
arg2时,通过valfromstring解析数值并覆盖上述默认值。注意:x64dbg 表达式中数字默认按十六进制解释,十进制需写作.123形式(docs/introduction/ConditionalTracing.md#L28)。 - 达到步数上限后,调试器会输出
Trace finished after %llu steps!并暂停(src/dbg/debugger.cpp#L1504-L1513),同时把已执行步数写入系统变量$tracecounter。
命令执行流程:从命令回调到逐指令步进
tiit的完整调用链如下:
- 命令入口
cbDebugTraceIntoIntoTraceRecord(src/dbg/commands/cmd-tracing.cpp#L112-L121):
bool cbDebugTraceIntoIntoTraceRecord(int argc, char* argv[]) { if(argc == 1) { const char* new_argv[] = { "tiit", "0" }; return conditionalTraceIntoCommand(cbTraceIntoIntoTraceRecordStep, 2, (char**)new_argv); } else return conditionalTraceIntoCommand(cbTraceIntoIntoTraceRecordStep, argc, argv); }conditionalTraceIntoCommand根据**步进过滤(party filter)**选择底层步进函数(src/dbg/commands/cmd-tracing.cpp#L42-L59):
auto party = dbggettracepartyfilter(); if(party == mod_user) stepFunction = StepIntoUser; else if(party == mod_system) stepFunction = StepIntoSystem; else if(party == -1) stepFunction = StepIntoWow64;tiit使用STEP_FUNCTION(true),即StepIntoWow64(见 src/dbg/debugger.cpp#L1536 的宏定义#define STEP_FUNCTION(into) (into ? StepIntoWow64 : StepOverWrapper))。该过滤由TraceSetStepFilter(别名SetTraceStepFilter)命令配置,取值为none(-1)/user/system(src/dbg/commands/cmd-tracing.cpp#L205-L232)。
- 每步执行后触发步进回调
cbTraceIntoIntoTraceRecordStep(src/dbg/debugger.cpp#L1558-L1561):
void cbTraceIntoIntoTraceRecordStep() { cbTraceXXTraceRecordStep(STEP_FUNCTION(true), true, cbTraceIntoIntoTraceRecordStep); }核心原理:何时判定"进入 trace coverage"
tiit的核心语义由cbTraceXXTraceRecordStep实现(src/dbg/debugger.cpp#L1528-L1534):
static void cbTraceXXTraceRecordStep(STEPFUNCTION stepFunction, bool bInto, TITANCBSTEP callback) { hActiveThread = ThreadGetHandle(GetDebugData()->dwThreadId); auto cip = GetContextDataEx(hActiveThread, UE_CIP); auto forceBreakTrace = TraceRecord.getTraceRecordType(cip) != TraceRecordManager::TraceRecordNone && (TraceRecord.getHitCount(cip) == 0) ^ bInto; cbTraceUniversalConditionalStep(cip, stepFunction, callback, forceBreakTrace); }逐项拆解这条判定表达式:
TraceRecord.getTraceRecordType(cip) != TraceRecordManager::TraceRecordNone:判断当前cip所在页是否已启用 trace coverage 记录(src/dbg/TraceRecord.cpp#L103-L112)。未被覆盖的页返回TraceRecordNone。TraceRecord.getHitCount(cip) == 0:判断当前地址的命中次数是否为 0,即该指令此前是否从未被执行过(src/dbg/TraceRecord.cpp#L450-L469)。命中次数按页粒度以位图(1 bit)、字节(6 bit 计数)或字(14 bit 计数)三种格式存储,详见 src/dbg/TraceRecord.h#L41-L47 的类型注释。(命中为 0) ^ bInto:当bInto == true(tiit)时,命中为 0 的地址(0 ^ 1 = 1)不强制中断,而命中非 0 的地址(1 ^ 1 = 0)触发forceBreakTrace = 1。
因此对tiit而言:只要cip落在"已记录且命中次数 ≥ 1"的地址上,就强制中断追踪——这正是文档所说"执行 StepInto 直到程序到达 trace coverage 内部"。
forceBreakTrace传入cbTraceUniversalConditionalStep后,会与用户自定义的 break condition 合并:若forceBreakTrace为真且 break condition 尚未命中,则强制置为 1(src/dbg/debugger.cpp#L1454-L1470)。这意味着进入 coverage 内部是最高优先级中断条件,arg1 条件只用于在进入之前提前停。
与姊妹命令的对比:tiit / tibt / toit / tobt / ticnd
tiit不是孤立命令,理解它与同类命令的差异才能真正用好它。下表基于命令注册(src/dbg/x64dbg.cpp#L261-L266)与步进回调(src/dbg/debugger.cpp#L1548-L1566)整理:
| 命令 | 别名 | 步进方式 | 终止语义(bInto 值) |
|---|---|---|---|
TraceIntoIntoTraceRecord | TraceIntoIntoTraceCoverage,tiit | StepInto | 进入 coverage 内部(bInto = true) |
TraceIntoBeyondTraceRecord | TraceIntoBeyondTraceCoverage,tibt | StepInto | 离开 coverage(bInto = false,命中为 0 时停) |
TraceOverIntoTraceRecord | TraceOverIntoTraceCoverage,toit | StepOver | 进入 coverage 内部 |
TraceOverBeyondTraceRecord | TraceOverBeyondTraceCoverage,tobt | StepOver | 离开 coverage |
- Into vs Over:
Into使用StepIntoWow64,会进入被调用函数内部逐步追踪;Over使用StepOverWrapper,单步越过函数调用。使用 Over 时,被跳过的调用内部不会暂停、不会记录、不会执行日志,速度更快(docs/introduction/ConditionalTracing.md#L44)。 - Into coverage vs Beyond coverage:前者在程序回到已执行过的路径时停,后者在程序进入从未执行过的新路径时停。两者互补,
tibt的等价表达式是ticnd tr.hitcount(cip)==0&&arg1, arg2(见 TraceIntoBeyondTraceRecord.md)。 - 与
ticnd/tocnd的关系:TraceIntoConditional(ticnd)是基于任意条件表达式的通用"Trace Into until condition"命令(docs/commands/tracing/TraceIntoConditional.md)。tiit/tibt是它基于 trace coverage 的特化版本,通过原生代码绕过表达式求值,性能更高,且天然与 trace coverage 数据联动。
前置条件:理解并启用 Trace Coverage
tiit依赖"已存在的 trace coverage 数据"作为判断依据。Trace coverage 是 x64dbg 对每条已执行指令的命中记录,每次调试器单步或暂停时都会自动更新(docs/introduction/ConditionalTracing.md#L26)。相关概念与入口:
- Trace recording(运行追踪记录):可通过菜单"Start trace recording"或命令
StartTraceRecording(别名StartRunTrace/opentrace)开启,将后续逐指令执行历史写入文件并显示在 Trace 视图中(StartRunTrace.md)。注意:只有手动单步或追踪的指令会被记录,直接run放行不会记录。 - Trace 文件格式:运行追踪文件以
TRAC魔数开头,附带 JSON 头部(版本、架构、murmurhash、被调试模块路径等),实现见 src/dbg/TraceRecord.cpp#L511-L549。 - 表达式函数:在自定义条件中可引用
tr.enabled(addr)(该地址是否启用 coverage)、tr.hitcount(addr)(命中次数)、tr.isrecording()(是否在记录),注册于 src/dbg/expressionfunctions.cpp#L122-L123,实现于 src/dbg/exprfunc.cpp#L454-L467。相关说明见 docs/introduction/Expression-functions.md#L92-L96。 - 清理覆盖数据:需要从零开始记录路径时,可重新启用 trace recording 以清空旧的 coverage 数据(见 TraceIntoBeyondTraceRecord.md 的用法示例)。
实战场景
场景一:定位"输入 B 相对输入 A 新增的执行路径"
与tibt配套的经典差分思路同样适用于tiit的反向场景(参考 TraceIntoBeyondTraceRecord.md):
- 启用 trace recording,清空覆盖数据;
- 提供输入 A 并执行追踪/单步,建立"基线路径"的 coverage;
- 提供输入 B 后执行
tiit:程序从新路径出发,一旦 StepInto 回到输入 A 已执行过的指令就自动暂停——此时cip附近即为两条路径的汇合点,可用于定位收敛逻辑或校验代码。
场景二:确认某段代码是否被二次执行
调试循环或重入逻辑时,若怀疑当前指令此前已执行过,直接运行:
tiit无条件进入追踪,直到某条"命中次数 ≥ 1"的指令被再次踩中而暂停,日志将输出Trace finished after N steps!。
场景三:组合自定义条件,提前刹车
在循环体内只想观察"进入 coverage 的前若干步"时,可用 arg1 提前终止,例如:
tiit eax == 0x1234, 10000满足eax == 0x1234或步数达到 10000 即停,避免无谓长跑。
场景四:借助 GUI 菜单快速触发
在 Debug 菜单 的 Trace Coverage 相关入口及 "Trace into until condition"/"Trace over until condition" 菜单(docs/introduction/ConditionalTracing.md#L40)中可交互式发起追踪,底层命令即ticnd/tocnd/tiit等;亦可在 CPU 视图底部命令行直接输入。
注意事项与限制
- 不设置结果变量:
tiit不写任何 result variables(如$result),停靠位置请通过暂停后的cip/EIP/RIP、日志或$tracecounter获取。 - 先暂停再追踪:若调试对象正在运行,命令会直接拒绝执行并提示
Cannot start a trace when running, pause execution first.(src/dbg/commands/cmd-tracing.cpp#L22-L26)。 - trace 未激活时不可启动:若已有 trace 处于激活状态,会提示
Trace already active(src/dbg/commands/cmd-tracing.cpp#L17-L21)。 - coverage 数据为空的行为:若当前地址所在页从未启用 trace coverage(
TraceRecordNone),tiit不会因 coverage 命中而停,只能依靠 arg1 条件或步数上限收尾——这是"先录制覆盖数据"这一前置条件的重要性所在。 - 不要在 trace 命令文本中使用
run等会改变运行状态的命令,条件追踪/断点命令中应改用 break condition、command condition 或$tracecondition系统变量(docs/introduction/ConditionalTracing.md#L42)。 - 数值默认十六进制:所有表达式中的数字默认按十六进制解释,十进制请使用
.123语法(docs/introduction/ConditionalTracing.md#L28)。
参考链接
- 命令文档:TraceIntoIntoTraceRecord.md、TraceIntoBeyondTraceRecord.md、TraceOverIntoTraceRecord.md、Tracing 命令索引
- 概念文档:条件追踪、表达式、表达式函数、Trace 视图
- 核心实现:命令注册、命令处理、步进判定、TraceRecord 类型定义、命中计数实现
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考