
2026/04/29 2:41
『SimTower』のリバースエンジニアリング
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
towers.world プロジェクトは、オリジナルのエンジンソースコードへのアクセスなしで古いバイナリをリバースエンジニアリングするという課題を克服することで、伝説的なゲーム SimTower を成功裡に甦らせました。初期の試みにおいて、大規模言語モデル(LLM)が静的解析を用いて失敗しました。これは、雑多なディスアセンブルから早合点を行い、 verbose なコンテキストウィンドウにより精度を失い、上位レベルの設計と下位レベルの実装とを混同させたためです。開発チームは最終的に動的解析ワークフローへ切り替えることで成功しました。レガシーの Windows 3.1 API をエミュレートする Unicorn ベースのエミュレータを組み込むことで、自律的なコード実行ループを実現しました。LLM エージェント("Claude Code")を用いて、チームはこのエミュレータを約 30 分で構築し、8 時間動作させることに成功しました。これにより、ステートマッチングや乱数発生器のパリティに関するバグ(例:スラブアロケータの再現パターン)などを自律的に修正することが可能となりました。この実績は、雑多なディスアセンブルを持つ複雑なレガシーバイナリにおいて、AI によるリバースエンジニアリングが静的解析のみへの依存や抽象的なクリーンルーム設計に頼るのではなく、動的検証ループを最優先すべきことを示しています。これにより、特定のソフトウェアの動作を再現するために必要な時間と資源を大幅に削減できます。
本文
2026 年 4 月 28 日(火)、午後 5 時 05 分
ある日の朝、私はこう自問しました:「大規模言語モデル(LLM)が、私が愛してやまない幼少期のビデオゲームのモダンなクローン版を逆エンジニアリングすることはできるでしょうか?」そして、実際に行ってみました。本日、「towers.world」が公開され、オリジナルの完璧なクローン版における協力プレイ(Co-op 動作)を可能にしています。
クローン版とオリジナル
この回顧記事への感謝です。
本作の核心はエレベータシミュレーションにあります。塔の高さが増し、スイマー(キャラクター)たちが望む場所に移動しようと競い合うようになると、ゲームは困難さを増していきます。例えば、オフィス棟が塔を最も急速に成長させますが、それに伴って 9 時〜17 時の同期スケジュールを持つスイマーが 6 体も登場し、エレベーターに甚大な負荷をかけます。その結果、各エレベーターがどのような階へ行き、いつ行くのかを非常に厳密に管理する必要があるのです。
残念ながら、現在はエミュレータなしにはこのゲームを実際に遊ぶことがほぼ不可能になっています。もしエンジンがどこかで完全に説明されていれば再実装も可能だったでしょうが、そのような情報は存在しません。当初の私のアイデアは、LLM がバイナリを完全な仕様に逆エンジニアリングし、その仕様に基づいてゲームを再実装することでした。サイモン・ウィリソン氏のプログラムを別の言語へ翻訳するに関する投稿に触発されました。畢竟、アセンブリ言語も他のプログラミング言語の一つであり、明確に定義されたセマンティクスを持っています。そして、協力機能やより良い UI(主に部屋のグリッドを作成する機能)などの機能を追加したいと考えたのです。
そして、私はその道を選択しました。私が構築してきた一般的なバイナリ逆エンジニアリングフレームワーク「reaper」から始めました。これは静的解析のためにコーディングエージェントを囲むシンプルなハネスです。「ディスアセンブルされたコードを読み取ることで、このバイナリについて何を知ることができるか」という問いかけに対して答えを出します。
静的解析の時代
「reaper」は、プロンプトテンプレートセットと Ghidra に接続するためのスキルを持ち込み、選択した AI を使用させます。複数のプロンプトセッションを通じて、AI はバイナリに対する理解を深めていきます。最初は低レベルで容易に識別可能な関数やデータ構造から始め、それらがより高レベルのロジックを構成するようになる過程を経て、理解が積み上がります。このプロセスは私の手元でも较小的プログラムで成功しており、その出力をクリーンルーム仕様の基礎として利用しようと考えていました。
しかし、これは私の理論に過ぎませんでした。実際には、完全な仕様の複雑さを著しく過小評価していました。各部屋のスイマーには 10〜15 個の可能な状態があり、それらが持つ遷移関数は情報の精密な追跡を必要とします。また当時の最適化の要件により、ゲームはできる限り計算結果を激进的にキャッシュ・保存しており、パッキングされたビットフィールドや他の奇妙な構造体をよく使用していました。その結果でしょうか?機能するシミュレーションに近い形にはなったと言っても過言ではありませんが、プレイ可能な状態には決して至らず、ましてや振る舞いの同等性(behavioral parity)に達することはできませんでした。
静的解析を通じて、現在の AI の 3 つの具体的な失敗パターンを学ぶことができました。
第一に、AI はサブシステムについて premature な結論を drawn し、それを記録してしまう一方で、それ以前の推測をいつ放棄すべきかを見出せないまま苦悩しています。さらに深刻なのは、どの程度の詳細さ(specifcity)で記述すべきかのバランスを取るのが困難であることです。「runtime entity」といった奇妙な抽象用語を使ってスイマーを表したり、「queued-car continuation」という言葉でエレベータの列に並ぶ状態を説明したりすることが頻繁に見られました。最初のパスにおいて、マルチフロアのロビーコードを見つけ、「lower-atrium band」と命名しましたが、今なお Ghidra データベースやノート書(どこにでも散らばっています)から完全に除去できていません。
第二に、結論の記録について安易になりがちで、指示通りパラメータやローカル変数の名称付けを行うことをしばしば怠ります。私のプロンプトは極めて明確に、「関数単位で解析し、すべてのローカル変数とパラメータを命名すること」を求めています。しかし、それが実行されることはほとんどありません。モデル側はこの点において各イテレーションで徐々に向上していますが、まだ到達点には至っていません。
第三に、仕様の構築においては、シミュレーションの概念的な設計バイナリの実装詳細から切り離すことに苦戦しました。私はデータ構造のレイアウトの説明ではなく、クリーンルームの設計仕様を求めていたにもかかわらず、バイナリ詳細の含まないよう指示すると、再実装には不十分なほど抽象的な概念レベルへと移行してしまいました。
これらの課題の多くは、コンテキストウィンドウを管理するためのツールが不足していることによるものです。ディスアセンブルやデコンパイラの出力は、LLM がゼロから始めるにはあまりにも冗長です。確かに探索をより LLM ネイティブな手法にすることでこれを扱う方法があるかもしれませんが、単一の Ghidra データベースで作業する環境自体、LLM にとっては困難であると言えます。度々、LLM がコンパクト化・要約化されすぎて、直前に探査した詳細を忘却させる様子を観察させられました。テキスト処理タスクとは異なり、逆エンジニアリングでは正確な詳細情報をワーキングメモリに保持する必要があります。コンパクト化されたサマリーでは不充分です。
それでも私は諦めず進めました。LLM に仕様の洗練、誤り推測の棄却、そして書き直したシミュレーション向けのテスト構築を指導するために約 1 週間費やしました。欠落している具体的な不足点を指摘し、仕様を見直し改善するよう繰り返し求めましたが、目標に達することはできませんでした。
動的解析の時代
では次にどうすればよいでしょうか?友人から紹介されたこの 2 月の投稿が示唆的でした。そこでは SimCity の機能単位でのポート(移植)をオリジナルと比較するための入出力(I/O)比較手法について、エンジニア Christopher Ehrlich の取り組みが記されていました。Ehrlich のツイートはさらに banteg というユーザーによるゲーム『Crimsonland』の投稿を参照していました。
しかし、私は関数単位のポートを実行することに乗りたがりませんでした。まず第一に、API は著作権の対象となり得ます。バイナリと極めて類似したものをコピーすることは、クリーンルーム設計に近いアプローチよりも著作権上の問題を招く可能性があります。しかし、再実装における LLM の学習対象である「ヒル(傾斜面)」を提供するためには、元のバイナリからの何らかのフィードバックが必要であることを認識しなければなりませんでした。
直ちに直面した問題:このプロジェクトの根本的な理由は、SimTower を現代ハードウェアで実行できないことにあるためです。banteg プロジェクトが WinDbg を使用したのに対し、我々は何らかのエミュレータを必要とします。市販のホールのシステムエミュレータは最適な選択肢ではありません。なぜなら、操作系の数百万行の冗長コードの中から関心のあるプログラム動作を篩い出す必要があるからです。素早い検索が Unicorn というエミュレーションフレームワークの存在に気づかせてくれました。Unicorn は QEMU の JIT コード生成ロジックを利用し、任意のエミュレーションパイプラインを可能にするのです。
Claude Code にゲームが呼び出している Windows 3.1 API 関数の 195 つそれぞれに対して独自の実装を持つモック(マウス)を組み込んだ Unicorn エミュレータを作成させる指示を出しました。私は Claude に対するプロンプトは、ほぼ次の文章程度のものでした:「ご教授ください」。この作業には合計約半時間しかかかりませんでした。出来上がりは 99% 正解で、 loaders に存在する一つの問題点(後ほど Claude が発見し修正)を除けば完璧でした。私自身、ほとんど何も作業せずに行いました。正直に申し上げますと、ほぼゼロのガイド仅提供下でのこのような成果は、LLM の進歩に対する又一つの衝撃的な瞬間と感じています。
その後、Claude Code に複数の小規模な塔構成を指定させ、エミュレータされたバイナリ内の関数を呼び出して部屋を作成し、数ゲームティックごとに塔の状態のスナップショットを記録させるように指示しました。状態トレースを取得した以降、AI に再実装とエミュレータされたバイナリの動作間の乖離を引き起こす問題を自律的に修正させるよう指示を出しました。
もちろん、これはあらゆる詳細が精密に一致していることを確保することを意味します。特に面倒なのは、RNG(乱数ジェネレーター)や各ステップでのスイマーの処理順序です。RNG 的一致性とは、RNG を使用するすべての関数が同じ順序で実行される必要があり、例えば誤ったスイマーがディスパッチされると、連鎖的に状態が不一致となってしまいます。元のバイナリはスラブアロケータのような機構を使ってスイマーを割り当てており、一部の部屋はそのテーブルから空間を「獲得」した後、解放します。RNG 的一致性を得るためには、スイマーの反復によって誘発される RNG の呼び出しが同じ順序で発生するように、そのパターンをどこかで再現する必要があります。
そこから先は、トレーステストのカバー範囲の繰り返し改善と、Claude Code を長時間実行させるだけの作業となりました(私がプロンプトなしに最も長い実行時間は約 8 時間であり、その間に自律的に 5 つのパリティバグを修正しコミットしました)。
教訓
私がより深く進めるにつれ、ゲームはオリジナルバイナリの構造に近い再現へと収束していき、当初の「関数単位の移植ではない」という制約に違反することになりました。しかし、これは問題ありません。コメントや命名された関数・変数を備えた高レベル言語における完全な再現が得られれば、その後再び AI を使って翻訳し直すことも可能です。
素晴らしいのは、エミュレーションと状態一致(state-matching)が優れた検証ターゲットを提供できることです。トレースの一致が進むにつれ、LLM は段階的にヒルクライミングを行いながら、コードについて十分な知識を持ち、自律的にデバッグや問題解決を行うことができます。大きな教訓は一つです:これは私が何度も学び直さなければならない点ですが、複雑なタスクを実行する際には LLM を閉じたループ(closed loop)に置く必要があるということです。静的解析からのクリーンルーム設計という抽象的なタスクを LLM が確実に実行できる能力はまだ備わっていません。「改善せよ」と繰り返すだけでは問題解決にはなりません。裏側で動的解析による検証が必要です。
また、いくつか具体的なハネスパターンが役立つことがわかりました。現在の LLM を自律的に動作させるための最良の方法の一つは、長期間実行される高レベルの調整スレッドと、特定タスクのための低レベルサブエージェントを組み合わせることです。
このプロジェクトで使用されたトークン量は非常に多かったと言えます。Claude Code の $200/月のプランへアップグレードし、午前 8 時から午後 2 時のピーク使用時間(2 倍)を厳重に避ける必要がありました。自律的なヒルクライミングは LLM にとっては優れたパターンですが、安価には実行できません。LLM は実験を試み、プロジェクトから知る必要があるかもしれない情報をコンテキストウィンドウに埋めようとします。これらに対して非常に注意深く回避策を講じることができますが、多くの避けられないコストが存在します。学術文献にはディスアセンブルに関する興味深いアプローチ(例えば冗長性を減らすための独自フォーマットなど)がありますが、根本的な問題に対応するものはありません。
結論:私はこれを成し遂げました。あなたもできるようになります!マシ語からモダンな言語へコードを翻訳することが可能です。世界中にテラバイト単位の放棄されたマシネコードが存在します。初めて、それを経済的に修正・再利用できる持続可能な方法を持つツールが登場しました。これは驚異的な進歩です。
「towers.world」は現在利用可能です。ぜひプレイしてください!
補足 この全ての作業は、Recursion Center の参加者として行いました。NYC にあるプログラマのためのリトリートであり、私にとって世界で最も好きな場所の一つです。応募するか迷っている方は、ぜひ申し込んでください!