
2026/02/17 3:19
**LLM 支援による逆コンパイルのロングテール**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
(欠落した詳細を組み込む):**
デコンパイル作業は、約 25 % の一致コードから 75 % へと進展し、最初のワンショットデコンパイル後に ~58 % の中間ピークがありました。進捗は、大きくてグラフィックスを多用する関数の小規模なセットで停滞しており、逆解析が困難です。
ワンショットデコンパイルは最初にカバレッジを向上させましたが、残ったタスクが難しいと判定したロジスティック回帰モデルによる関数優先度付けは失敗しました。アセンブリ命令のテキスト埋め込みを用いた関数類似性優先化に切り替えることで、進捗が大幅に改善されました。手作業で構成された複合スコアリングと Coddog のレーベンシュタイン距離ベースのオペコード距離という二つの類似性手法は 90.6 % のケースで異なる最も類似した候補を選択したため、両方が現在採用されています。
“gfxdis.f3dex2”(マイクロコード逆アセンブル)などの Claude スキルが追加され、ノイズが多く不安定な出力だったデコンパイル・パーミュテーターは後に削除されました。手動または Claude による自動クリーンアップループは、関数をより慣用的にし、類似性ベースのスケジューリングを支援することでマッチ品質を向上させました。
ワークツリーは別々のコードコピーで同時エージェントが作業できるようにし、変更の衝突を防ぎます。Claude フックはガードレール(例:SHA1 ハッシュ編集のブロック、テストスキップの禁止、特定スクリプトへのビルド制限)を強制します。Nigel the Cat は設定ファイルで長時間タスクを調整し、リアルタイムモデル出力と繰り返しプロンプトをサポートし、ワークツリー間で作業をシャーディングします。「Glaude」はルーチンクリーニング/ドキュメントタスクを安価な GLM バックエンドに振り分け、Opus トークンは難しい問題に留めます。
1 月初時点では 157 個 の関数が残っていました;現在は 124 個 に減少し、大きい(> 1,000 命令)、グラフィックスを多用する、そして数学的に負荷の高いルーチンが難易度を支配しています。プロジェクトのワークフローツール―スキル、類似性スコアリング、フック、オーケストレーション、モデルルーティング―は総合してデコンパイル曲線を平坦化しますが、残りの難しいケースには更なる最先端モデルが必要です。
本文
前回の投稿では、コーディングエージェントを使って Nintendo 64 のゲームを逆コンパイルし、ワンショットでの逆コンパイルが非常に有効だったことについて説明しました。その手法のおかげで Snowboard Kids 2 の逆コンパイルは急速に進み、マッチしたコードの割合が約 25 % から 58 % に上昇しました。しかしその後進捗は劇的に鈍化し、ワークフローを大幅に変更せざるを得ませんでした。変更を加えた結果、逆コンパイルはおよそ 75 % 程度まで達しましたが、その先で再び停滞してしまいました―今回は本当に止まったのかもしれないと考えているものの、まだ証明される可能性も残っています。
この投稿では、プロジェクトが成熟するにつれてワークフローがどのように進化したか、何が役立ち、現在どこで行き詰まっているかを説明します。これらの観察が他の逆コンパイルプロジェクトにも有用になることを願っています。
類似関数を優先する
逆コンパイルは時間とトークンを消費するため、どの未マッチ関数に取り組むかの選択は非常に重要です。
- 元々のアプローチ – 推定難易度(命令数や制御フローの複雑さに対してロジスティック回帰)で優先順位を決める。初期はうまくいったものの、残りがすべて難しい状態になってしまいました。
- 埋め込みによる類似性 – Macabeus はアセンブリ命令のテキスト埋め込みを作成し、高次元潜在空間で近傍関数をクエリしました。Claude も同様に関数の類似性を認識でき、パターンを再利用できると示唆していました。
未マッチ関数に対して類似した既存関数を算出するツールを書き、エージェントループで「類似(既にマッチ済み)な相手がある」関数を優先させました。これが非常に効果的で、多くの未認識だった類似性が Claude の試行を導きました。
関数類似度の算出
ベクトル埋め込みは高速検索に向いていますが、候補は数千程度しかなく、クエリ自体が時間的に厳密ではありませんでした。したがって、未マッチ関数同士をすべて対比し、正確な類似度を算出する方針に切り替えました。これは試行ごとのトークンコストを考えると妥当です。
まずは手作業で複合スコア(命令 n‑gram、制御フローパターン、メモリアクセスオフセット、構造指標)を組み合わせましたが、後から見ると過剰設計でした。そこで Coddog を使用しました。これは opcode シーケンスに対して境界付き Levenshtein 距離を高速に計算し、0–1 の範囲で正規化します。
残った未マッチ関数については、Coddog と私の手法が 90.6 % のケースで最も類似した候補を異なるものとして選択しました。両方とも使い続けていますが、お互いに補完的であり、どちらかが絶対に優れているわけではありません。
専用ツール
プロジェクトでは Claude のスキルを数多く使用しています。その中でも特に有効だったものは次の二つです:
- gfxdis.f3dex2 – ヘクサ値を F3Dex2 コマンドへ逆アセンブルします。
- decomp‑permuter – 小規模な変異を膨大に試し、100 % のマッチを狙います。
F3Dex ツールとドキュメント
N64 の Reality Display Processor(RDP)はマイクロコードで動作します。 Snowboard Kids 2 は Nintendo の F3Dex2 ライブラリを使用しているため、API を逆解析するだけで済みました。
Claude に対し F3Dex2 コマンドの参照と gfxdis.f3dex2 ツールを提供したことで、F3Dex2 コードの認識・逆コンパイルが格段に向上しました。
パーミュータ
パーミュータは「盲目的に数百万回小さな変異を試みる」手法です。理論上は LLM を補完しますが、実際には不自然なコード(論理的でない変数再利用、
do {} while (0) ループ、入れ子代入)を生成し、Claude がノイズに対処するために多くのトークンを消費してしまいます。数回試した結果、パーミュータは除外しました。時折勝利があったものの、クリーンアップコストや不安定さが正当化できませんでした。
クリーンアップとドキュメント
コードの整形・文書化はマッチ率を直接向上させるわけではありませんが、以前は脆弱だった(ポインタ算術、変数置換)関数を安定した実装へ移行し、類似ベースのスケジューリングに有効な例を増やしました。
Claude をループで走らせ、一度に一つの関数だけを整形させました。これによりプロジェクト構造の文書化も進みました(すべてがどのように機能するかを書き下すことは Claude にとっても必須です)。
副作用として、チートコードシステムを文書化する過程で未知のチートコードを発見しました。
スケーリングと新ワークフロー
継続的な逆コンパイル作業に加え、非逆コンパイルタスクへの拡張がリソース・安定性・タスクオーケストレーションの課題となりました。以下の四つの変更でスケールを維持できました:
- Worktrees – 複数エージェントの同時作業を容易にします。
- Agent hooks – 破壊的・無駄な操作を制限します。
- Nigel the Cat – タスクオーケストレーションを向上させます。
- Glaude – より多くのトークンを確保します。
Worktrees
複数タスクが衝突しないように、独立したコードベース(ワークツリー)を使用しています。私はエージェントを三つの別々のワークツリーと、人間作業用メインブランチで走らせています。
Claude Hooks で安全策強化
自動化は Claude が数時間見逃してしまうミスを生むリスクがあります。フックにより、特定アクション(ファイル編集など)の前にコードを実行し、以下のような制限を設けています:
- SHA1 ハッシュの変更をブロック
- コミット時にテストスキップを防止
以外でのビルドを禁止build-and-verify.sh- 自動生成ファイルへの編集を停止
これにより、無駄や破壊的操作が大幅に減少しました。ただし完全ではなく、Claude はまだ粘り強いです。
Nigel the Cat によるタスクオーケストレーション
長時間実行されるエージェントループは不可欠でした。古い
run.py を独自リポジトリ Nigel に切り出し、タスクを設定ファイルで表現しました:
candidate_source: | grep -o '.name = .*"' src/main.c | sort | uniq prompt: | Look up the modelEntityConfigs entry or entries where `$INPUT` in src/main.c. The fields compressedDataStart/compressedDataEnd/displayListStart/displayListEnd contain hex addresses. These hex addresses should be asset entries. Look for the appropriate entry in assets.h/snowboardkids2.yaml. If the entry is not present, you will need to add it. In both cases, ensure that it has a semantically appropriate name (based on the asset name, e.g. TOWN_DISPLAY_LIST). Commit your changes when you're done.
Nigel はスクリプト名で検索し、適切に処理します:同一入力は二度実行されず、有効な変更だけがコミットされます。
Nigel の主な機能
- 非対話モードでもリアルタイムモデル出力
で現在のタスク終了後に安全停止Ctrl-\- ビルトイン並列処理(
)でワークツリー間の衝突を回避--shard X/Y
Ralph Wiggum
Ralph と同様、Nigel は
--repeat オプションで Claude に同じタスクを繰り返し提示できます。初期はトークン節約のため 30 回に制限しましたが、実験後は --repeat 3 を除外し、プロンプトごとの試行回数は無制限にしました。これで極端なケースに対処できつつも全体のトークン消費を抑えられます。
Glaude と GLM の寛大クォータ
残り未マッチ関数には多くの試行、途中出力、リファクタリングが必要で、Claude のトークン予算を圧迫します。GLM(z.ai から)は料金が安く、クォータも寛大です。そこで glaude を作り、Claude と同じインターフェースで GLM バックエンドへ静かに転送します。
主にメカニカルなタスク(クリーンアップ、リファクタリング、ドキュメントループ)には glaude を使用し、難易度の高い作業だけを Claude に任せます。完全ではありませんが、Opus のトークンを真価ある作業に集中させることでシステム全体を持続可能にしています。
現状
すべてのエンジニアリング(類似スコアリング、スキル、フック、オーケストレーション、モデルルーティング)を終えた結果、1 月初めに曲線は平坦化しました:
- 157 個の関数が残っていました。
- 継続作業で 124 へと減りましたが、ダイナミクスは根本的に変わりました。
三つの要因が主導しています:
- 大きな関数(1,000 命令を超える) – Claude は即座に諦めることがあります。
- グラフィック中心の関数 – マクロでディスプレイリストを構築する方法は LLM を深く混乱させます。
- 数学関数(行列/ベクトル変換)– 短いものでも Claude を混乱させることがあります。
Nigel the Cat はまだ稼働中で、マッチングは次世代モデルが登場するまで難しくなります。
手伝ってもらえますか?
ここまで読んでくださった方なら、逆コンパイルと Snowboard Kids 2 に興味を持っていることでしょう。Snowboard Kids 2 逆コンパイルプロジェクトをご覧になり、Discord でご協力いただける場合はぜひお声掛けください。また Bluesky でも更新情報を発信していますので、フォローも歓迎です。