binary-analysis-patterns について
逆アセンブル、制御フローの識別、プログラムロジックの再構成に関するパターンとチェック項目をまとめたスキルです。コンパイル済みバイナリの解析に利用できます。
主な機能
- 01x86-64 と ARM のアセンブリパターン
- 02呼び出し規約の参考情報
- 03制御フローとデータ構造のパターン
- 04逆コンパイルと型復元のヒント
- 05Ghidra と IDA Pro の例
活用シーン
- 01関数の引数と戻り値の推定
- 02ループと分岐構造の識別
- 03アセンブリからのデータ構造復元
- 04許可されたバイナリの解析手順整理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill binary-analysis-patternsインストール後、Claude Code で/binary-analysis-patternsを入力して読み込まれたか確認します。
詳細
バイナリ解析のパターン
コンパイル済みバイナリの解析、アセンブリコードの理解、プログラムロジックの再構築に関する包括的なパターンと手法です。
このスキルを使用する場合
- バイナリ解析のパターンに関するタスクやワークフローに取り組む場合
- バイナリ解析のパターンに関するガイダンス、ベストプラクティス、チェックリストが必要な場合
このスキルを使用しない場合
- タスクがバイナリ解析のパターンに関係しない場合
- この範囲外の別の分野やツールが必要な場合
手順
- 目標、制約、必要な入力を明確にします。
- 関連するベストプラクティスを適用し、結果を検証します。
- 実行可能な手順と検証方法を提示します。
解析の境界
許可されたバイナリのみを、まず不活性なデータとして解析します。不明なコードの実行やエミュレーションは、ホストの認証情報や外部ネットワークにアクセスできない、隔離された使い捨て環境でのみ行います。ファイルハッシュ、アーキテクチャ、ABI、コンパイラに関する前提を記録します。アセンブリのスニペットはパターンであり、そのままアセンブル可能なプログラムではありません。浮動小数点数や集約型の引数、ABIのアラインメント規則については、別途考慮する必要があります。以下の構造体オフセットはLP64形式のレイアウトを前提としています。Windows x64ではlongは32ビットです。逆コンパイラが推定した型や名前、疑わしいAPI参照は仮説であり、ソースコードの事実でも、脆弱性の証明でもありません。
逆アセンブルの基礎
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 for unsigned/nonnegative values
and eax, 7 ; x % 8 (not signed negative C remainder)
ビット操作
; 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
test ecx, ecx ; BSR result is undefined for zero input
jz zero_input ; application-specific branch: CLZ(0) is 32
bsr eax, ecx ; Bit scan reverse for nonzero input
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相対アドレッシング
制限事項
- 上記の説明にある適用範囲にタスクが明確に該当する場合にのみ、このスキルを使用してください。
- 出力を、環境固有の検証、テスト、専門家によるレビューの代替として扱わないでください。
- 必要な入力、権限、安全上の境界、成功基準が不足している場合は、作業を中止して確認を求めてください。
コメント
Markdown 対応