vm-and-bytecode-reverse
カスタムVMおよびバイトコードのリバースエンジニアリング手順書。CTFチャレンジや保護されたソフトウェアが、独自バイトコード・ディスパッチャループ・迷路型チャレンジといったカスタム仮想マシンを実装している場合に使用します。
description の原文を見る
>- Custom VM and bytecode reverse engineering playbook. Use when CTF challenges or protected software implement custom virtual machines with proprietary bytecode, dispatcher loops, or maze-style challenges.
SKILL.md 本文
SKILL: VM & Bytecode Reverse Engineering — Expert Analysis Playbook
AI LOAD INSTRUCTION: Expert techniques for reversing custom virtual machines and bytecode interpreters. Covers dispatcher identification, opcode mapping, custom ISA reconstruction, disassembler/decompiler writing, maze challenges, and real-world VM protector analysis. Base models often fail to recognize the fetch-decode-execute pattern or attempt to analyze VM bytecode as native code.
0. RELATED ROUTING
code-obfuscation-deobfuscationVM がコマーシャルプロテクター (VMProtect/Themida) である場合symbolic-execution-toolsangr を使用して VM ベースのチャレンジを解く場合anti-debugging-techniquesVM がアンチデバッグチェックを含む場合
Quick identification
| Binary Pattern | Likely VM Type | Start With |
|---|---|---|
while(1) { switch(bytecode[pc]) } | Switch ベースディスパッチャー | 各ケースを操作にマッピング |
テーブル経由の間接ジャンプ jmp [table + opcode*8] | テーブルベースディスパッチャー | ジャンプテーブルをダンプ、ハンドラーを分析 |
| バイト値の if-else 連鎖 | If-chain ディスパッチャー | switch と同じ、構文が異なるだけ |
| スタック push/pop が支配的な操作 | スタックベース VM | push、pop、算術操作を識別 |
reg[X] = ... 配列操作 | レジスター基盤 VM | レジスターインデックスを操作にマッピング |
| 2D グリッド + 方向入力 | 迷路チャレンジ | グリッドを抽出、BFS/DFS を適用 |
1. CUSTOM VM IDENTIFICATION
1.1 Structural Indicators
VM Architecture Components:
┌─────────────────────────────────┐
│ Bytecode Program (data section)│
├─────────────────────────────────┤
│ Program Counter (pc/ip) │
│ Register File / Stack │
│ Memory / Data Area │
├─────────────────────────────────┤
│ Dispatcher Loop │
│ ├─ Fetch: opcode = code[pc] │
│ ├─ Decode: lookup handler │
│ └─ Execute: run handler │
└─────────────────────────────────┘
1.2 IDA/Ghidra Signatures
Switch ディスパッチャー (CTF で最も一般的):
while (running) {
unsigned char op = bytecode[pc++];
switch (op) {
case 0x00: /* nop */ break;
case 0x01: /* push imm */ stack[sp++] = bytecode[pc++]; break;
case 0x02: /* add */ stack[sp-2] += stack[sp-1]; sp--; break;
// ...
case 0xFF: /* halt */ running = 0; break;
}
}
テーブルディスパッチャー (より最適化):
typedef void (*handler_t)(vm_ctx_t*);
handler_t handlers[256] = { handle_nop, handle_push, handle_add, ... };
while (running) {
handlers[bytecode[pc++]](&ctx);
}
2. ANALYSIS METHODOLOGY
Step 1: ディスパッチャーを見つける
探すべき特徴:
- ループ内の大きな switch ステートメント (多くのケース)
- データバッファからバイト単位で読み取られたバイトでインデックス付けされた関数ポインター配列
- 高い循環複雑度を持つ単一関数
- データバッファをバイト単位で読み取る相互参照
Step 2: オペコードを操作にマッピング
各ケース/ハンドラーについて、以下を判定します:
| プロパティ | 識別方法 |
|---|---|
| オペコード値 | ケース番号またはテーブルインデックス |
| 操作タイプ | レジスター/スタック変更 |
| オペランド数 | オペコード後に消費されるバイト数 |
| オペランドタイプ | 即値、レジスターインデックス、メモリアドレス |
| 副作用 | 出力、メモリ書き込み、フラグ変更 |
Step 3: バイトコードプログラムを抽出
# バイナリからの典型的な抽出
import struct
with open('challenge', 'rb') as f:
f.seek(bytecode_offset)
bytecode = f.read(bytecode_length)
# または IDA から:
# bytecode = idc.get_bytes(bytecode_addr, bytecode_len)
Step 4: カスタムディスアセンブラーを書く
OPCODES = {
0x00: ("nop", 0), # (mnemonic, operand_bytes)
0x01: ("push", 1), # push immediate byte
0x02: ("pop", 0),
0x03: ("add", 0),
0x04: ("sub", 0),
0x05: ("xor", 0),
0x06: ("cmp", 0),
0x07: ("jmp", 2), # jump to 16-bit address
0x08: ("je", 2),
0x09: ("jne", 2),
0x0A: ("mov", 2), # mov reg, imm
0x0B: ("load", 1), # load from memory[operand]
0x0C: ("store",1), # store to memory[operand]
0x0D: ("print",0),
0x0E: ("read", 0), # read input
0xFF: ("halt", 0),
}
def disassemble(bytecode):
pc = 0
while pc < len(bytecode):
op = bytecode[pc]
if op not in OPCODES:
print(f" {pc:04x}: UNKNOWN {op:#04x}")
pc += 1
continue
mnemonic, operand_size = OPCODES[op]
operands = bytecode[pc+1:pc+1+operand_size]
operand_str = ' '.join(f'{b:#04x}' for b in operands)
print(f" {pc:04x}: {mnemonic:8s} {operand_str}")
pc += 1 + operand_size
disassemble(bytecode)
Step 5: ディスアセンブルされたプログラムを分析
カスタムディスアセンブリを使用して、標準的なリバースエンジニアリングを実施:
- 入力読み取りの識別 (read opcode)
- 入力から比較へのデータフロー追跡
- 成功/失敗条件の判定
- チェックロジックの抽出 (多くの場合、定数と比較される入力の XOR/ADD 変換)
3. COMMON VM PATTERNS IN CTF
3.1 スタックベース VM
操作はスタック上で機能します (JVM または Python bytecode のような)。
| オペコード | 操作 | スタック効果 |
|---|---|---|
| PUSH imm | 即値をプッシュ | [...] → [..., imm] |
| POP | トップをポップ | [..., a] → [...] |
| ADD | トップ 2 つを加算 | [..., a, b] → [..., a+b] |
| SUB | 減算 | [..., a, b] → [..., a-b] |
| MUL | 乗算 | [..., a, b] → [..., a*b] |
| XOR | ビット単位 XOR | [..., a, b] → [..., a^b] |
| CMP | 比較 | [..., a, b] → [..., (a==b)] |
| JMP addr | 無条件ジャンプ | 変更なし |
| JZ addr | トップがゼロの場合ジャンプ | [..., a] → [...] |
| トップを文字として出力 | [..., a] → [...] | |
| READ | 文字をスタックに読み込む | [...] → [..., input] |
| HALT | 実行停止 | - |
3.2 レジスターベース VM
操作はレジスターインデックスを使用します (x86、ARM のような)。
| オペコード | フォーマット | 操作 |
|---|---|---|
| MOV r, imm | 0x01 RR II II | reg[R] = imm16 |
| MOV r1, r2 | 0x02 R1 R2 | reg[R1] = reg[R2] |
| ADD r1, r2 | 0x03 R1 R2 | reg[R1] += reg[R2] |
| SUB r1, r2 | 0x04 R1 R2 | reg[R1] -= reg[R2] |
| XOR r1, r2 | 0x05 R1 R2 | reg[R1] ^= reg[R2] |
| CMP r1, r2 | 0x06 R1 R2 | flags = compare(r1, r2) |
| JMP addr | 0x07 AA AA | pc = addr |
| JE addr | 0x08 AA AA | if equal: pc = addr |
| LOAD r, [addr] | 0x09 RR AA | reg[R] = mem[addr] |
| STORE [addr], r | 0x0A AA RR | mem[addr] = reg[R] |
| SYSCALL | 0x0B | reg[0] に基づく I/O 操作 |
| HALT | 0xFF | 停止 |
3.3 Brainfuck 風 / エソテリック VM
| BF コマンド | VM 同等物 | 説明 |
|---|---|---|
> | INC ptr | データポインターを右に移動 |
< | DEC ptr | データポインターを左に移動 |
+ | INC [ptr] | ポインターのバイトをインクリメント |
- | DEC [ptr] | ポインターのバイトをデクリメント |
. | OUTPUT [ptr] | ポインターのバイトを出力 |
, | INPUT [ptr] | ポインターに入力バイトを読み込む |
[ | JZ forward | バイトがゼロの場合、] より先にジャンプ |
] | JNZ back | バイトが非ゼロの場合、[ に戻る |
4. MAZE CHALLENGES
4.1 Identification
- バイナリが方向入力 (WASD、矢印キー、UDLR) を読み取る
- データセクション内の 2D 配列 (壁、パス、スタート、ゴール)
- x,y 座標による位置追跡
- 特定座標でのゴール条件
4.2 Map Extraction
# バイナリデータセクションから迷路グリッドを抽出
MAZE_ADDR = 0x601060
WIDTH = 20
HEIGHT = 15
# バイナリダンプから:
maze = []
for row in range(HEIGHT):
line = ""
for col in range(WIDTH):
cell = bytecode[MAZE_ADDR + row * WIDTH + col - base_addr]
if cell == 0: line += "." # path
elif cell == 1: line += "#" # wall
elif cell == 2: line += "S" # start
elif cell == 3: line += "E" # end
else: line += "?"
maze.append(line)
print(line)
4.3 自動解法
from collections import deque
def solve_maze(maze, start, end):
"""方向文字列を返す BFS ソルバー."""
rows, cols = len(maze), len(maze[0])
directions = {'U': (-1, 0), 'D': (1, 0), 'L': (0, -1), 'R': (0, 1)}
queue = deque([(start, "")])
visited = {start}
while queue:
(r, c), path = queue.popleft()
if (r, c) == end:
return path
for name, (dr, dc) in directions.items():
nr, nc = r + dr, c + dc
if (0 <= nr < rows and 0 <= nc < cols and
maze[nr][nc] != '#' and (nr, nc) not in visited):
visited.add((nr, nc))
queue.append(((nr, nc), path + name))
return None
# スタートとゴール位置を見つける
for r, row in enumerate(maze):
for c, cell in enumerate(row):
if cell == 'S': start = (r, c)
if cell == 'E': end = (r, c)
solution = solve_maze(maze, start, end)
print(f"Path: {solution}")
4.4 Direction Encoding
チャレンジによって方向の エンコーディング方法が異なります:
| エンコーディング | 上 | 下 | 左 | 右 |
|---|---|---|---|---|
| WASD | W | S | A | D |
| UDLR | U | D | L | R |
| 矢印キー | ↑ (0x48) | ↓ (0x50) | ← (0x4B) | → (0x4D) |
| 数字 | 1 | 2 | 3 | 4 |
| 16進オペコード | 0x01 | 0x02 | 0x03 | 0x04 |
5. REAL-WORLD VM PROTECTORS
5.1 VMProtect Analysis Approach
1. VM エントリーを見つける: pushad/pushfd シーケンスを検索
2. VM コンテキスト構造を識別 (レジスター、フラグ、バイトコードポインター)
3. ハンドラーテーブルを特定 (多くの場合、不透明述語で難読化)
4. 各ハンドラーについて:
a. ジャンクコード / 不透明述語を削除
b. コア操作を識別
c. ハンドラーセマンティクスをドキュメント化
5. バイトコード実行をトレース (命令レベルトレース)
6. トレースから元のコードを復元
5.2 Tigress Obfuscator
カスタマイズ可能な保護レイヤーを備えたアカデミック VM オブファスケーター。
| 特徴 | アプローチ |
|---|---|
| シングルディスパッチ VM | 標準的なハンドラー抽出 |
| 分割ハンドラー | ハンドラーが複数の関数に分散 |
| ネストされた VM | 外部 VM ハンドラーが内部 VM を呼び出す |
| 暗号化バイトコード | 各フェッチ前の動的復号化 |
| ポリモーフィックハンドラー | 各ビルドで同じ操作に異なるコード |
5.3 Common VM Protector Patterns
| プロテクター | ディスパッチャースタイル | 難易度 |
|---|---|---|
| VMProtect | テーブル + 不透明述語 | 高 |
| Themida (Code Virtualizer) | CISC 風、大規模ハンドラーセット | 高 |
| Tigress | カスタマイズ可能、アカデミック | 中程度~高 |
| カスタム CTF VM | シンプル switch | 低~中程度 |
| Movfuscator | すべて-mov 計算 | 中程度 |
6. TOOLS
| ツール | 目的 | 使用法 |
|---|---|---|
| IDA Pro | ディスパッチャーを識別、ハンドラーをリバース | F5 デコンパイル、xref 分析 |
| Ghidra | 無料の代替案 (Sleigh プロセッサーモジュール付き) | VM ISA 用カスタムプロセッサーを作成 |
| angr | VM を通じたシンボリック実行 | 全体を制約システムとして扱う |
| Pin / DynamoRIO | トレース用動的インストルメンテーション | オペコードハンドラー実行シーケンスを記録 |
| REVEN | フルシステムトレースレコーディング | VM 実行をリプレイして分析 |
| Unicorn | VM 実行をエミュレート | 高速ハンドラーエミュレーション |
| Miasm | IR ベースの分析 | VM ハンドラーを IR に lift |
| カスタム Python | ディスアセンブラー/デコンパイラーを作成 | チャレンジごとのカスタムツーリング |
Ghidra Sleigh Processor Module
繰り返される VM アーキテクチャーについては、Sleigh プロセッサー仕様を書きます:
define space ram type=ram_space size=2 default;
define space register type=register_space size=1;
define register offset=0 size=1 [ R0 R1 R2 R3 FLAGS PC SP ];
define token opcode(8)
op = (0,7)
;
:NOP is op=0x00 { }
:PUSH imm is op=0x01; imm { SP = SP - 1; *[ram]:1 SP = imm; }
:POP is op=0x02 { SP = SP + 1; }
:ADD is op=0x03 { local a = *[ram]:1 (SP+1); *[ram]:1 (SP+1) = a + *[ram]:1 SP; SP = SP + 1; }
7. DECISION TREE
バイナリにはカスタムバイトコードインタープリターが含まれていますか?
│
├─ ディスパッチャーを識別できますか?
│ ├─ はい (switch/table/if-chain)
│ │ ├─ 少数のオペコード (< 20) → シンプル CTF VM
│ │ │ ├─ スタックベース → push/pop/算術操作をマッピング
│ │ │ ├─ レジスターベース → mov/add/cmp 操作をマッピング
│ │ │ └─ ディスアセンブラーを作成 → プログラム分析 → 解法
│ │ │
│ │ └─ 多数のオペコード (50+) → コマーシャルプロテクター
│ │ ├─ 既知のプロテクター → 特定の脱プロテクションツールを使用
│ │ └─ カスタム → 実行をトレース、ハンドラーパターンマッチング
│ │
│ └─ 明確なディスパッチャーなし
│ ├─ すべて-mov 命令 → movfuscator
│ ├─ 暗号化バイトコード → 復号化を見つけ、デコード後にダンプ
│ └─ 分割/分散ハンドラー → 実行をトレースしてハンドラーを探す
│
├─ 迷路チャレンジですか?
│ ├─ データセクションからグリッドを抽出
│ ├─ 方向エンコーディングを識別
│ ├─ BFS/DFS で最短経路を見つける
│ └─ パスを期待される入力形式に変換
│
├─ VM に入力検証がありますか?
│ ├─ 小さな入力スペース → Unicorn エミュレーション経由でブルートフォース
│ ├─ 既知の形式 → 制約付き angr 解法
│ └─ 複雑なチェック → ディスアセンブラーを作成、チェックロジック分析
│
└─ 複数の VM レイヤー (VM の中の VM)?
├─ 外部 VM を最初に分析
├─ 内部バイトコードを抽出
├─ 内部 VM の分析を繰り返す
└─ 考慮: シンボリック実行はネストされた VM を直接処理できる場合があります
8. CTF SOLVING WORKFLOW
1. バイナリを実行 — I/O 動作を理解
└─ どのような入力を予期していますか? 成功/失敗時の出力は?
2. IDA/Ghidra で開く — メインループを見つける
└─ switch または間接ジャンプを持つ while/for ループを探す
3. VM コンポーネントを識別:
├─ バイトコード位置 (プログラムデータはどこ?)
├─ PC/IP 変数 (現在位置はどのように追跡?)
├─ レジスター/スタック (VM 状態はどこに保存?)
└─ I/O ハンドラー (どのオペコードが入力を読む/出力する?)
4. すべてのオペコードをマッピング (ISA 仕様を作成)
└─ 各ケース/ハンドラーについて: オペコード番号、操作、オペランド
5. Python でディスアセンブラーを作成
└─ バイトコード用の読み可能なアセンブリを出力
6. ディスアセンブルされたプログラムを分析:
├─ 入力読み取りを見つける
├─ 入力に適用される変換をトレース
├─ 期待値との比較を見つける
└─ 有効な入力を見つけるために変換を逆転
7. 解法:
├─ シンプルな変換の場合 (XOR、ADD) → 手動で逆転
├─ 複雑な場合 → Z3 に制約として入力
└─ 迷路の場合 → グリッドを抽出、経路探索を実行
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- yaklang
- リポジトリ
- yaklang/hack-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/yaklang/hack-skills / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。