mcpField
C
code-obfuscation-deobfuscation

code-obfuscation-deobfuscation

github.com/yaklangセキュリティ2.6k4.1k 回インストール6 週間前に更新
リポジトリ

code-obfuscation-deobfuscation について

code-obfuscation-deobfuscationは、難読化されたネイティブバイナリを扱うリバースエンジニア向けの分析ガイドです。難読化のパターンを見分け、静的・動的解析、シンボリック実行、エミュレーションを使って構造と挙動を調べます。

主な機能

  1. 01難読化パターンの分類
  2. 02不透明述語とジャンクコードの分析
  3. 03自己書き換えコードの調査
  4. 04制御フロー平坦化の復元
  5. 05VM保護と文字列の分析
  6. 06逆アセンブル妨害の分析

活用シーン

  1. 01制御フロー平坦化されたバイナリの復元
  2. 02実行時に復号されるコードの調査
  3. 03VM保護されたバイナリの分析
  4. 04隠された文字列やインポートの特定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yaklang/hack-skills --skill code-obfuscation-deobfuscation

インストール後、Claude Code で/code-obfuscation-deobfuscationを入力して読み込まれたか確認します。

詳細

SKILL:コード難読化と難読化解除 — エキスパート分析プレイブック

AI LOAD INSTRUCTION:ネイティブバイナリのコード難読化を特定、分類し、無効化するためのエキスパート向けテクニックです。ジャンクコード、不透明述語、SMC、制御フローのフラット化、movfuscator、VM プロテクター(VMProtect/Themida/Code Virtualizer)、文字列暗号化、インポート隠蔽、逆アセンブル対策のトリックを扱います。ベースモデルは、パッキングと難読化を混同しがちで、静的な難読化解除戦略と動的な難読化解除戦略の違いを見落とすことがあります。

0. 関連するルーティング

すばやく見分けるポイント
IDA/Ghidra での症状考えられる難読化最初に試すこと
平坦な CFG、大きな単一の switch制御フローのフラット化シンボリック実行で CFG を復元
mov 命令のみmovfuscator難読化解除/トレースベースのリフト
pushad/pushfd → VM エントリVM プロテクターハンドラーテーブルの抽出
コード実行前の XOR ループSMC/文字列暗号化動的解析、復号後にブレークポイントを設定
不可能な条件(不透明述語)ジャンクコードの挿入パターンベースの除去
すべての文字列が読めない文字列暗号化復号ルーチンにフックするか、エミュレート
IAT にインポートがないインポート隠蔽GetProcAddress/ハッシュ解決をトレース

1. ジャンクコードと不透明述語

1.1 ジャンクコードの挿入

プログラムの出力に影響しないデッドコードを追加し、解析にかかる時間を増やします。

識別方法:

  • レジスターやメモリーに書き込むが、その後読み取られない命令
  • 戻り値が破棄され、副作用もない関数呼び出し
  • 使用されない結果を計算する、不変の境界値を持つループ

除去戦略:

  1. 定義・使用チェーンを計算します(IDA/Ghidra のデータフロー解析)
  2. 後続で使用されない命令をデッドコードとしてマークします
  3. 除去によってプログラムの動作が変わらないことを確認します(トレース比較)
1.2 不透明述語

条件が常に真または常に偽であるにもかかわらず、それが明白ではない条件分岐です。

種類例常に評価される値
算術x² ≥ 0真
数論x*(x+1) % 2 == 0真(連続する整数の積)
ポインタベースエイリアス後の ptr == ptr真
ハッシュベースCRC32(constant) == known_value真

難読化解除:

  • 抽象解釈:条件が定数であることを証明します
  • シンボリック実行:Z3 で ∀x: predicate(x) = True を証明します
  • パターンマッチング:既知の不透明述語のパターンを認識します
  • 動的解析:トレースして、分岐が実行されない/常に実行されることを確認します
import z3
x = z3.BitVec('x', 32)
s = z3.Solver()
s.add(x * (x + 1) % 2 != 0)
print(s.check())  # unsat → always true

2. 自己書き換えコード(SMC)

実行時にコードを書き換えます。暗号化されたコードは、実行直前に復号されます。

2.1 XOR 復号ループ(最も一般的)
lea esi, [encrypted_code]
mov ecx, code_length
mov al, xor_key
decrypt_loop:
    xor byte [esi], al
    inc esi
    loop decrypt_loop
    jmp encrypted_code  ; now decrypted
2.2 分析戦略
1. Identify the decryption routine (look for XOR/ADD/SUB in loops writing to .text)
2. Set breakpoint AFTER the loop completes
3. At breakpoint: dump the decrypted memory region
4. Re-analyze the dumped code in IDA/Ghidra
5. For multi-layer: repeat for each decryption stage
2.3 エミュレーションによる自動アンパック
from unicorn import *
from unicorn.x86_const import *

mu = Uc(UC_ARCH_X86, UC_MODE_32)
mu.mem_map(0x400000, 0x10000)
mu.mem_write(0x400000, binary_code)
mu.emu_start(decrypt_entry, decrypt_end)
decrypted = mu.mem_read(code_start, code_length)

3. 制御フローのフラット化(CFF)

3.1 構造

元の逐次ブロックは、ディスパッチャーループに変換されます。

Original:      A → B → C → D

Flattened:     ┌──────────────────┐
               │   dispatcher     │
               │   switch(state)  │◄─────┐
               ├──────────────────┤      │
               │ case 1: block A  │──────┤
               │ case 2: block B  │──────┤
               │ case 3: block C  │──────┤
               │ case 4: block D  │──────┘
               └──────────────────┘

各ブロックは、ディスパッチャーに戻る前に state = next_state を設定します。

3.2 復元テクニック
テクニックツール有効性
シンボリック実行angr、Triton、miasm高 — すべての状態遷移をトレースします
トレースベースの復元Pin/DynamoRIO のトレース → CFG を再構築中 — 実行されたパスのみをカバーします
パターンマッチングカスタム IDA/Ghidra スクリプト中 — 既知のフラット化手法に有効です
D-810(IDA プラグイン)IDA Pro高 — CFF 専用に設計されています
3.3 シンボリックなデフラット化(angr の手法)
import angr, claripy

proj = angr.Project('./obfuscated')
cfg = proj.analyses.CFGFast()

# Find dispatcher block (highest in-degree basic block)
dispatcher = max(cfg.graph.nodes(), key=lambda n: cfg.graph.in_degree(n))

# For each case block, symbolically determine successor
for block in case_blocks:
    state = proj.factory.blank_state(addr=block.addr)
    # ... solve state variable to find real successor

4. MOVFUSCATOR

4.1 概要

すべての計算を mov 命令のみに還元します(メモリーマップされた計算テーブルを介してチューリング完全です)。Christopher Domas が作成しました。

4.2 識別
  • 関数に含まれるのはmov命令のみです(add、sub、xor、jmp、callは含まれません)
  • データセクションに大きなルックアップテーブルがあります
  • メモリマップドフラグレジスターがあります
4.3 デモブフスケーション
アプローチ説明
demovfuscator(ツール)静的解析を行い、movパターンから元の操作を復元します
トレース+テイント解析Pin/DynamoRIOで実行し、入力にテイントを設定して計算を観察します
シンボリック実行関数全体を制約システムとして扱います

5. VM 保護(VMProtect / Themida / Code Virtualizer)

5.1 VM アーキテクチャ
Protected code → bytecode compiler → custom bytecode
Runtime: VM entry (pushad/pushfd) → fetch → decode → execute → VM exit (popad/popfd)
5.2 VM エントリーポイントの識別
; Typical VMProtect entry
pushad                    ; save all registers
pushfd                    ; save flags
mov ebp, esp              ; VM stack frame
sub esp, VM_LOCALS_SIZE   ; allocate VM context
mov esi, bytecode_addr    ; bytecode instruction pointer
jmp vm_dispatcher         ; enter VM loop
5.3 ハンドラーテーブルの抽出
1. Find dispatcher (large switch or indirect jump via table)
2. Each case/entry = one VM handler (implements one VM opcode)
3. Map handler addresses to operations by analyzing each handler:
   - Handler reads operand from bytecode stream (esi)
   - Performs operation on VM registers/stack
   - Advances bytecode pointer
   - Returns to dispatcher
5.4 デバーチャライゼーションのアプローチ
方法説明ツール
手動でのハンドラーマッピング各ハンドラーをリバースエンジニアリングし、ISA仕様を作成しますIDA + スクリプト
トレース記録すべてのハンドラー実行を記録し、プログラムを再構成しますREVEN、Pin
シンボリックリフティングハンドラーをシンボリック実行し、IRにリフトしますTriton、miasm
パターンマッチングハンドラーパターンを既知のVMファミリーと照合しますカスタムスクリプト
5.5 VMProtect の詳細
  • ディスパッチャーで不透明述語を使用します
  • ハンドラーの変更:同じオペコードでも、ビルドごとに異なるハンドラーコードになります
  • 複数のVMレイヤー(VM内のVM)
  • アンチデバッグ機能と整合性チェックを統合します

6. 文字列の暗号化

6.1 一般的なパターン
パターン例復元方法
XORループfor (i=0; i<len; i++) s[i] ^= key;XOR関数をフックまたはエミュレートします
スタック文字列mov [esp+0], 'H'; mov [esp+1], 'e'; ...IDA FLIRT / Ghidraスクリプトで再構成します
RC4暗号化暗号化されたブロブ+バイナリ内のRC4キーキーを抽出し、オフラインで復号します
AES暗号化暗号化されたブロブ+実行時に導出されるAESキー復号後にフックします
カスタムエンコーディングBase64 + XOR + 反転デコード関数をトレースして再現します
6.2 文字列の自動復号
# Ghidra script: find XOR decryption calls, emulate them
from ghidra.program.model.symbol import SourceType

decrypt_func = getFunction("decrypt_string")
refs = getReferencesTo(decrypt_func.getEntryPoint())

for ref in refs:
    call_addr = ref.getFromAddress()
    # extract arguments (encrypted buffer ptr, key, length)
    # emulate decryption, add comment with plaintext

7. インポートの隠蔽

7.1 GetProcAddress +ハッシュ検索
FARPROC resolve(DWORD hash) {
    // Walk PEB → LDR → InMemoryOrderModuleList
    // For each DLL, walk export table
    // Hash each export name, compare with target hash
    // Return matching function pointer
}
7.2 復元
  1. ハッシュアルゴリズムを特定します(一般的なもの:CRC32、djb2、ROR13+ADD)
  2. 既知のすべてのAPI名についてハッシュを計算します
  3. ハッシュ → API名のルックアップテーブルを作成します
  4. IDA/Ghidraで解決された呼び出しに注釈を付けます
7.3 一般的なハッシュアルゴリズム
名前アルゴリズム使用例
ROR13hash = (hash >> 13 | hash << 19) + charMetasploitのシェルコード
djb2hash = hash * 33 + charさまざまなマルウェア
CRC32関数名の標準CRC32高度なパッカー
FNV-1ahash = (hash ^ char) * 0x01000193最新のマルウェア

8. 逆アセンブルを妨害する手法

8.1 手法
手法仕組み修正方法
命令の重複jmp $+2; db 0xE8(偽のcallプレフィックス)正しいオフセットから手動で再解析します
アラインメントがずれたジャンプ複数バイト命令の途中にジャンプしますIDAにターゲット位置から再解析させます
条件付きジャンプのペアjz $+5; jnz $+3(常にジャンプし、線形逆アセンブルを混乱させます)無条件のjmpに変換します
戻りアドレスの操作jmp addrの代わりにpush addr; retを使用しますpush+retをジャンプとして認識します
例外ベースのフロー例外を発生させ、ハンドラー内に実際のコードを配置します例外ハンドラーチェーンを解析します
Call + add [esp]call $+5; add [esp], N; ret(計算されたジャンプ)実際のターゲットを計算します
8.2 IDA の修正
Right-click → Undefine (U)
Right-click → Code (C) at correct offset
Edit → Patch → Assemble (for permanent fix)

9. 判断ツリー

Obfuscated binary — how to approach?
│
├─ Can you run it?
│  ├─ Yes → Dynamic analysis first
│  │  ├─ Set BP on interesting APIs (file, network, crypto)
│  │  ├─ Trace execution to understand real behavior
│  │  └─ Dump decrypted code/strings at runtime
│  │
│  └─ No (embedded/firmware/exotic arch) → Static only
│     └─ Identify obfuscation type from patterns below
│
├─ What does the code look like?
│  │
│  ├─ Giant flat switch/dispatcher loop?
│  │  ├─ State variable drives control flow → CFF
│  │  │  └─ Use D-810 or symbolic deflattening
│  │  └─ Bytecode fetch-decode-execute → VM protection
│  │     └─ Extract handlers, build disassembler
│  │
│  ├─ Only mov instructions?
│  │  └─ movfuscator → demovfuscator tool
│  │
│  ├─ XOR/ADD loop writing to .text section?
│  │  └─ SMC → breakpoint after decode, dump
│  │
│  ├─ Impossible conditions in branches?
│  │  └─ Opaque predicates → Z3 proving or pattern removal
│  │
│  ├─ Disassembly looks wrong / functions overlap?
│  │  └─ Anti-disassembly → manual re-analysis at correct offsets
│  │
│  ├─ No readable strings?
│  │  └─ String encryption → hook decrypt function or emulate
│  │
│  ├─ No imports in IAT?
│  │  └─ Import hiding → identify hash, build lookup table
│  │
│  └─ pushad/pushfd → complex code → popad/popfd?
│     └─ VM protector entry/exit → full VM analysis
│
└─ What tool to use?
   ├─ Known protector (VMProtect/Themida) → specific deprotection guide
   ├─ Custom obfuscation → combine: IDA scripting + Triton + manual
   ├─ CTF challenge → angr symbolic execution often fastest
   └─ Malware analysis → dynamic (debugger + API monitor) first

10. ツールボックス

ツール用途最適な用途
IDA Pro + Hex-Rays逆アセンブル、逆コンパイル、スクリプト作成幅広い分析
Ghidraスクリプト作成(Java/Python)に対応した無料の代替ツール低予算でのリバースエンジニアリング
D-810(IDA プラグイン)CFF の自動フラット化解除OLLVM 形式の難読化
miasmIR ベースの分析フレームワークシンボリックな難読化解除
Triton動的シンボリック実行不透明述語の解決、CFF
REVENシステム全体のトレース記録と再生VM プロテクターの分析
demovfuscatormovfuscator のリバースmov のみのバイナリ
x64dbg + プラグインスクリプト作成に対応した動的分析Windows のリバースエンジニアリング
Unicorn EngineCPU エミュレーションSMC のアンパック、シェルコード
Capstone逆アセンブルライブラリカスタムツール
IDA FLIRT関数シグネチャの照合シンボルを削除したバイナリ内のライブラリコードの特定
Binary NinjaMLIL/HLIL に対応した代替逆アセンブラー自動分析

コメント