← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

x86-64のデバッグと逆アセンブル実践

クラッシュアドレスから関数、スタック、ABI、メモリ契約を復元する

INTERACTIVE READING MODE →

x86-64のデバッグと逆アセンブル実践

本記事は講義を目的としています。 逆アセンブルはbinary動作を観察する道具であり、source意図や解析権限を証明しません。権限のあるprogramとcrashだけを対象にしてください。

最適化buildでは変数が消え、関数がinlineされ、instruction pointerが一つのmovを指す。必要なのはopcode暗記より、register、stack、memory、ABIを証拠順に結ぶ手順である。

最初に保存する証拠

正確な実行file/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/free/wrong objectか、rdxはindexかbyte offsetか、計算値はfault addressと一致するか、4バイト全体が範囲内か、前の命令が両registerをどこから作ったかを確認する。fault位置は原因発生位置とは限らない。

register provenanceとpath constraint

mov rcx, [rbx + 20h]
test rcx, rcx
je error
mov edx, [rsi + 8]
mov eax, [rcx + rdx*4]

親objectのlifetime、実際のbranch path、他threadの変更を調べる。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だが、関数中間では再利用される。callerのcall siteまでdata flowを戻してからsource引数名を付ける。

push rbx
sub rsp, 40h
mov rbx, rcx

入口rsp=Sからsaved registerとreturn address位置を計算し、全exitで復元を確認する。Windowsでは.pdata/.xdataと実prologueを照合する。

間接callとmemory

call qword ptr [rax + 18h]

function table、vtable、import thunk、callbackの可能性がある。raxの由来、table access、targetのexecutable範囲、calling convention、argument、object lifetime、CFG/CETを確認する。実行可能addressだけでは正しいsignatureを証明しない。

memoryはaccess幅とlittle endianのraw byteで見る。use-after-freeはまだreadableで直ちにfaultしないことがあり、guard pageやAddressSanitizerが原因を早く捕捉できる。

toolの役割

disassemblerはbyteを命令へ、debuggerは一実行の状態を、decompilerは高水準表現を推測し、profilerは時間/eventをsamplingし、sanitizerは特定契約違反をinstrumentする。decompilerの変数名とifは仮説なのでreal instructionとABIで照合する。

fault命令をNOPにするbinary patchはflags、register lifetime、relocation、signature、unwind、mitigationを壊し得る。原則としてsource契約を修正し、regression test付きで再buildする。

まとめ

x86-64 crashはfaulting instructionからeffective address、register provenance、path constraint、ABI、stack、object lifetimeへ範囲を広げて調べる。正確なbinary/symbolが必要で、stack traceとdecompilerも検証対象である。最終的にsource修正と回帰testで閉じる。

公式資料