LLM4Decompile - LLMを活用したバイナリコード逆コンパイル技術
(github.com/albertan017)- LLM4Decompileは、Linux x86_64バイナリをGCC O0〜O3最適化レベルで人間が読めるCソースコードへ戻すオープンソースの大規模言語モデルプロジェクト
- アプローチは、バイナリをObjdumpでアセンブリに変換した後、LLMがCコードへ逆コンパイルする流れで、Ghidra出力の擬似コードを整形するLLM4Decompile-Ref系統も提供
- モデルは1.3B〜22B規模で公開されており、llm4decompile-9b-v2はDecompileベンチマークで再実行可能率64.9%を記録
- 評価指標は、逆コンパイルされたコードが事前定義されたテストを通過して正しく実行されるかを見る再実行可能率で、HumanEval-Decompileの164個のC関数とExeBenchの2,621個の関数がベンチマークとして使われる
- プロジェクトは2025年にdecompile-benchとSK²Decompileを公開し、より多くのアーキテクチャ・設定・逆コンパイルツール連携を目標に拡張中
LLM4Decompileの目的と対応範囲
- LLM4Decompileは逆コンパイルに特化したオープンソースの大規模言語モデルプロジェクト
- 現在のバージョンは、Linux x86_64バイナリをGCC最適化レベルO0〜O3の範囲で、人間が読めるCソースコードへ逆コンパイルする
- プロジェクトは、より広いアーキテクチャと設定をサポートするための拡張を進めている
- 主な利用方法は2つある
- LLM4Decompile-End: バイナリを直接逆コンパイルするモデル系統
- LLM4Decompile-Ref: Ghidraが逆コンパイルした擬似コードをLLMで整形するモデル系統
逆コンパイルの学習・評価フロー
- コンパイル過程はCソースコードから始まり、前処理、コンパイル、アセンブル、リンクを経て実行ファイルを生成する
- 逆コンパイルはこの過程を逆にたどり、バイナリコードを再びソースファイルへ変換する
- LLMはバイナリデータを直接処理できないため、まずバイナリをObjdumpでアセンブリ言語へ逆アセンブルする必要がある
- READMEでは、バイナリと逆アセンブルされたASMは相互変換可能であるため同等に扱えると説明している
- 学習では逆コンパイルされたコードと元のソースコードの間の損失を計算し、評価ではテストアサーションを通過するかで機能性を確認する
評価指標とベンチマーク
- 中核指標はRe-executability
- 逆コンパイルされたコードが正しく実行できるかを確認する
- 事前定義されたすべてのテストケースを通過するかを評価する
- HumanEval-Decompileは、標準Cライブラリのみに依存する164個のC関数の集合
- ExeBenchは、実際のプロジェクトから取得した2,621個の関数の集合
- ユーザー定義関数、構造体、マクロを含む
公開モデルと性能
- LLM4Decompileは1.3B〜33Bパラメータ規模のモデルを含み、モデルはHugging Faceで公開されている
- 主なモデルの再実行可能率は以下の通り
- llm4decompile-1.3b-v1.5: 1.3B, 27.3%
- llm4decompile-6.7b-v1.5: 6.7B, 45.4%
- llm4decompile-1.3b-v2: 1.3B, 46.0%
- llm4decompile-6.7b-v2: 6.7B, 52.7%
- llm4decompile-9b-v2: 9B, 64.9%
- llm4decompile-22b-v2: 22B, 63.6%
- V1.5系統は、15Bトークンのより大きなデータセットと最大トークン長4,096で学習され、従来モデル比で100%以上の性能向上があったとしている
- V2系統はGhidraベースで、Ghidraが生成した逆コンパイル擬似コードを整形するよう2Bトークンで学習された
- 22B-V2は6.7B-V1.5よりさらに40.1%高い性能を示したとしている
最近公開された項目
- 2025年10月4日にSK²Decompileが公開
- 第1段階のStructure Recovery、すなわちSkeleton段階では、バイナリまたは擬似コードを難読化された中間表現へ変換する
- 第2段階のIdentifier Naming、すなわちSkin段階では、意味のある識別子を持つ人間が読めるソースコードを生成する
- モデルリンク: sk2decompile-struct-6.7b, sk2decompile-ident-6.7
- 2025年5月20日にdecompile-benchが公開
- 学習用バイナリ・ソース関数ペア200万件を含む
- 評価用関数ペア7万件を含む
- 詳細はdecompile-benchフォルダにある
- 2024年10月17日にdecompile-ghidra-100kが公開
- 最適化レベルごとに25,000件ずつ、合計100,000件の学習サンプルを含む
- 単一のA100 40G GPUで約3.5時間で実行できる学習スクリプトを提供
- 迅速な再現コストは合計20ドル未満で、再実行可能率0.26を達成
- 2024年9月23日にLLM4Decompile-9B-v2が公開
- Yi-Coder-9Bをベースにファインチューニング
- Decompileベンチマークで再実行可能率0.6494を達成
利用フロー
- クイックスタートは、リポジトリの複製、Conda環境の作成、
requirements.txtのインストールで構成される - 前処理段階では、CコードをGCCでバイナリへコンパイルした後、
objdump -dでアセンブリ命令を抽出する - 関数名は例の
func0ではなく、逆コンパイルしたい関数名に置き換える必要がある - 入力アセンブリは次の形式を想定している
<FUNCTION_NAME>:- 続く複数行のアセンブリ命令
- 逆コンパイル段階では、
transformersのAutoTokenizerとAutoModelForCausalLMでHugging Faceモデルを読み込み、アセンブリプロンプトからCコードを生成する - Dockerの利用も可能
- イメージをビルドした後、GPUオプション付きでコンテナを実行する
ghidraディレクトリでdemo.pyを実行するフローが提供される
HumanEval-Decompileのデータ形式
- HumanEval-Decompileデータは
llm4decompile/decompile-eval/decompile-eval-executable-gcc-obj.jsonにJSONリスト形式で保存されている - サンプル数は164個の関数にO0, O1, O2, O3最適化レベルを掛けた164*4件
- 各サンプルは5つのキーを持つ
task_id: 問題IDtype: O0, O1, O2, O3のいずれかである最適化段階c_func: HumanEval問題のC解答c_test: Cテストアサーションinput_asm_prompt: アセンブリ命令とプロンプト
- 評価スクリプトはevaluationフォルダにある
進行中の項目とライセンス
- 進行中の項目には、より大きな学習データセットと整理過程、人気の高い言語・プラットフォーム・設定対応、実行ファイル対応、Ghidra・Rizinのような逆コンパイルツール統合が含まれる
- より大きな学習データセットと実行ファイル対応は、2024年5月13日に完了した項目として表示されている
- コードリポジトリはMIT LicenseとDeepSeek Licenseでライセンスされている
- 論文はarXiv:2403.05286にあり、プロジェクトはColabとYouTube資料も提供している
1件のコメント
Hacker News のコメント
興味深いアイデアだが、結果が信頼できるのかが気になる。
再コンパイルすると別の機械語が出る可能性があるためハルシネーションを見分けにくく、特にコードの中核になり得る新しい構造で静かに失敗するのではないかと心配している。
生成方式で実行する際に、LLM が特定区間の確信度もあわせて報告する方法があるのか気になるし、結局は人間の確認が必要になりそうだ。
バイナリをソースに逆コンパイルした後、再びバイナリにコンパイルしたときに元のバイナリが得られるべきで、損失が許容できる水準まで下がるまで繰り返せばよい。
こうした問題には強化学習が非常によく合っており、実際にこの種の問題で異常なほど効果的だと知られている。
最近よくある「とりあえず LLM を使おう」というミームを探る実験ではあるかもしれないが、既存の逆コンパイラがはるかに少ない計算量ですでにより良くできている点のほうが大きな反論になる。
その後、検証ツールで LLM が提供した証明に従って結果が正しいか確認できる。
もちろん、そのような証明を作れる LLM を構築して学習させることのほうが大きな難題だろうが、ハルシネーションを安全に捕捉する方法にはなり得る。
したがって、その数個の関数だけを再コンパイルすればよい。
アプリケーションを作った開発者を知っているなら、彼らの過去のコードを学習データとして使い、逆コンパイルモジュールを訓練できるのかは興味深い。
例えば Super Mario 64 と Zelda 64 は完全に逆コンパイルされており、他の N64 ゲームも進行中なので、両ゲームに関わった開発者をマッピングし、誰がどのモジュールを作ったのかまで推定して、他のゲームの逆コンパイルに活用できるのか気になる。
これが本当に良くなれば、PC 内のすべてのバイナリブロブを解読してドライバを公開し、OS まで開け放つような生活も夢見られる。
Linux に満足せず、Windows XP を復活させて現代的なセキュリティとアプリ互換性をバックポートし、Microsoft の Windows 11 はそのままにしておく、という想像もできる。
LLM が既存の逆コンパイラと同じことをできるなら、弁護士はそれを同等の手続きと見る可能性が高い。
核心的な問題は技術ではなく、法的・政治的な問題だ。
実際に有用に使われた事例はよく知らないが、個人のコーディングスタイルがコンパイル過程を経た後にも残り、互いのコンパイル済みプログラムを区別できるという点は面白い。
結果は元のものにかなり似るだろうが、多くのコードスタイル要素は失われ、残って見えるスタイルもおおむねハルシネーションに近いだろう。
公開されている C コードから大量の入力/出力ペアのデータセットを作るのは簡単なので、これはLLM のファインチューニングに非常に良いユースケースだ。
通常は合成学習データの品質が大きな懸念になるが、ここではコードがコンパイルされるという事実が重要だ。
結果図の再実行可能性の数値を正しく読めているなら、アイデアは素晴らしいが、実際にはうまく機能していないようだ。
https://raw.githubusercontent.com/albertan017/LLM4Decompile/...
補足すると、再実行可能性は意味的正確性を測る中核的な指標だ。
逆コンパイル結果を再コンパイルしてテストケースを実行し、プログラムのロジックと動作が保たれているかを評価するもので、再コンパイル可能性と再実行可能性はそれぞれ構文の復元と意味の保存を表す。
この問題は少なくとも二つの面で興味深い。
第一に、理想的な逆コンパイラはプロプライエタリなソースコードの意味を弱める可能性がある。
第二に、公開されている C コードが豊富なので、アセンブリとソースコードのペアデータセットを簡単に作れ、最適化レベル・コンパイラ・プラットフォームも多様にできる。
ただ、なぜ著者たちが DeepSeek-Coder をファインチューニングしたのかは気になる。
似たデータセットで LLM をゼロから学習できるのか、どの程度の規模が必要なのか、ローカル実行が可能なのかも気になる。
やりたい作業が初期モデルとそれほど近くなくても、ランダム初期化より事前学習済みモデルから始めるほうが、ほぼ常に望ましい。
コンパイラが情報を失うため、ある意味では決して存在し得ないし、「結果コードの高水準な理解」という寛容な観点で見ても、これはコンピュータセキュリティ分野における汎用人工知能級の問題だ。
まだ誰もこれに近づけていない。
Llama2 は2兆トークンで開発されたが、このデータセットは約40億トークン規模だ。
適切なモデルサイズも単純には決めにくく、実験では70億パラメータモデルが21%の実行可能性を示した一方、10億パラメータモデルは10%にとどまった。
ただし再コンパイル可能性は両者でかなり近い。
10億パラメータモデルは最低 2GB の GPU メモリが必要で、ほとんどの GPU で可能であり、70億モデルは 14GB が必要なので 3090/4090 系に適している。
330億モデルは単一カードなら A100 80GB が選択肢で、技術的には MacBook でも可能だろうが、実際に使いたいとは思わないはずだ。
アイデアを検証するための出発点でもあるのかもしれない。
Pythonバイトコード向けのLLMベース逆コンパイラ https://github.com/kukas/deepcompyle を作っている
この研究方向に取り組んでいる人は多くないようだが、特に長い注意コンテキストが可能になりつつある今は、かなり面白いものになり得ると思う
この分野を扱うチームを知っている人がいれば、協業に興味がある
経験上、Pythonバイトコードは十分に高レベルなので、ソースコードへ直接変換できる
Pythonには大規模なオープンソースライブラリエコシステムがあるが、バイナリ形式で配布されるソフトウェアに多く使われているとは思わない
LLMベースなのかもはっきりしない
バイナリにコンパイルされるPythonプロジェクトは多くなさそうだ
こういうものを試そうと計画していた
いつか誰かがバイナリ入力 → 良いソースコード出力のパイプラインを突破するだろうが、まだ数年はかかりそうだ
この問題の先に大金が積まれているようには見えないのでそう思うが、間違っているかもしれない
良い暫定的なアプローチは、Ghidraをヘッドレスモードで動かす逆コンパイルパイプラインを作り、逆コンパイラの厳密な構文正確性とLLMの直感的能力を組み合わせることだ
AlphaGeometryのように、逆コンパイラとLLMが互いの弱点を補完する必要がある: https://deepmind.google/discover/blog/alphageometry-an-olymp...
また、AICIのようなものを接着剤として使い、Cソース生成を調整する方式が必要だ: https://github.com/microsoft/aici
LLMの重みを文法的に正しいCソース生成に使うより、変数名・スニペットパターン・アーキテクチャ選択を考えさせ、GhidraやLLVMのようなツールに残りを任せるほうが望ましい
やや手振りに近い元大学院生の安楽椅子コメントではあるが、この研究者たちが飛び込んだのは素晴らしいし、著者らが今後の作業でGhidra統合に言及しているのを見ると、方向性は合っているように思う
60億モデルが330億モデルよりうまくやる点が興味深い
330億モデルにはより多くの学習データが必要という意味なのか気になる
約100万個のCプログラムで事前学習されたものと、数兆トークン規模で学習されたDeepSeek-Coderを比べると、データ量に数桁の差がある
LLMではない解法と比べるとどうなのかも気になる
ほとんどのLLMは大きく過小学習されており、70億モデルは主流モデルの中ではまだ過小学習が少ないほうなので、LLMファインチューニングコミュニティで広く使われるようになった
標準方式のまま単純にファインチューニングするナイーブなファインチューニングでは大きなモデルの学習は難しく、データ量だけでなく、データ精製・学習率・減衰といったあらゆる要素が最終性能に影響する
そのためには、それらのCプログラムの平均サイズが200万トークンより数桁小さいと仮定しなければならないが、実際にそうであり得るとしても、かなり楽観的な仮定に聞こえる
成功するなら、コンパイラの機械語を1:1で複製することになるのか気になる
そうだとすれば、完全なコードが潜在空間内に確率分布として存在し得るという意味になる
あるいは、より可能性が高いのは、ロジックだけを複製してから対象言語へ翻訳する形かもしれない
コンパイルに非決定的な入力、たとえばキーやハッシュが必要なバイナリは壊れそうだ
本当に興味深い
GPT-4が比較でいまだにかなり健闘しているのが驚きだ
このモデルよりコンパイル可能なコードははるかにうまく作るが、動作が正しいコードを再現する正確度は低い
それでもかなり印象的だ
モデルを改善中なので、アップデートを引き続き見守ってほしい