
Ghidraを使用したAI支援リバースエンジニアリング
Rev·Deck は、ローカルで動作するシングルユーザー向け静的解析ワークステーションです。エビデンスを最優先する Web UI と、ヘッドレス Ghidra サービスで解析されたバイナリを対象とする LLM コパイロットを組み合わせています。関数、文字列、インポート、クロスリファレンス、限定されたコールグラフといった確定的なエビデンスを直接参照したり、事実主張には検査可能なエビデンスの引用を必須とする、範囲を限定した質問をアシスタントに行うことができます。
解析対象のバイナリが実行されることはありません。ブラウザはこの Flask アプリとのみ通信し、アプリは検証・型付けされたリクエストを Ghidra サービスにプロキシします。
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
cp .env.example .env # API_BASE と MODEL_NAME を設定。必要な場合は API_KEY も設定
docker compose up --build
Docker Compose は補間のために .env を自動的に読み込みます。API_BASE または MODEL_NAME が欠けていると、起動前に失敗します。API_KEY=not-used はローカル/キーレスプロバイダーでは有効です。スタックは両方のサービスを起動します。http://127.0.0.1:5000 を開いてください。
Ghidra サービスのみを実行する場合:
docker pull biniamfd/ghidra-headless-rest:latest # 最新イメージを確実に取得
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:latest
再現性のあるピンを利用するには、latest の代わりにテスト済みリリースのダイジェストを使用してください:
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3a8448d8ed969079b452306e806f36079c3ddd298f4a618d6e2f1442d
biniamfd/ghidra-headless-rest:latest.env.example を .env にコピーし、必要事項を入力してください。完全なリストとデフォルト値は同ファイルを参照してください。
Rev·Deck は OpenAI SDK を通じて OpenAI 互換の Chat Completions エンドポイント と通信します。設定はすべて API_BASE / API_KEY / MODEL_NAME で行います。プロバイダー固有のヘッダー、パラメーター、モデルロジックはありません。ローカルの Ollama サーバー(API_BASE=http://127.0.0.1:11434/v1)、セルフホストの vLLM/llama.cpp/LM Studio エンドポイント、OpenAI 自身、または OpenRouter などのゲートウェイもすべて同様に動作します。
.env プロバイダー設定の例(プレースホルダーを使用し、実際のキーをコミットしないでください):
# Ollama
API_BASE=http://127.0.0.1:11434/v1
API_KEY=not-used
MODEL_NAME=qwen3:8b
# OpenRouter
API_BASE=https://openrouter.ai/api/v1
API_KEY=replace-with-your-key
MODEL_NAME=anthropic/claude-opus-4.8
# OpenAI
API_BASE=https://api.openai.com/v1
API_KEY=replace-with-your-key
MODEL_NAME=replace-with-a-supported-model-id
# LM Studio、vLLM、または llama.cpp(ポート/モデルはサーバーに合わせて調整)
API_BASE=http://127.0.0.1:1234/v1
API_KEY=not-used
MODEL_NAME=replace-with-the-served-model-id
デフォルト(LLM_STREAM=auto)では、アシスタントはストリーム応答を要求し、トークンが到着するたびにブラウザに中継します。ストリーミングはキャンセル保証も強化します: 応答を停止したりタブを閉じたりすると、Rev·Deck はプロバイダーのストリームを直ちに閉じ、それ以上のツールやモデルのラウンドを実行しないため、バックグラウンドで完了まで実行されることなく、上流の生成が強制的に終了されます。
注意点:
auto では、プロバイダーがコンテンツやツールコール出力の前にストリームリクエストを互換性エラー(HTTP 400/404/405/422)で拒否した場合、Rev·Deck は一度だけブロッキング呼び出しにフォールバックし、以降のプロセスでそれを記憶します。認証エラー(401/403)、レート制限(429)、サーバーエラー(5xx)は互換性の問題として扱われず、エラーとして表示され、サイレントに再試行されることはありません。ストリーミングを完全にスキップするには LLM_STREAM=false を、ストリーミングを必須にするには LLM_STREAM=true を設定してください(フォールバックなし)。アプリを開き、バイナリをアップロードして解析ジョブを開始します。明らかにプレーンテキストのコンテンツは、Ghidra に送信する前に確認を求めます。実行可能形式ではなく意図的にファームウェア/データである場合にのみ、明示的な raw バイナリ上書きオプションを使用してください。解析が完了したら、2 つのワークスペースタブを切り替えます:
どちらのモードもタスクごとにステップ予算を設定でき、ステップ制限なしオプションではタスクが完了するまで実行されます(それでも MAX_STEP_BUDGET で上限が設定されるため、ループするモデルが暴走することはありません)。実行が予算に達すると、部分的な結果を報告し、「Continue」を提供します。これは、完了したツールコールをやり直すことなく、すでに取得したエビデンスを使用して同じ会話を再開します。コストはツール/モデル呼び出しの回数に応じて増加するため、予算が高いほどコストが高くなります。
利用可能なワークフロー:
各解析ジョブには、Main チャットに加えて、オプションのフォーカスされたサブスレッドがあります。New sub-investigation を選択し、1 行のブリーフィングを入力すると、同じバイナリと同一の読み取り専用ツールに対して、新しい会話コンテキストで作業できます。スレッド履歴は分離され、同時にストリーミングされるのは 1 つのスレッドのみです。
フォーカスされた作業が完了したら、Return conclusion to parent を選択します。Rev·Deck はそのサブスレッドのみに対して 1 回の制限付きモデル呼び出しを行い、そのエビデンス引用を検証し、証明マーク付きの結論カードを親に追加します。ブランチ全体は再開可能なままですが、親コンテキストにはコンパクトな結論のみが受け取られます(ブランチのトランスクリプトは受け取りません)。検証済み引用がないまま戻されたカードは、明示的に未検証とマークされます。
アシスタントの回答は、[function:0xADDR]、[string:0xADDR]、[import:name] のようにエビデンスをインラインで引用します。引用はそのターン中に実際に取得されたものと照合され、一致しない引用は「(未検証)」とフラグが立てられ、未確認の主張として扱う必要があります(事実ではありません)。
アシスタント出力内の Mermaid 図(コールグラフのスケッチなど)は、外部ネットワークアクセスのないサンドボックスフレームでレンダリングされます。
ブラウザは Rev·Deck Web アプリケーションとのみ通信します。Rev·Deck は設定された LLM とヘッドレス Ghidra サービスを調整し、結果のエビデンスとエージェントアクティビティを 1 つのワークスペースに表示します。
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
npm ci && npm run vendor # 1 回のみ: 固定された Mermaid ランタイムをベンダリング
cp .env.example .env # API_BASE / MODEL_NAME / API_KEY を編集
set -a; source .env; set +a # 通常の Python は .env を自動的に読み込まない
# 別の Ghidra サービスを起動し、次に:
python webui/app.py
http://127.0.0.1:5000 を開いてください。Docker Compose は .env を自動的に読み込みますが、ソース実行の場合は上記のようにエクスポートする必要があります。Flask 開発サーバーはローカル使用には十分です。Docker イメージは Gunicorn を実行します。
これは、自分のマシン上で作業する信頼できる 1 人のアナリスト向けに設計されています。マルチユーザーや公開ホスティング向けではありません。デフォルトでは、アプリと Ghidra サービスは 127.0.0.1 のみにバインドされ、デバッグモードはオフ、アップロードされたバイナリは決して実行されず、LLM プロバイダーのキーはサーバーサイドに留まります。
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
node --test "webui/static/js/tests/**/*.test.mjs"
npm ci && npm run vendor:verify # ベンダリングされた Mermaid バンドルの整合性を検証
/readyz が 503 を返す — Ghidra サービスが GHIDRA_API_BASE で到達不能、または API_BASE/MODEL_NAME が未設定です。API_BASE/API_KEY/MODEL_NAME を確認し、プロバイダーが LLM_TIMEOUT 内に到達可能であることを確認してください。MAX_UPLOAD_BYTES を増やしてください。ANALYSIS_TIMEOUT を増やし(例: 1 万以上の関数を含む C++/Android バイナリの場合は 5400)、再アップロードしてください。LLM_TIMEOUT は関係ありません。| 変数 | デフォルト | 意味 |
|---|
API_BASE | 必須 | OpenAI 互換のベース URL(http/https)。Compose は値がないと早期に失敗します。 |
API_KEY | not-used | プロバイダーのキー。ログに記録されたりブラウザに送信されることはありません。not-used はキーレスのローカルプロバイダーで有効です。 |
MODEL_NAME | 必須 | 設定されたエンドポイントが期待するモデル ID。Compose は値がないと早期に失敗します。 |
LLM_STREAM | auto | ストリーミング転送: auto(ストリーム、出力前の互換性エラーが一度発生した場合はブロッキングにフォールバック)、true(常にストリーム)、false(常にブロッキング)。 |
GHIDRA_API_BASE | http://127.0.0.1:9090 | Ghidra サービスのベース URL。 |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | 不変ダイジェストでピン留めされたテスト済みリリース。:latest もこのダイジェストに解決されます。別のリリースをピン留めするには上書きします。 |
HOST / PORT | 127.0.0.1 / 5000 | 開発サーバーのバインド。 |
MAX_UPLOAD_BYTES | 104857600 | アップロードサイズの上限。 |
CHATS_DIR | webui/chats | チャット履歴のディレクトリ。 |
| ワークフロー | 目的 | ターゲット関数アドレスが必要 |
|---|
program_triage | メタデータ、インポート、文字列、関数からプログラムの推定目的を要約する。 | いいえ |
suspicious_behavior | 最初に確定的な指標を表示し、その後、範囲を限定し明確にラベル付けされた仮説を提示する。 | いいえ |
selected_function | 関数を逆コンパイルし、その呼び出し元/呼び出し先とともに説明する。 | はい |
call_chain | 開始関数から、限定されたネイティブ/合成コールグラフの近傍を探索する。 | はい |
attack_surface_triage | 確定的スコアのカバレッジ/Top-K を読み取り、その後最大 3 つの候補を深く調査する。スコアは優先度であり、判定ではない。 | いいえ |
vulnerability_hypothesis | 1 つの限定された候補を選択し、エビデンス、反証、未解決の質問を提示する。決して自動確認はしない。 | いいえ |