スピードハック一つがゲーム経済を崩壊させる
放置系RPGで速度を上げたユーザーが、わずか1日で通常ユーザー数か月分の資源をかき集めます。リズムゲームやアクションゲームのランキング上位が、不自然なプレイ時間とスコアで埋め尽くされます。対戦ゲームでは一人だけ1.2倍速く動くだけで、公平なマッチメイキングはその場で崩れます。
スピードハック(SpeedHack)は最も一般的でありながら、最も過小評価されているチートです。メモリ改ざんのように特定の値を直接いじるのではなく、ゲームが認識する時間の流れそのものを丸ごとねじ曲げるからです。「資源獲得量の検証」や「移動座標の検証」といった事後の防御線では、この時間の歪みを根本的に捉えるのは難しく、被害は売上減少・ユーザー離脱・ランキング信頼性の低下へと広がります。
本記事では、スピードハックがUnityエンジンの背後で実際にどのように時間を操作するのか、よく試みられるC#レベルの防御がなぜ簡単に破られるのか、そしてNative(C++)層でどう検知すべきかを実戦的な観点から見ていきます。
スピードハックは何を操作するのか — 仕組み
Unityゲームのコアループは「前のフレームからどれだけ時間が経過したか」を絶えず計算します。Time.deltaTimeがまさにその値で、移動速度・クールダウン・物理・アニメーション・資源の蓄積など、ほぼすべてのシステムがこれに依存します。
スピードハックの原理は単純です。この時間測定値を人為的に膨らませたり縮めたりして、エンジンに「実際より多くの時間が経過した」と錯覚させるのです。鍵となるのは、Unityが時間を自ら生成せず、OSのタイマーAPIを呼び出して取得している点であり、攻撃者はその呼び出しを横取りします。
[ 通常の時間の流れ ]
OSハードウェアタイマー ──> OSタイマーAPI ──> Unity(Time.deltaTime) ──> ゲームロジック
[ スピードハック適用時 ]
OSハードウェアタイマー ──> OSタイマーAPI ──> [スピードハックのフック: ×10] ──> Unity ──> ゲームロジックが10倍に加速
1) PC(Windows)
Cheat Engineなどのスピードハックは、ゲームプロセスにDLLをインジェクションした後、時間測定に使うAPIをフックします。代表的なのはQueryPerformanceCounter、timeGetTime、GetTickCount / GetTickCount64です。フックされた関数は本来のティック値に倍率(例:×10)を掛けて返し、ゲームは正直にAPIを呼んだだけなのに、1フレームの間に10倍の時間が流れたと認識します。
2) モバイル(Android)
GameGuardianなどのツールもメカニズムは同じです。libcのgettimeofday・clock_gettimeといった関数を横取りしたり(PLT/GOTフック)、アプリを仮想空間(Virtual Space)内で実行して時間の基準そのものを操作します。root環境ではさらに低い層まで介入することもあります。
どのプラットフォームでもパターンは同じです。信頼の連鎖の最下層(OSタイマー)をねじ曲げ、その上に積み上がったエンジンとゲームロジック全体を一度に欺くのです。
よく試みられるC#レベル防御の限界
スピードハックに直面したチームは、たいていC#レベルで自前の防御を組み込みます。方向性は正しいのですが、実戦では次の限界にぶつかります。
1) Time.deltaTimeとTime.unscaledDeltaTimeの比較 — 無意味
どちらも同じOSタイマーから派生します。タイマーがフックされると両方の値が同じ比率で歪むため、差は出ません。「操作された2つの時計」を突き合わせて正常か確認するようなものです。
2) C#の時計との照合(DateTime.UtcNow、Stopwatch) — 解析で除去される
別の基準と照合する発想は正しいです。問題は、検査コードがマネージドコード(C#)に露出している点です。IL2CPPでビルドしても、global-metadata.datとIl2CppDumperでクラス・メソッド構造がかなり復元され、攻撃者は検知メソッドを見つけて分岐をNOP/JMPパッチで無力化します。
// 攻撃者はIL2CPP解析でこのメソッドを見つけ、
// 分岐を実行しないようバイナリをパッチする。
bool CheckSpeedHack() {
if (MySystemTime != UnityTime) {
return true; // <-- この分岐を飛ばすようにJMPパッチ
}
return false;
}
3) サーバー側タイムスタンプ検証 — 強力だが全区間には適用不可 クライアントのアクション間隔をサーバー時刻と照合するのは確実な最終防御線です。しかし、すべてのシングルプレイ要素、放置進行、細かな動きまでサーバーで検証するのは帯域・演算コストが大きく、オフライン対応ゲームには適用できません。
要するに、攻撃はOSタイマーが動くNative層から入ってくるのに、監視の目がその上のC#マネージドコードに留まっていれば、攻撃者に先に主導権を渡すようなものです。
Native(C++)層でスピードハックを検知する方法
安定した検知のためには、攻撃者が歪める層と同じか、それより低い領域で独立した監視体制を確保する必要があります。核心は3つの軸です。
(1) フックに強い低レベル独立クロックの確保
チートが主に狙う高レベルAPI(QueryPerformanceCounterなど)から離れ、より低い時間源を直接読んで基準を作ります。Windowsではcpuサイクルカウンタ(rdtsc)やユーザー領域で読めるシステム時刻フィールド、Androidではライブラリインターフェースを迂回したsyscall経路の単調クロック(CLOCK_MONOTONIC / CLOCK_BOOTTIME)が例です。この基準とゲームが使う時間の流れの比率を相互検証し、閾値以上ずれたら時間軸の操作と判定します。
ただし
rdtscも万能ではありません。ハイパーバイザベースのツールはRDTSC自体をトラップ・仮想化でき、アプリが仮想空間内で実行されると直接syscallもコンテナに横取りされる可能性があります。そのため低レベルクロックは「高レベルAPIフックに強い一つの軸」と捉え、下記の(2)(3)およびサーバー側の信頼時刻と組み合わせて堅牢にすべきです。
(2) 時間APIのメモリフック痕跡を直接検知
倍率を掛けるには、時間関数のプロローグを改ざんしてジャンプ(JMP、例:0xE9)させるか、関数アドレステーブル(IAT/EAT)を書き換える必要があります。Native検知は主要な時間関数の先頭バイトが改ざんされていないか、アドレスが非正規のモジュールを指していないかを点検し、ツールの痕跡を直接捉えます。
(3) セキュリティロジックのC#デカップリング
最も重要な設計原則です。検知ループ・クロック比較・対応ロジックは、C#メモリやIL2CPP成果物に露出してはいけません。核心ロジックを難読化されたNative C++バイナリ(.dll / .so)の独立スレッドで実行すれば、攻撃者がC#全体を解析しても監視者の位置を突き止められず、解析・パッチの難易度が飛躍的に上がります。
OZero Securityのスピードハック対応
Native アンチチートを自前で開発するには、OS内部の深い理解、マルチプラットフォーム対応、頻繁なOS更新への保守が必要で、中小スタジオには大きな負担です。OZero Securityは、Unityにすぐ適用できるNative C++ベースのアンチチートを提供します。
- 信頼時刻ベースの検証(OZeroの核心的な差別化): ローカルクロックだけに依存せず、複数のwebTimeエンドポイントをラウンドロビン+クォーラム(quorum)で照合し、信頼できる基準時刻を確立します。自社運用のwebTimeエンドポイントを登録することもでき、ローカルタイマーが操作されても外部の信頼時刻との不一致で時間軸の操作を判別します。
- Native C++検知: 低レベルクロックの相互検証と時間APIフック痕跡の検知をマネージドコードの外で行います。
- 性能設計: セキュリティロジックをC#ではなくNative C++層で扱い、保護値はGCアロケーションが発生しないよう設計されています。
- 柔軟な対応: 検知時の動作はポリシーで設定可能です — ログ記録、機能制限、プロセス終了、または暗号化された検知ログをサーバー/バックオフィスへ送信してリアルタイム制裁(Pro Add-onのテレメトリ)。
- 導入: コードを書かずに、
.unitypackageをインポートし、ダッシュボードでモジュールをトグルし、Configを保存する方式で適用します。
リアルタイム検知デモ
OZero Securityがスピードハックをリアルタイムで検知する実際の動作です。
まとめ
- 時間を盗むチート: スピードハックは個別の値ではなくシステム全体の時間軸を欺き、経済と公平性を麻痺させます。
- C#セキュリティの限界: IL2CPPを経ても、C#に露出した検査ロジックは解析ツールの前で無力化されます。
- 王道の解法: 低レベル独立クロック、時間APIフック痕跡の検知、検査ロジックのNative分離を組み合わせ、ローカルクロックの限界はサーバー側の信頼時刻(webTimeクォーラム)で補完すべきです。
スピードハック一つを防ぐにも、OSアーキテクチャからアセンブリ層、リバースエンジニアリング回避まで膨大な知識が要ります。チートパターンとの終わりなき追走に疲れたなら、実証済みのソリューションでゲーム開発本来の価値に集中することをご検討ください。