dwgx@blog:~$dwgx
> cd ../posts

VRChat の IL2CPP をリバースエンジニアリングする:静的 dumper が全滅したとき

// created

vrchat-il2cpp-re は VRChat の IL2CPP バイナリ専用の難読化解除パイプラインだ。現在はアーカイブ済みで、状態は 2026-07-01 で凍結されている。目的は単純で、GameAssembly.dll(222MB)から難読化されたコードを抜き出してリネームすること。ベースラインは Unity 6(6000.0.60f1)、クラスがおよそ 64,773 個、メソッドが 569,859 個、フィールドが 188,384 個ある。

難読化は Beebyte によるもので、識別子はすべて ÌÍÎÏ のような文字化けに書き換えられ、バージョンごとに構造体のフィールドレイアウトも並べ替えられる。さらに metadata の暗号化と IL2CPP エクスポートの strip が加わり、Il2CppDumper のような静的 dumper はそのままお手上げになる。

自分のやり方は逆方向の MethodInfo 列挙だ。ヒープ上のすべての MethodInfo をスキャンし、その klass を解決して、型ツリー全体を再構築する。全工程がセルフチェック付きで、ASLR のヒープセグメントは自動検出、オフセットは自己検証し、さらに ground truth と突き合わせる。たとえば Vector3 = x/y/zTransform のメソッド数だ。

最終的にクラスの 56.9% がセマンティックに命名され、これで頭打ち、構造上の天井だ。メソッドは 93.5%、フィールドは 85.1%。はっきりさせておくと、これらの名前は推論であって復元ではない。Beebyte はコンパイル時に元のクラス名を破棄してしまうので、そもそも拾える元の名前が存在しない。

パイプラインは run_full_pipeline.py の中で 5 ステージを走り切る。命名ソースを統一した語彙にマージし、11 ステージのリネームエンジンに通し、相互参照を適用し、難読化解除済みの dump、name_mapping.json、そして 1,538 個の C# ファイルからなる src/ ツリーを出力し、最後に 226K+ の関数をカバーする IDA リネームスクリプトを生成する。一回のフル実行はおよそ 30 秒。

ついでにいくつか掘り当てたものもある。ネットワーク層は Photon Realtime + FlatBuffer で、Steam → VRChat API → Photon → EAC という 4 トークンの認証チェーンになっている。EAC(EOS アンチチート)はオンライン解析だと BAN されるので、オフラインの VRChat でやるしかない。Beebyte のカスタム XOR による metadata 復号ルーチンも一緒に記録しておいた。プライベートな研究プロジェクトで、もう配布はしていない。C# 61.6% + Python 37.5%、188 個のスクリプト、IDA Pro / Ghidra / Frida と組み合わせている。