Flashゲームのチート対策

Flashゲームでチートを行う人はかなり多くいます。カジュアルゲームの場合、チートをされたところで金銭的な被害や信用問題などにつながるわけでもないので特にどうということもないのですが、やはりランキングなどにおかしな値が入ると他のプレイヤーの印象が悪くなります。ここではカジュアルゲーム向けの簡単なチート対策を紹介します。

以下の対策は前提としてswfが暗号化されている必要があります。swfは容易にデコンパイル可能なので、暗号化ソフトを使って難読化しないとどのような対策を行ってもソースから解析されて意味を失います。暗号化ソフトとして海外ではsecureSWFというソフトが良く使われています。またmochi adsも登録したswfをある程度暗号化してくれます。もちろん、あくまで解析が難しくなるという程度なので重要なデータの取扱には向きません。しかし、カジュアルゲームの為にわざわざ難読化されたソースを解析する人は少数派でしょうから、チート対策としてはかなりの抑止力を持ちます。

以下によく行われるチートとその対策を列挙します。

SharedObjectを書き換える

SharedObjectは無防備な状態でプレイヤーのPCに保存されるので、ちょっとしたツールで書き換え可能です。
これを防ぐには、SharedObject読み込み時に最大値、最小値などの値チェックをしっかりと行うのがもっとも重要です。そうすればあまりにも制作者の意図とかけはなれた動作を防ぐことができます。さらに厳密に値を保持したい場合は、SharedObject自体を保存時に暗号化しておくか、値を保存するときにmd5ハッシュなどを一緒に保存しておき、読み込み時に整合性チェックを行ないます。例えば「100」という値を保存するときには一緒にmd5(100+”文字列”)としてもとめたハッシュを保存し、読み込み時に同じ方法でハッシュを作成しチェックを行ないます。”文字列”の部分がわからないと整合性のあるデータが作れないため、ソース解析を行わない限りチートが不可能になります。md5やその他の暗号化やハッシュアルゴリズムについてはネット上にいくつもActionScriptによるコードが公開されているのでそれを利用すると簡単に実装できます。

サーバーへの送信データを偽造する

知識があればswfからサーバへ送信されるランキングなどのデータを偽造するのは容易です。そして、それを利用してランキングを荒らされる場合があります。
対策としては送信データ自体を暗号化する、送信データを使ったハッシュを同時に送る、などがあります。SharedObjectの場合と同様、送るデータ+何か文字列、としてそれをインプットとしたハッシュをサーバに送り、サーバ側で整合性をチェックします。こうするとやはり偽造にはソースの解析が必要となりデータを作成するのが難しくなります。

実行中のプログラムのメモリを書き換える

実行中のゲームの表示されている値をプログラムのメモリから検索し、それを書き換えるツールがあります。それを使われるとスコアやライフなどといった重要な値を自由に変更されてしまいます。おそらくこれが今一番よく行われているチートです。
対策としてはまずは値を検索されないように表示されているのとは違う形で保持するという方法があります。
例えばスコアを

private var _score:Number = 0;
private var n:Number = Math.random();
public function set score(value:Number):void{
    _score = n * value;
}
public function get score():Number{ return( _score / n );}

とすると、表示されているスコアscoreと内部で保持されているスコア_scoreが未知の乱数n倍だけ異なるので、検索が不可能となります。この方法で画面に表示されている値全てを隠蔽すればメモリの書き換えが出来なくなります。ただし、少し負荷がかかってしまうのと、演算による数値誤差がでる可能性があるのが難点です。

他にはメモリを2重化する方法もあります。例えば

private var _score:Number = 0;
private var _scoreSec:Number = 0;
private var n:Number = Math.random();

public function set score(value:Number):void{
    if(_scoreSec == _score*n){
        _scoreSec = n*value;
        _score = value;
    }else{
        trace(“cheat!”);
        /*チート検出時の処理*/
    }
}
public function get score():Number{ return( _score );}

とします。つまり、_score,_scoreSecという2つのメモリを用意し、値が書き換えられるたびにそれらの整合性をチェックします。整合性が失われた場合にはどちらかのメモリが不正に書き換えられたことを意味します。その場合には、たとえばroot.visible=falseなどとして、ゲームを続行不可能にしたりします。この方法だとそのままの形で値が保持されているので誤差は発生しません。またチートを検出できるので、例えばチートが行われた場合はランキングの登録だけを阻止する、ということが可能です。しかし、負荷はかかります。

2つ紹介しましたが前者のほうがおそらく実用的です。数値演算の誤差についてもアルゴリズムを工夫すれば避けられるでしょうし、そもそもそれほど大きな誤差にはならないので無視してこのまま使っても多くの場合問題にならないでしょう。

後者の方法は例えばどれくらいの人がチートをするかという情報が得たい場合には、チート処理としてサーバに情報を送ればよいので便利です。チートを行う人の割合や傾向を知れば、今後の対策に生かせるかもしれません。

これ以外にはmochi adsのAPIはMochiDigitsと言うセキュアなメモリを扱うクラスを提供しているので、それを使えば簡単にチートを防ぐことが出来ます。

おわりに

最初に書いたようにチート自体は制作者にとって何か意味を持つような行為ではありません。ただ、ランキングなど、他のプレイヤーの目に触れる場所にその結果が投稿されるのが問題です。ゆえに、チート対策は面倒という場合は、サーバ連動するようなゲームを作らないのが一番です。またここで紹介した方法はswfを解析すれば破られてしまいます。完全にそれらを防ぐにはサーバ側で処理を行うしかありません。
とはいえ、これらの対策を破るのはよほどチートに情熱を傾け、そのための時間のある人であり、そのような人物はほとんどいないと期待できます。ゆえにこれらの対策はカジュアルゲームに対しては十分機能するでしょう。

Share

コメントする