アンチチート

DLLインジェクションとフッキングとは?— プロセス侵入とNativeレイヤー防御

「外部プログラムが見当たらないのにチートが動いている」— 見えない侵入者

壁越しに敵をロックオンするエイムボットや、クールタイムゼロでスキルを連発するプレイヤー——怪しい外部プロセスを探しても何も見つからない。高度なチートが見かけ上の外部プログラムなしに動作しているとき、攻撃者はすでにゲームプロセスの内部に陣地を築いている可能性が高いです。

現代の高度なチートツールは、外側からゲームのメモリを読み書きするだけではありません。悪意のあるコードをゲームプロセスに直接埋め込み、ゲーム自身にチートを実行させます。

これがDLLインジェクションファンクションフッキングの本質です。コードがゲームの外ではなく内側から動作するため、単純なバックグラウンドプロセススキャンやC#レベルの検出ロジックでは捕捉が困難です。この記事では、インジェクションとフッキングが実際にどのように機能し、システムレベルでどう対策するかを解説します。

DLLインジェクションの仕組み

DLLインジェクションは、攻撃者が作成したコード(チートロジック)を含む.dllファイルを、実行中のターゲットプロセス(ゲーム)のメモリ空間に強制ロードする技術です。インジェクションが成功すると、悪意のあるコードはゲーム自体と同等のメモリアクセス権限と実行権限を持ちます。

攻撃者がDLLを準備 ──> ゲームプロセス内に強制ロード・実行
                           └──> ゲームメモリへの直接読み書きアクセス
                           └──> ゲーム内部関数の呼び出し・フック能力

インジェクションの技術的手法は複数あります——スレッドハイジャック、CreateRemoteThreadの悪用など——しかし結果は同じです:攻撃者のコードがゲームの一部になります。OSの視点から見ると、このコードはゲームクライアントが正当に実行しているスレッドと区別がつかないため、表面的なプロセス監視では検出できません。

インジェクション後 — ファンクションフッキングによるゲームロジックの操作

DLLのインジェクションに成功した後、攻撃者がゲームの流れを制御するために最もよく使う手法がファンクションフッキングです。ターゲット関数のエントリーポイントを横取りし、ゲームがその関数を呼び出したとき、元のロジックの代わりに(または前に)攻撃者のコードが実行されるようにします。

アセンブリレベルの典型的なインラインフックは次のようになります。

[元の関数エントリーポイント]          [フック後]
push rbp              →          jmp <攻撃者チートコード>   ← 先頭命令が置換
mov rbp, rsp                     ...
...                               (攻撃者コードが実行され、返るか強制終了)

CheckSpeedHack()のようなセキュリティ関数の先頭数バイトが攻撃者のコードへのjmp命令に書き換えられると、ゲームがこの検出関数を呼び出すたびに、本来のチェックがスキップされ「問題なし」というレスポンスが返ります。

Cheat Engineの高度な機能や、Fridaのような動的インストルメンテーションフレームワークがこの原理で動作しており、エイムボット、スキルクールタイム除去、課金確認バイパスを実現します。

C#レイヤー防御の構造的限界

多くのUnity開発者は、使い慣れたC#スクリプトにチート検出と防御ロジックを書きます。しかしDLLインジェクションの前では、このアプローチには根本的な脆弱性があります。

インジェクションされた悪意のあるDLLは、C#仮想マシン(MonoまたはIL2CPP)より深いレイヤー——OSに隣接するNativeレベル——で動作できます。C#コードが実行される前にプロセスの制御を奪い、インラインフッキングでC#スクリプトに書かれた「番人」(検出関数)を盲目にできます。

つまり、C#スクリプトに検出ロジックを置いても、より低いレイヤーに侵入した攻撃者はその検出ロジックを先に見つけて無効化できます。防御コードを攻撃者が簡単に解析できる場所に置くこと自体が、構造的な盲点です。

Nativeレイヤーでのインジェクション・フッキング抑止

インジェクションとフッキング攻撃を有効に検出・抑止するには、検出ロジック自体を攻撃者の解析ツールが簡単に到達できないレイヤーに置く必要があります。

(1) ロード済みモジュールリストのスキャン ランタイムで定期的に、ゲームプロセスのメモリに読み込まれているDLLモジュールのリストを検査します。正規配布ビルドに存在するはずのモジュールのホワイトリストと実際のロードリストを比較し、未承認・不明なモジュールを特定します。

(2) 関数プリアンブルの完全性検証 配布時に重要なゲームロジックとセキュリティ関数のエントリーポイント(先頭数バイト)の状態を記録し、ランタイムで再読み込みして比較します。インラインフッキングはこれらのエントリーポイントバイトを書き換えなければ機能しないため、数バイトの改ざんでも精密な完全性検証で捕捉できます。

(3) デバッガー・フッキングツール環境の検出 Cheat Engine、Frida、x64dbgなど著名な解析・フッキングツールが残す痕跡——デバッガーのアタッチ状態、特定のドライバー、メモリハンドルなど——を確認します。チートツールの存在自体を早期検出シグナルとして使用し、ゲームロジックが改ざんされる前に対応します。

(4) 検証システムをNativeレイヤーに隔離 最も重要なのは、上記の全検出機能をC#ではなく難読化されたNative C++レイヤーに置くことです。番人の場所と動作をIL2CPPとC#スクリプトから切り離すことで、攻撃者が防御を理解・バイパスするために必要な時間とコストが指数関数的に増加します。

OZero Securityは、不正インジェクションモジュール検出、関数完全性検証、フッキングツール環境検出をNative C++レイヤーで完全実行します。全検出ロジックは難読化されたバイナリ内に安全に収められ、C#レイヤーから完全に分離されています。Plus Add-onのアプリ別Native Variantにより、セキュリティバイナリの構造がビルドごとに異なり、あるゲームへのハック手法が他のゲームに流用されることを防ぎます。

まとめ

  • DLLインジェクションは攻撃者のコードをゲームプロセス内部に直接埋め込み、外部プロセスのスキャンだけでは検出不可能にする。
  • 埋め込まれたコードはゲームと同等の権限を持ち、フッキング——関数エントリーポイントの書き換え——によって検出ロジックを無効化し、エイムボット等の高度なチートを実行する。
  • C#レイヤーに書かれた防御コードは、Nativeレベルのインジェクション・フッキングに対して構造的に脆弱。 番人自身が最初の改ざんターゲットになる。
  • 有効な防御には、不正モジュールスキャン・関数完全性検証・解析ツール環境検出を組み合わせ、全セキュリティロジックをNativeレイヤーに安全に隔離することが必要。

現代のチートは外からドアをノックしない。プロセスの内側に既に居るとき、微細な変化に気づく精密なセンサーが必要です。

OZero SecurityのNativeレイヤーでプロセスレベルの侵入を検出し、インジェクションとフッキングをブロック。

あわせて読みたい