x86-64 调试与反汇编实战
从崩溃地址恢复函数、栈、ABI 与内存契约
x86-64 调试与反汇编实战
本文以教学为目的。 反汇编用于观察 binary 行为,不能证明 source 意图或分析权限。只分析自己有权检查的程序与 crash。
优化 build 中,变量可能消失、函数 inline,instruction pointer 只落在某一条 mov。关键不是背 opcode,而是按证据顺序连接 register、stack、memory 与 ABI。
先保存证据
保存完全一致的 executable/module 与 build ID/hash、PDB/DWARF、exception/signal、fault address 与 module offset、register/flags、stack trace 与 stack bytes、read/write/execute 类型、输入、thread、OS、CPU。相似但不同 build 的 symbol 可能指向错误函数。
ASLR 地址先转换:
module_offset = faulting_address - loaded_module_base
从 faulting instruction 开始
mov eax, dword ptr [rcx + rdx*4]
effective_address = RCX + RDX * 4
access_width = 4 bytes
确认 rcx 是 null、已释放还是错误 object;rdx 是 index 或 byte offset;计算结果是否等于 fault address;完整四字节是否在范围内;前面指令从哪里取得两个 register。fault 是症状出现的位置,不一定是原因发生的位置。
provenance 与 path constraint
mov rcx, [rbx + 20h]
test rcx, rcx
je error
mov edx, [rsi + 8]
mov eax, [rcx + rdx*4]
追踪 parent object、实际分支、其他 thread 是否改变 lifetime。race 可能无法从单 thread snapshot 证明。
cmp rdx, r8
jae fail
mov eax, [rcx + rdx*4]
正常 edge 证明 unsigned rdx < r8。记录这些条件,避免把 jl 与 jb 混淆。
stack trace 与 ABI
stack trace 依赖 symbol 与 unwind metadata,可能被 buffer overflow、错误 ASM unwind、wrong symbol、tail call、inline 或特殊 context switch 破坏。检查每个 return address 是否在可执行 module 且接近合理 call site。
Windows 入口参数通常在 rcx,rdx,r8,r9,System V 在 rdi,rsi,rdx,rcx,r8,r9,但函数中段可被重用。必须沿 data flow 回到 caller 才能命名 source 参数。
push rbx
sub rsp, 40h
mov rbx, rcx
令入口 rsp=S,推算 saved register 与 return address,验证所有 exit 的恢复。Windows 还要比对 .pdata/.xdata 与真实 prologue。
间接调用与内存
call qword ptr [rax + 18h]
可能是 function table、vtable、import thunk 或 callback。追踪 rax、table access、target executable 区域、calling convention、参数、object lifetime 与 CFG/CET。可执行地址本身不能证明 signature 正确。
用 access width、little endian 与 raw bytes 查看 memory。use-after-free 可能仍可读而不立即 fault;guard page 与 AddressSanitizer 可更早抓到原因。
disassembler 解码 bytes,debugger 观察一次执行,decompiler 推测高层结构,profiler 采样时间,sanitizer instrumentation 特定契约。decompiler 的变量名与 if 都要用真实指令与 ABI 交叉验证。
总结
x86-64 crash 分析从 faulting instruction 扩展到 effective address、register provenance、path constraint、ABI、stack 与 object lifetime。精确 binary/symbol 是前提,stack trace 与 decompiler 也要验证。最后用 source contract 修复与 regression test 收尾。