「IL2CPPでビルドすれば安全」— その誤解が招く危険
「IL2CPPに切り替えれば、コードがネイティブバイナリになってリバースエンジニアリングが難しくなる」— 多くのUnity開発者が一度は耳にしたアドバイスです。確かに、Monoビルドと比べてIL2CPPは解析の敷居を上げます。しかしIL2CPPが「解読不可能」かというと、全くそうではありません。
IL2CPPビルドには必ずglobal-metadata.datという補助ファイルが生成されます。このファイル一つで、クラス構造・メソッド名・フィールド名など、C#コードのほぼ全貌が復元可能です。Il2CppDumperのような公開ツールを使えば、経験の浅い攻撃者でも数時間でゲームの内部構造を把握できます。
この記事では、IL2CPPビルドが実際にどのようにリバースエンジニアリングされるか、その仕組みと限界、そして有効な防御戦略を解説します。
Il2CppDumperとglobal-metadata.datによるリバースエンジニアリングの流れ
攻撃者がIL2CPPビルドを解析する際、一般的に以下の手順を踏みます。
APK/IPAを展開 ──> libil2cpp.soとglobal-metadata.datを抽出
──> Il2CppDumperで解析 ──> クラス・メソッド構造を復元
──> IDA Pro/Ghidraでバイナリ解析 ──> セキュリティロジックを特定・パッチ
Step 1: ファイルの抽出
AndroidのAPKファイルは実質的にZIPアーカイブです。展開するだけでlibil2cpp.so(ネイティブコード)とglobal-metadata.dat(メタデータ)を取り出せます。iOSのIPAも同様の構造です。
Step 2: Il2CppDumperによるメタデータ解析
Il2CppDumperはGitHub上に公開されているツールで、libil2cpp.soとglobal-metadata.datを入力として受け取り、元のC#コードの構造を示すダンプファイルを生成します。クラス名・メソッド名・フィールド名・継承関係まで、ほぼ全てが復元されます。
Step 3: IDA Pro/Ghidraでのバイナリ解析
復元されたシンボル情報をIDA ProやGhidraにインポートすることで、ネイティブコードの解析が飛躍的に効率化されます。攻撃者はCheckSpeedHack()やVerifyPurchase()といったセキュリティ関連メソッドを素早く特定し、その実装を解析できます。
Step 4: パッチ適用とリパッケージング セキュリティチェックのロジックを理解した後、攻撃者はそのチェックをスキップするようにバイナリをパッチし、再署名して配布します。これがMOD APKの主要な作成手法の一つです。
IL2CPPの限界と誤解
IL2CPPは確かにMonoに比べて解析を困難にしますが、以下の重要な限界があります。
global-metadata.datは暗号化されていない
デフォルトのUnityビルドでは、global-metadata.datは平文のバイナリファイルです。Il2CppDumperのようなツールがこのファイルのフォーマットを完全に解析しており、特別な知識なしに構造を復元できます。
ネイティブコードでも構造は推測可能 C++にコンパイルされていても、シンボル情報が復元できれば、関数の入出力とその振る舞いから実装の意図は読み取れます。特にゲームロジックのような比較的単純な処理は、リバースエンジニアリングが容易です。
難読化なしでは「隠れていない」のと同じ
コードの難読化を施さないIL2CPPビルドは、メソッド名がそのまま残ります。CheckHack()という名前のメソッドは攻撃者にとって格好のターゲットです。
有効な防御戦略
IL2CPPの限界を踏まえた上で、実践的な防御層を積み重ねることが重要です。
(1) global-metadata.datの保護と難読化
global-metadata.dat自体を暗号化・難読化し、実行時に動的に復元する方式を採用することで、Il2CppDumperによる直接解析を防ぎます。カスタム暗号化キーを用いることで、公開ツールによる自動解析が不可能になります。
(2) ビルド完全性の検証 配布バイナリのハッシュ値を実行時に検証し、改ざんされたビルドを検出します。これにはメインの実行バイナリだけでなく、重要なアセットファイルも含まれます。
(3) Nativeレイヤーへのセキュリティロジック分離 チートの検出ロジックをC#スクリプトに書いた場合、Il2CppDumperによって名前が特定され、ターゲットにされます。OZero SecurityはすべてのコアセキュリティロジックをNative C++レイヤーで実行し、IL2CPPとC#スクリプトから完全に分離しています。
(4) ランタイム環境の整合性検証 デバッガーのアタッチ、フッキングフレームワーク(Frida等)の存在、改ざんされたモジュールのロードをランタイムで継続的にチェックします。静的な解析対策だけでなく、動的な実行時保護が不可欠です。
OZero Securityは、IL2CPPビルドに対する追加的な保護層として、ビルド一意性(Native Variant)機能を提供します。各アプリビルドでセキュリティバイナリの構造が異なるため、あるゲームへのチートが他のゲームへの汎用バイパスとして流用されることを防ぎます。
まとめ
- IL2CPPは解析の敷居を上げるが、
global-metadata.datの存在により構造の完全な復元が可能。 - Il2CppDumperのような公開ツールにより、専門知識がなくてもリバースエンジニアリングが実行可能。
- 防御の本質は「難読化+ランタイム完全性検証+Nativeレイヤー分離」の組み合わせ。
- IL2CPPは出発点であり、それだけでは十分な保護にならない。
「IL2CPPだから安全」という誤った安心感が、ゲームを最もリスクにさらす先入観です。