開発
Androidゲームをチートしてみた(SECCON 2015 × CEDEC CHALLENGEにて)
beko
こんにちは、佐藤です。SECCON 2015 × CEDEC CHALLENGEは8月10日まで募集されていたゲームのクラッキングとチートのチャレンジです。
対象アプリは今回はAndroidアプリでした。私がしたチートを書きたいと思います。
[注意]今から書くことを一般のアプリに対して行った場合、犯罪になることがあります。今回は、許可されているアプリであるため検査を行いました。悪用厳禁でお願いします。
対象アプリ1つ目 「sandback」
このアプリは、下のようなサンドバッグをタップするとポイントが加算されていくゲームです。そして、右のようにランキングがでます。使っているゲームエンジンはUnity。
このゲームはぼろぼろでした。
まず、通信をのぞき見てみましたら。
httpリクエストは以下の通り。
POST /score/ranking/ HTTP/1.1
X-Unity-Version: 5.0.2f1
Content-Type: application/json; charset=UTF-8
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; SO-02G Build/23.0.B.1.59)
Host: api.sandbag2015.net
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 76
{“uuid”:”a36cfae0-1eb8-4a55-83ef-e4ac8a2fc809″,”name”:”hooters”,”point”:12345678}
これを送りつけるスクリプトにして、
#!/usr/bin/env python import urllib2 import sys argvs = sys.argv names = '"' + argvs[1] + '"' points = argvs[2] target_url = "http://api.sandbag2015.net/score/ranking/" data = '{"uuid":"a36cfae0-1eb8-4a55-83ef-e4ac8a2fc809","name":' + names + ',"point":' + points + '}' cl = len(data) heders = {'X-Unity-Version':'5.0.2f1', 'Content-Type':'application/json; charset=UTF-8', 'User-Agent':'Dalvik/1.6.0 (Linux; U; Android 4.4.4; SO-02G Build/23.0.B.1.59)', 'Host':'api.sandbag2015.net', 'Connection':'Keep-Alive', 'Accept-Encoding':'gzip', 'Content-Length': cl, } print data getpoint = urllib2.Request(target_url,data,heders) resp = urllib2.urlopen(getpoint) logs = resp.read() print logs res_head = resp.info() print res_head.getheaders("Date") print res_head.getheaders("Server") print res_head.getheaders("X-Powered-By") print res_head.getheaders("Set-Cookie") print res_head.getheaders("Expires") print res_head.getheaders("Cache-Control") print res_head.getheaders("Pragma") print res_head.getheaders("Content-Length") print res_head.getheaders("Connection") print res_head.getheaders("Content-Type")
もはやゲームしなくてもスコア好きな値にできるし他の人のクッキーも予想できそうにないので次のアプリへ・・・
対象アプリ2つ目「SUNIDRA」
このステージの奥にいるドラゴンを倒したら勝ち。残っているタイムが自分のスコア。
通信見たが暗号化されていてめんどくさいので他のことを考える。
クライアントを改竄する。
Unityアプリ内にゲームをルールなど司るバイナリをリーバースエンジニアリングする。
このバイナリの正体は他のライブラリから直接参照できる中間言語。故に、逆コンパイル、逆アセンブラが簡単。
DotNetResolverという逆コンパイラーツールをつかってます。上は、ゲームオーバー時の処理です。
とりあえず、nop(何もしない)で埋めてみました。
そして、Apk Editerというアプリを使って再ビルドします。apkを再構築するのには作者を守るため署名が必要でそれなしには偽装アプリをインストールすらできないのですがうまく偽装されています。
ゲームオーバーの処理が行われずスコアがマイナスになり(笑
これでは勝てないので、
逆コンパイラのコードを読むとGameRuleCtrlモジュールのUpdate()に時間をカウントしているところがありました。
Update関数はUnityでワンフレームごとに実行する関数です。
これを+にします。
背景がピンクなのは運営側も知っているバグ出そうです。
sub というオペコードをaddに変更します。
ドラゴンの攻撃力が尋常じゃないので受けたダメージ分自分のHPに加える用に同じ容量で変更しました。
対策されてた(笑
おそらくチェックサムによるクライアント認識。
こうなったら、プロセスメモリをいじるしかない。(諦め早いかも)
AndroidプロセスメモリエディタのGameGuardianを使って
Androidのasrlを無効(0)にして
メモリを素早く見つけるには少し慣れが必要。アドレス0007DD0に格納される000008DEがスコアに相当。
これを変更すれば反映されると思ったんだのですが。 うまくいかなかった。。。
指摘するところと言えばコピー作成が可能など。
例のぼろぼろアプリはこの方法でも改竄できました。
肝心レポートに手を抜いてしまった。ゲームをチートから守る方法が大きな点数らしいのでまずいです。
とりあえずrootの状態でゲームをさせてしまうと危険です。開発者の皆さんお気をつけください。rootだと判明したら(アプリでrootが必要なコマンドをわざと実行しErrorを得るか得ないかで判定とか)ゲームはさせない設計などもいいと思います。
以上です。