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 收尾。