【個人開発】チート対策について

前置き

ゲーム開発者の頭を悩ませるチート。

もちろんこの世から消えてくれたらそれ以上のことはないですが、手をこまねいていても悪は滅びてくれないので開発側で対策する必要があります。

ゲーム内容が完全に個人で完結している場合は勝手にしてくれで済むところを、他人とアイテムを交換できるだとかランキング要素があるとなったら話が変わってきます。

今回は個人で開発を進めているゲームにランキング要素を入れたいと思ったので、対策について手探りながらアウトプットしておきます。

開発進行中のゲームについて

詳しい内容や仕様書については未公開なので伏せますが、カジュアルゲームです。

どこかで見たようなミニゲームをいくつか実装予定で、それぞれハイスコアとランキングくらいは保存した方が良いなと考えています。

スコアやランキングによっての報酬は存在せずあくまで自己満足なものなので、最悪チート対策を入れなくても問題ありませんが、せっかくなので何かしら対策を講じようと思いました。

そして調べてみたところ選択肢が色々あったので、取捨選択も含めてこちらの備忘録とさせていただきます。

対策方法について

・IL2CPP

IL2CPP の概要 - Unity マニュアル
IL2CPP (Intermediate Language To C++) スクリプティングバックエンドは、Mono バックエンドの代替品です。IL2CPP は、広範なプラットフォームのアプリケーションに対し、優れたサポートを提供します。IL2CPP バックエンドは、MSIL (Microsoft Intermedia...

Intermediate Language to C++の略。
仕事での開発だと度々目にするワードですがこれって何ぞやというところから。

ILとは中間言語のこと。
IL2CPPはUnityでの開発においてC#で書いたコードをC++に変換し、その後ネイティブコードにコンパイルするという方式です。

ではそのC++への変換が何をもたらすかというと、今回の記事で関係する点としてリバースエンジニアリングが難しくなるのがメリットです。
その他、実行速度とパフォーマンスも後述のMonoより向上するそうです。

じゃあ良いこと尽くしかというとデメリットがあり、ビルド時間が長くなったり変換によってデバッグが難しくなる…まあこのくらいは許容範囲かとも思います。

逆にデフォルトはMonoと呼ばれるもので、コード実行時に必要な部分を即コンパイルする方式。
上記と逆なので製品版ビルドで選択しない方が良いですが、開発段階では基本こちらだと思います。

ただ、注意点としてコンパイル方式が異なる点、メモリ管理方法が異なる点からMonoでは起こらなかった不具合が見つかったり問題が発生する可能性があることから、開発の序盤ではMonoでスピーディに進めつつ終盤にはIL2CPPでのデバッグを厚くしておきたいです。

・アンチチートのサービス利用

これに関しては特に説明は不要な気がします。
大規模なゲームだと大抵アンチチートシステムを導入していますね。

CrackProof、EasyAntiCheat等々名前は把握していますが、個人のカジュアルゲーム開発で導入するにはコストパフォーマンスがちょっと…なところがあるので今回は見送るつもりです。

・メモリ改竄への対応

メモリ改竄は比較的簡単なチート手法らしいです。
例えばスコアを2026と表示していたとして、専用ツールを利用して2026の値が入っているメモリのアドレスを割り出して中身を書き換えるといったものです。

これに対してはゲーム次第で取れる選択肢が変わってくると思います。

まず有料アセットになりますがこちらは簡単にメモリ改竄を検知できるらしいです。
https://assetstore.unity.com/packages/tools/utilities/anti-cheat-toolkit-202695
今回は前述のとおりコスパ考慮で不採用。

ゲーム本体で対応するにあたっての方策としてはサーバ側でロジック周りを管理する。
いじられて困るものはサーバ側で管理、検証することでメモリ改竄されても見た目の変化に留まるはずです。

メモリ改竄だけで済めばいいですが、クライアントアプリへの不正をすべて対応するのは不可能に近いのでサーバ側での対応は必須と言えるでしょう。
ただ、その場合でも有効と判定される領域でのチートは防げないことには注意が必要です。

また、クライアント側での対策としてはメモリ上の値を表示している値と変えてしまうのが有効かと思います。
よくある手法としてはXOR(排他的論理和)で二度計算すると元の値に戻るのを利用するもので

  1. ランダムな値を取得
  2. メモリで保持したい値を1.のXOR(排他的論理和)で計算する
  3. 表示用に値を取り出すときは保持した値を1.のXOR(排他的論理和)で再度計算する

という手順を踏むことで、表示されている情報に対してメモリの値が都度ランダムに置き換わるので特定が困難になります。
可能であればランダムな値についても難読化しておくのが良いかもしれないですね。

今回採用する対応について

規模が大きくなく個人開発のカジュアルゲームであり、保存した情報が報酬等にも関わらないという性質上、有料系のサービスは今回遠慮しようと思っています。
また必要になった際に比較・検討した内容を備忘録にまとめます。

現状ロジック周りで入れる対応として、クライアント側のメモリ改竄対策のXOR計算、サーバ側の値管理・検証を考えています。
XOR計算に関しては今後も流用できるよう基盤でそれっぽいものを作るとして、サーバ側での検証についてはまだ模索中なのでメモ程度になりますが以下に方策を書いておきます。

まずスコアはもちろん必須で検証する。
時間制限等があるミニゲームだと理論値でどのくらいになるかを一応想定しておき、スコアがそれプラスアルファくらいに設定した閾値を超えていたら不正として返す。

それに加えて操作回数も検証を追加しようと考えています。
連射ツール等のあからさまな操作異常や、スコアに対してありえない操作回数の場合をこれで弾けるのではないかと。
パーティゲームにたまにあるただの連打ゲームだと連打の速度がやたら速い人とツールとの判定が難しいですが、今回は思考→操作が必要な内容にする予定なので一旦問題ないです。

ゲーム内容次第で他にも要素を追加しようと思いますが、一旦上記の2軸をマストで対応します。

クライアント側で上記を偽装しながら保持して必要なタイミングでサーバ側に送信。
サーバ側で人間的にありえないスコアや操作回数、理論値を超えた値を検知したときは保存せずクライアント側に不正として返す。
あくまでカジュアルなゲームなのと、人間の限界を甘く見ていた場合を考慮してふんわりとした警告を出す。
※スコアもしくは操作に不審な点が見つかりました。ツール等の使用は控えてください。もし正常なプレイでこちらが表示された場合はお手数をおかけしますがお問い合わせをお願いいたします。

こんな感じで実装してみます!

あと書き

今回は無料のカジュアルゲーム想定なのでそこまでしてチートをする輩はいないと思い記事に起こしていますが、本来チート対策をオープンにすべきではないです(当たり前だ)。
開発予定の人は参考にしつつうまくやってください。

…もしかしたらリリース後は非公開にするかも。

コメント

タイトルとURLをコピーしました