binary-analysis-patterns について
許可を得た防御的なコンパイル済みバイナリ解析向けのリファレンススキルです。x86やARMの逆アセンブル、制御フロー、データ構造、デコンパイルのパターンをまとめています。
主な機能
- 01x86-64とARMのアセンブリパターン
- 02呼び出し規約のリファレンス
- 03制御フローとデータ構造のパターン
- 04デコンパイルと型の復元
- 05GhidraとIDA Proの分析ヒント
- 06静的解析の安全上の実践事項
活用シーン
- 01未知のバイナリの初期調査
- 02関数とデータ構造の復元
- 03逆アセンブルによるデコンパイル結果の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ oimiragieo/ agent-studio --skill binary-analysis-patternsインストール後、Claude Code で/binary-analysis-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
commands/binary-analysis-patterns.mdhooks/post-execute.cjspre-execute.cjsreferences/research-requirements.mdrules/binary-analysis-patterns.mdschemas/input.schema.jsonoutput.schema.jsonscripts/main.cjstemplates/implementation-template.md詳細
バイナリ解析パターン
セキュリティに関する注意
許可された用途のみ:これらのスキルは、防御目的のセキュリティ分析および許可された調査に限って使用してください。
- 書面による許可を得たペネトレーションテスト
- CTF コンテストおよびセキュリティ研究
- 防御的セキュリティおよびマルウェア分析
- 適切な情報開示を伴うセキュリティ研究
- 管理された環境での教育目的
次の目的には絶対に使用しないでください:
- 悪意のあるコードの作成や強化
- システムへの不正アクセス
- ソフトウェアライセンスの不正な回避
- 知的財産の窃盗
- あらゆる違法行為
コンパイル済みバイナリを分析し、アセンブリコードを理解して、プログラムのロジックを再構築するための包括的なパターンと手法です。
逆アセンブリの基礎
x86-64 命令パターン
関数のプロローグ/エピローグ
; Standard prologue
push rbp ; Save base pointer
mov rbp, rsp ; Set up stack frame
sub rsp, 0x20 ; Allocate local variables
; Leaf function (no calls)
; May skip frame pointer setup
sub rsp, 0x18 ; Just allocate locals
; Standard epilogue
mov rsp, rbp ; Restore stack pointer
pop rbp ; Restore base pointer
ret
; Leave instruction (equivalent)
leave ; mov rsp, rbp; pop rbp
ret
呼び出し規約
System V AMD64(Linux、macOS)
; Arguments: RDI, RSI, RDX, RCX, R8, R9, then stack
; Return: RAX (and RDX for 128-bit)
; Caller-saved: RAX, RCX, RDX, RSI, RDI, R8-R11
; Callee-saved: RBX, RBP, R12-R15
; Example: func(a, b, c, d, e, f, g)
mov rdi, [a] ; 1st arg
mov rsi, [b] ; 2nd arg
mov rdx, [c] ; 3rd arg
mov rcx, [d] ; 4th arg
mov r8, [e] ; 5th arg
mov r9, [f] ; 6th arg
push [g] ; 7th arg on stack
call func
Microsoft x64(Windows)
; Arguments: RCX, RDX, R8, R9, then stack
; Shadow space: 32 bytes reserved on stack
; Return: RAX
; Example: func(a, b, c, d, e)
sub rsp, 0x28 ; Shadow space + alignment
mov rcx, [a] ; 1st arg
mov rdx, [b] ; 2nd arg
mov r8, [c] ; 3rd arg
mov r9, [d] ; 4th arg
mov [rsp+0x20], [e] ; 5th arg on stack
call func
add rsp, 0x28
ARM アセンブリパターン
ARM64(AArch64)の呼び出し規約
; Arguments: X0-X7
; Return: X0 (and X1 for 128-bit)
; Frame pointer: X29
; Link register: X30
; Function prologue
stp x29, x30, [sp, #-16]! ; Save FP and LR
mov x29, sp ; Set frame pointer
; Function epilogue
ldp x29, x30, [sp], #16 ; Restore FP and LR
ret
ARM32 の呼び出し規約
; Arguments: R0-R3, then stack
; Return: R0 (and R1 for 64-bit)
; Link register: LR (R14)
; Function prologue
push {fp, lr}
add fp, sp, #4
; Function epilogue
pop {fp, pc} ; Return by popping PC
制御フローパターン
条件分岐
; if (a == b)
cmp eax, ebx
jne skip_block
; ... if body ...
skip_block:
; if (a < b) - signed
cmp eax, ebx
jge skip_block ; Jump if greater or equal
; ... if body ...
skip_block:
; if (a < b) - unsigned
cmp eax, ebx
jae skip_block ; Jump if above or equal
; ... if body ...
skip_block:
ループパターン
; for (int i = 0; i < n; i++)
xor ecx, ecx ; i = 0
loop_start:
cmp ecx, [n] ; i < n
jge loop_end
; ... loop body ...
inc ecx ; i++
jmp loop_start
loop_end:
; while (condition)
jmp loop_check
loop_body:
; ... body ...
loop_check:
cmp eax, ebx
jl loop_body
; do-while
loop_body:
; ... body ...
cmp eax, ebx
jl loop_body
Switch 文のパターン
; Jump table pattern
mov eax, [switch_var]
cmp eax, max_case
ja default_case
jmp [jump_table + eax*8]
; Sequential comparison (small switch)
cmp eax, 1
je case_1
cmp eax, 2
je case_2
cmp eax, 3
je case_3
jmp default_case
データ構造のパターン
配列へのアクセス
; array[i] - 4-byte elements
mov eax, [rbx + rcx*4] ; rbx=base, rcx=index
; array[i] - 8-byte elements
mov rax, [rbx + rcx*8]
; Multi-dimensional array[i][j]
; arr[i][j] = base + (i * cols + j) * element_size
imul eax, [cols]
add eax, [j]
mov edx, [rbx + rax*4]
構造体へのアクセス
struct Example {
int a; // offset 0
char b; // offset 4
// padding // offset 5-7
long c; // offset 8
short d; // offset 16
};
; Accessing struct fields
mov rdi, [struct_ptr]
mov eax, [rdi] ; s->a (offset 0)
movzx eax, byte [rdi+4] ; s->b (offset 4)
mov rax, [rdi+8] ; s->c (offset 8)
movzx eax, word [rdi+16] ; s->d (offset 16)
リンクリストの走査
; while (node != NULL)
list_loop:
test rdi, rdi ; node == NULL?
jz list_done
; ... process node ...
mov rdi, [rdi+8] ; node = node->next (assuming next at offset 8)
jmp list_loop
list_done:
よくあるコードパターン
文字列操作
; strlen pattern
xor ecx, ecx
strlen_loop:
cmp byte [rdi + rcx], 0
je strlen_done
inc ecx
jmp strlen_loop
strlen_done:
; ecx contains length
; strcpy pattern
strcpy_loop:
mov al, [rsi]
mov [rdi], al
test al, al
jz strcpy_done
inc rsi
inc rdi
jmp strcpy_loop
strcpy_done:
; memcpy using rep movsb
mov rdi, dest
mov rsi, src
mov rcx, count
rep movsb
算術演算のパターン
; Multiplication by constant
; x * 3
lea eax, [rax + rax*2]
; x * 5
lea eax, [rax + rax*4]
; x * 10
lea eax, [rax + rax*4] ; x * 5
add eax, eax ; * 2
; Division by power of 2 (signed)
mov eax, [x]
cdq ; Sign extend to EDX:EAX
and edx, 7 ; For divide by 8
add eax, edx ; Adjust for negative
sar eax, 3 ; Arithmetic shift right
; Modulo power of 2
and eax, 7 ; x % 8
ビット操作
; Test specific bit
test eax, 0x80 ; Test bit 7
jnz bit_set
; Set bit
or eax, 0x10 ; Set bit 4
; Clear bit
and eax, ~0x10 ; Clear bit 4
; Toggle bit
xor eax, 0x10 ; Toggle bit 4
; Count leading zeros
bsr eax, ecx ; Bit scan reverse
xor eax, 31 ; Convert to leading zeros
; Population count (popcnt)
popcnt eax, ecx ; Count set bits
逆コンパイルのパターン
変数の復元
; Local variable at rbp-8
mov qword [rbp-8], rax ; Store to local
mov rax, [rbp-8] ; Load from local
; Stack-allocated array
lea rax, [rbp-0x40] ; Array starts at rbp-0x40
mov [rax], edx ; array[0] = value
mov [rax+4], ecx ; array[1] = value
関数シグネチャの復元
; Identify parameters by register usage
func:
; rdi used as first param (System V)
mov [rbp-8], rdi ; Save param to local
; rsi used as second param
mov [rbp-16], rsi
; Identify return by RAX at end
mov rax, [result]
ret
型の復元
; 1-byte operations suggest char/bool
movzx eax, byte [rdi] ; Zero-extend byte
movsx eax, byte [rdi] ; Sign-extend byte
; 2-byte operations suggest short
movzx eax, word [rdi]
movsx eax, word [rdi]
; 4-byte operations suggest int/float
mov eax, [rdi]
movss xmm0, [rdi] ; Float
; 8-byte operations suggest long/double/pointer
mov rax, [rdi]
movsd xmm0, [rdi] ; Double
Ghidra の分析のヒント
逆コンパイルの改善
// In Ghidra scripting
// Fix function signature
Function func = getFunctionAt(toAddr(0x401000));
func.setReturnType(IntegerDataType.dataType, SourceType.USER_DEFINED);
// Create structure type
StructureDataType struct = new StructureDataType("MyStruct", 0);
struct.add(IntegerDataType.dataType, "field_a", null);
struct.add(PointerDataType.dataType, "next", null);
// Apply to memory
createData(toAddr(0x601000), struct);
パターンマッチングスクリプト
# Find all calls to dangerous functions
for func in currentProgram.getFunctionManager().getFunctions(True):
for ref in getReferencesTo(func.getEntryPoint()):
if func.getName() in ["strcpy", "sprintf", "gets"]:
print(f"Dangerous call at {ref.getFromAddress()}")
IDA Pro のパターン
IDAPython による分析
import idaapi
import idautils
import idc
# Find all function calls
def find_calls(func_name):
for func_ea in idautils.Functions():
for head in idautils.Heads(func_ea, idc.find_func_end(func_ea)):
if idc.print_insn_mnem(head) == "call":
target = idc.get_operand_value(head, 0)
if idc.get_func_name(target) == func_name:
print(f"Call to {func_name} at {hex(head)}")
# Rename functions based on strings
def auto_rename():
for s in idautils.Strings():
for xref in idautils.XrefsTo(s.ea):
func = idaapi.get_func(xref.frm)
if func and "sub_" in idc.get_func_name(func.start_ea):
# Use string as hint for naming
pass
ベストプラクティス
分析ワークフロー
- 初期トリアージ:ファイル形式、アーキテクチャ、インポート/エクスポート
- 文字列分析:興味深い文字列やエラーメッセージを特定する
- 関数の特定:エントリポイント、エクスポート、相互参照
- 制御フローのマッピング:プログラム構造を理解する
- データ構造の復元:構造体、配列、グローバル変数を特定する
- アルゴリズムの特定:暗号、ハッシュ、圧縮
- ドキュメント化:コメント、名前を変更したシンボル、型定義
よくある落とし穴
- オプティマイザーによるアーティファクト:コードはソースの構造と一致しない場合があります
- インライン関数:関数はインライン展開されている場合があります
- 末尾呼び出し最適化:
call+retではなくjmpが使われます - デッドコード:最適化によって到達不能になったコード
- 位置独立コード:RIP 相対アドレッシング
絶対遵守の原則
- 動的実行の前に、必ず静的分析を行う — まず逆アセンブルして制御フローをマッピングしてください。事前の静的分析なしに信頼できないバイナリを実行することは、セキュリティ上のリスクとなり、再現可能な証拠を損ないます。
- 逆コンパイラーの出力を決して真実として扱わない — 逆コンパイラーが生成するのは近似結果です。セキュリティ上重要な経路については、必ず逆コンパイル結果を生の逆アセンブル結果と照合してください。
- 逐次実行を決して前提としない — 間接ジャンプ、仮想ディスパッチテーブル、JIT コンパイルされたコードは、いずれも直線的なフローを崩します。コードパスを追跡する前に、必ず相互参照とジャンプテーブルを確認してください。
- 分析の開始時に、必ずアーキテクチャと呼び出し規約を記録する — x86、x64、ARM、MIPS では呼び出し規約が異なります。これらを誤認すると、すべての引数と戻り値の分析が誤ったものになります。
- 動的分析で確認されるまで、必ず仮定を未検証として記録する — 静的分析だけでは不完全です。推定した動作にはすべて
[STATIC-ONLY]の印を付け、可能な場合は動的トレースの証拠で検証してください。
アンチパターン
| アンチパターン | 失敗する理由 | 正しいアプローチ |
|---|---|---|
| 静的解析の前にバイナリを実行する | フォレンジック上の状態を破壊し、安全上のリスクがある | まずエントリーポイントと制御フローを静的にマッピングする |
| デコンパイラーの出力をそのまま信頼する | デコンパイラーは成果物やエラーを生み出す | 重要な経路については、生の逆アセンブリと照合する |
| コードフローが線形だと仮定する | 間接ジャンプ、仮想関数テーブル、JIT 経路を見落とす | トレースする前にすべての xref とジャンプテーブルを確認する |
| アーキテクチャのドキュメント作成を省略する | 呼び出し規約を誤ると、すべての解析が無効になる | 解析を始める前にアーキテクチャと ABI を記録する |
| 静的な推論を確定事項として扱う | 動的な検証がなければ、推論が誤っている可能性がある | 実行時のトレースで確認されるまでは [STATIC-ONLY] と記す |
メモリープロトコル(必須)
開始前:
C:\dev\projects\agent-studio\.claude\context\memory\learnings.md を読みます
完了後:
- 新しいパターン ->
C:\dev\projects\agent-studio\.claude\context\memory\learnings.md - 見つかった問題 ->
C:\dev\projects\agent-studio\.claude\context\memory\issues.md - 決定事項 ->
C:\dev\projects\agent-studio\.claude\context\memory\decisions.md
中断を想定してください。メモリーに記録されていなければ、起きていないのと同じです。
コメント
Markdown 対応