ゲームでよくされるチート手法とその対策 〜アプリケーションハッキング編〜

ゲーム、特にソシャゲ、ネトゲにおいて様々なハッキング(チート)が実際に行われます。
大きく分類すると、<font color="FF7F0e">アプリケーションハッキング</font>(クライアントサイドでのハッキング)と<font color="FF7F0e">ネットーワークハッキング</font>(サーバーへのハッキング)と<font color="FF7F0e">その他のハッキング</font>があります。
<font color="#FF0000">多くはエンジニアがよくやらかすバグであったり、知識(経験)不足を狙ってくるものです。</font>
今回は内容のボリュームの関係上、アプリケーションハッキングについてのみ、実際によく行われるチート行為やその方法、対策などについて中心に挙げていきたいと思います。

# ボタン連打
## 何が起こる?
コスト(課金石など)を払うことなく無限にアイテムが増殖する。

## やり方
![8ac9161721c15418d17e5dfd95c67ff1.png](https://qiita-image-store.s3.amazonaws.com/0/72596/4a2767bb-8ecf-07f7-55e8-aee5de904e5a.png)
上記のような「ボタン」を連打する。

## 対策
1. データベースの排他制御(トランザクション + ロック)を行う。
2. 連打ができないように一度ボタンを押したら処理が完了するまで押せないようにする。

## 具体例・解説
1.について、具体的な対策例として、以下にMySQLで実行可能なSQLを記述します。

#### トランザクション

```SQL
/* トランザクション */
BEGIN
/* INSERT文 または UPDATE文 または DELETE文 などの更新系SQLを記述 */
...
/* 更新したデータを反映してトランザクションを終了したい場合はCOMMIT トランザクションスタート前の状態に戻したい場合はROLLBACK*/
COMMIT
/* または */
ROLLBACK
```

#### 悲観的ロック
```SQL
/* トランザクション + 悲観的ロック */
BEGIN
SELECT ... FOR UPDATE
/* INSERT文 または UPDATE文 または DELETE文 などの更新系SQLを記述 */
...
COMMIT
/* または */
ROLLBACK
```

楽観的ロックなどそのほかのSQLについての詳細については[こちら](https://www.slideshare.net/takukobayashi560/ss-76804739)を参照
![データベース入門](https://cdn.slidesharecdn.com/ss_thumbnails/random-170609162201-thumbnail-4.jpg?cb=1497031952)

## 被害例(参考)
 1. [「探検ドリランド」でまた複製技が発覚　グリー、一部トレード機能を停止](http://blogos.com/article/36121/)

上記の他にも最近のゲームにおいて、多数の発生ケースが多くあります。

# デコンパイル(逆コンパイル)
## デコンパイルとは?
逆コンパイルとも言います。
アプリの実行ファイルからソースコードの形式に戻すことを<font color="#FF0000">「デコンパイル」</font>といいます。デコンパイルすることでサーバーにアクセスしているパスワード、秘密鍵の情報や暗号化されているものを解除するためのアルゴリズムなどがわかってしまいます。

## やり方
|プラットフォーム|説明|
|---|---|
|ブラウザ|ブラウザのデベロッパーツールを開く。取得できたjavascriptはクライアントで実行されているソースコードになります|
|Android|[dex2jar](https://github.com/pxb1988/dex2jar)や[jad](https://varaneckas.com/jad/)やというツールを用いることでデコンパイルすることが可能です。詳しくはこちら。[AndroidのAPKを逆コンパイルする](https://qiita.com/chibi929/items/940623d33d9f6eb3877f)|
|iOS|具体的なやり方などはこちらを参照。[iOS Reverse Engineeringの操作手順](https://qiita.com/roysue/items/024a8e01347be1a91bd8)|

## 対策
1. 重要な値はサーバーにのみ保持し、ネットワークを用いてデータを取得する。その上でデータの更新はサーバー上でのみ行うようにする。
2. C/C++を使ったネイティブコードで処理を書く。

## 具体例・解説
1.の具体例として、ガチャを行う処理を実装する場合、サーバー側で抽選を行い、抽選の結果のみをクライアント(端末)に送るようにする。ガチャ抽選の実装内容をシーケンス図に表すと以下のようになる。
![cdraw.png](https://qiita-image-store.s3.amazonaws.com/0/72596/8632a1f0-e0c5-b734-e11d-3ea22c8fb5ca.png)

2.の具体例として[Android NDK](https://developer.android.com/ndk/index.html?hl=ja), iOS(Objective-C++), [UnityIL2CPP](https://docs.unity3d.com/jp/530/Manual/IL2CPP.html) などそれぞれ対応したプラットフォームでビルドするようにする。
2.の詳しい解説として、C/C++で記述されたコードをコンパイルすると機械語に変換されます。これを逆コンパイルしても、逆アセンブラまでにしかなりません。そのため、この状態ではソースコードの中身を解析するのは(人間では)非常に困難なため、ネイティブコードで書いた処理というのはデコンパイルへの対策となります。

## 参考
 * [Web開発でよく使う、特に使えるChromeデベロッパー・ツールの機能](http://www.buildinsider.net/web/chromedevtools/01)
 * [Objective-C++ 取扱説明書 〜始め方から使い方、注意点まで〜【完全保存版】](http://marycore.jp/prog/objective-c/objcpp-basics/)
 * [Ｃの逆コンパイラはどこまで実現可能か，Javaはなぜ逆コンパイルされやすいのか？](http://www5d.biglobe.ne.jp/~noocyte/Programming/Decompile.html)

# メモリ改ざん
## 何ができる?
アプリ実行時にメモリ値を可視化し、そのメモリ値を変更することができます。これにより、明らかにおかしい値にしてゲームバランスを崩壊させてしまったり、そのデータをそのまま端末内に保存させたりすることでゲームを破壊したり、メモリ値からサーバーとの認証鍵を解析、取得することでサーバーへのハッキングが可能となる、といったことができるようになります。
![メモリ改ざん](https://image.slidesharecdn.com/unity2017-171217163414/95/unity-2017-13-638.jpg?cb=1513528630)

## やり方
Android、iOSでメモリを改ざんできるツール(ソフト)はいくつかあります。[「iOS memory hack」](https://www.google.co.jp/search?q=iOS+memory+hack)や[「Android memory hack」](https://www.google.co.jp/search?q=Android+memory+hack)で検索すると紹介動画やツール紹介サイトがいくつか出てきます。以下、サイトの一例を紹介します。

 * [Top 8 Game Hacker Apps for Android with/without Root](https://drfone.wondershare.com/android/android-game-hacker.html)
 * [Top 14 Best Game Hack Apps / Tools for Android With and Without Root](https://hackerbot.net/mobile/android/16-top-android-game-hacking-cheating-apps)

割と有名なツールとしては[Frida](https://github.com/frida/frida), 
など

## 対策
1. データの更新はサーバー上でのみ行い、端末(クライアント)では行わない。
2. メモリに乗っている値の[チェックサム](https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%B5%E3%83%A0)や[MD5 ハッシュ値](https://ja.wikipedia.org/wiki/MD5)をみる。

## 具体例や解説
1.の例として、サーバー側でガチャの抽選を行い、抽選結果のみをサーバーから受け取るように実装する。
2.の解説として、[チェックサム](https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%B5%E3%83%A0)や[MD5 ハッシュ値](https://ja.wikipedia.org/wiki/MD5)というものは値がわずかに変更されれば値が変更されてしまうものです。変数の参照先が変わった場合も値が変化するため、[チェックサム](https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%B5%E3%83%A0)や[MD5 ハッシュ値](https://ja.wikipedia.org/wiki/MD5)を監視することでメモリ値が変更されれば検知することができます。
<font color="#FF0000">しかし、厳しく対応しようとすれば、対応箇所が膨大となり、対応コストが高くなります。また、対応することでパフォーマンスが犠牲になる可能性もあります。メモリ改ざんを行ったからといって、サーバー上のデータが書き換わるわけではないので、ケースバイケースで対応することをおすすめします。</font>

# Log検知
アプリを起動した時に出力されるアプリ専用のLogを検出することがLog検出です。
![logcat](http://yujilow1220.github.io/images/post/logcat.png)

## 何ができる?
処理の中身を解析するヒントを与えてしまいます。これ自体で何かをハッキングするということはないのですが、ハッキングするための方針、ヒントを与えることになります。

## やり方
|ケース|ツール|参考|
|---|---|---|
|ブラウザ|デベロッパーツール|[Web開発でよく使う、特に使えるChromeデベロッパー・ツールの機能](http://www.buildinsider.net/web/chromedevtools/01)|
|Android|Android Studio->Logcat|[Android Studio の logcat を快適にする](http://sea-mountain.hatenablog.jp/entry/2013/09/07/021603)|
|iOS|Xcode他|[iPhoneの実機ログを確認する方法](https://qiita.com/takashings/items/61f18fe76dd4c18f4bb7)|

## 対策
1. Logを全て消す
2. Buildする時に環境を切り分けるようにし、それぞれの環境において出力するLogレベルを切り替えるようにする
3. エラーなど、ログとして検知したいものは直接出力はせず、違う表現にして出力したり、一部だけ出力する。

### 具体例や解説
2.の具体例としてDebugビルドやReleaseビルドといったビルドの環境を分け、logレベルがDebugのものはReleaseビルドでは出力されない、といった設定、ルールを儲ける。(各プラットフォームの開発環境の中にはそのような、制御ができるような仕組みがあるはずです)
3.の解説として、検知したエラーや文言はサーバーに送ったり、サーバーでログとして記録するが、ユーザーが検知できる形としては表示せず、以下のような共通の文言として出力(表示)する。これにより詳しいことは開発側は検知し対応でき、またユーザーには何か問題が起きたことがそれとなく伝えることができるので、お互いの住み分けができる。
![エラー文言の例.png](https://qiita-image-store.s3.amazonaws.com/0/72596/777bba5a-0a5a-6daf-7673-e47743d0c5fa.png)

## 参考
 * [ログ設計指針](https://qiita.com/nanasess/items/350e59b29cceb2f122b3)

# 脱獄(root化)
## 脱獄とは?
[root権限](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC)を取得することを脱獄といいます。
[root権限](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC)を取得することですべての操作が可能になります。
端末の中のデータを全て消すといった、本来なら操作ができないように制限されているようなこともできるようになります。
また、脱獄することでアプリ内に用意された、アクセスが制限されているような領域にもアクセスすることができてしまうため、この領域に保存されている秘密鍵やアカウント情報といった特有の情報も見ることができるようになります。
全ての情報を見ることができるので、情報次第ではハッキングできなかったものが可能になったり、といったハッキングへの足がかりとすることが可能になります。
また、Android(Linux)では<font color="#FFAA00">root化</font>、iOSでは<font color="#FFAA00">脱獄</font>と呼ぶのが一般的です。

## やり方
|プラットフォーム|ツール例|参考|
|---|---|---|
|Android|[KingoRoot](http://www.kingoapp.com/), [dr.fone-Android root化](https://drfone.wondershare.jp/android-root.html) 他|[Android root化アプリ－Androidをroot化するアプリ6選](https://drfone.wondershare.jp/android/android-root-app.html) 他|
|iOS|[Electra](https://coolstar.org/electra/) 他|[Electra(Github)](https://github.com/coolstar/electra), [「iOS 11.0〜11.1.2」を脱獄する方法！全デバイス対応＆Cydia対応『Electra』](http://tools4hack.santalab.me/howto-ios11-ios1112-jailbreak-electra-support-cydia-subsuitle.html)他|

<font color="#FF0000">なお、脱獄すると端末の保証対象外となったり、端末の挙動が不安定になったり、端末が起動しなくなるなど、大きなリスクがあります。脱獄する場合はこれらのリスクを十分理解し、端末を一台、破壊するつもりで行ってください。</font>

## 対策
1. アプリ内で脱獄したかどうかを検知可能なため、脱獄していたら処理を変えるように実装する。
2. データを暗号化して保存する。

![ill02-1.gif](https://qiita-image-store.s3.amazonaws.com/0/72596/71dfd85a-328d-8dc9-e241-d253c7b9ae25.gif)

## 具体例や解説
1.の実装方法を以下にあげます。

 * Androidの場合、[SafetyNet API](https://developer.android.com/training/safetynet/attestation.html)や[rootbeer](https://github.com/scottyab/rootbeer)ライブラリを用いることで脱獄しているかどうか確認することができる。
 * iOSの場合、[iOS脱獄チェックメモ](https://qiita.com/PGMY/items/3ceae5cbbf93e013deb4)や[SwiftでiOS脱獄チェック](https://qiita.com/nnsnodnb/items/82c168eae3c3df6751f5)を参考にチェック処理を作成して確認することができます。

2.の解説として[AES暗号](https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard)などの[共通鍵暗号](https://ja.wikipedia.org/wiki/%E5%85%B1%E9%80%9A%E9%8D%B5%E6%9A%97%E5%8F%B7)の処理を施して、人間がデータを見てもわからないように加工する。(データの暗号化に関しては改めて別の機会にまとめます)

## 参考
 * [root化 (Android OS)](https://ja.wikipedia.org/wiki/Root%E5%8C%96_(Android_OS))
 * [Jailbreak](https://ja.wikipedia.org/wiki/Jailbreak)
 * [Root化とは](https://qiita.com/solar-arrow/items/981a67d6ed50d652bc08)
 * [SafetyNet Attestation API](https://developer.android.com/training/safetynet/attestation.html)
 * [SafetyNet attestation: 不正利用対策の構成要素](https://developers-jp.googleblog.com/2017/05/safetynet-attestation-building-block.html)
 * [アプリのroot化端末チェック(SafetyNet)を回避する方法まとめ](http://andmem.blogspot.jp/2016/10/avoid-safetynet.html)

# 乱数調整
## 乱数調整とは?
乱数の特性を掴み、狙ったものを出せるようにすることです。
本来「乱数」とは、「全く規則性が無く予測がつかない数字の並び」と言う意味ですが、必ず書いた通りに動くというプログラムの宿命上そのようなものをプログラムだけで仮想することは出来ません。
そのため、不規則な数が出現するような数式であったり、何らかの方法で作成した乱数の羅列の一部を移植する事で「擬似乱数」を生成して用います。この乱数を生成する特性をつかみ、操作することで調整する(狙ったものを出す)ことが可能になります。
![20170921194329.gif](https://qiita-image-store.s3.amazonaws.com/0/72596/8ac11e62-bc0c-2c28-7204-370170b23226.gif)

## 乱数の生成方法
乱数生成によく使われるアルゴリズムとしては[線形合同法](https://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E5%90%88%E5%90%8C%E6%B3%95)や[メルセンヌツイスタ](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%BB%E3%83%B3%E3%83%8C%E3%83%BB%E3%83%84%E3%82%A4%E3%82%B9%E3%82%BF)があります。乱数生成時、どのアルゴリズムが標準で使われるかどうかは開発言語ごとに異なります。
なお、乱数を生成する際、現在時刻を基にして、擬似乱数を生成することが多いので、特定のタイミング(時刻)を狙うとうまくいく、といった都市伝説みたいな論調はあながち間違っているわけではありません。

## 対策
乱数を生成するとき、「真の乱数」を用いる。

## 具体例や解説
「真の乱数」とは自然界の中で生じてしまう(物理的な)誤差やノイズのことをいいます。具体的には通信時間やセンサーの検出した生のデータ([誤り検出訂正](https://ja.wikipedia.org/wiki/%E8%AA%A4%E3%82%8A%E6%A4%9C%E5%87%BA%E8%A8%82%E6%AD%A3)していない値)などを乱数生成時に用いることです。
最も簡単な実装方法としては抽選処理は全てサーバーで行い、抽選結果のみ取得することで乱数を調整することが困難にすることができます。

## 被害例
 1. [乱数調整(ポケモン)](http://dic.nicovideo.jp/a/%E4%B9%B1%E6%95%B0%E8%AA%BF%E6%95%B4(%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3))

この他、クライアントで乱数を用いた抽選処理を行っているゲームでは乱数調整を行ったというケースに度々存在します。

## 参考
 * [乱数調整とは (ランスウチョウセイとは) [単語記事] - ニコニコ大百科](http://dic.nicovideo.jp/a/%E4%B9%B1%E6%95%B0%E8%AA%BF%E6%95%B4)
 * [乱数調整とは - はてなキーワード](http://d.hatena.ne.jp/keyword/%CD%F0%BF%F4%C4%B4%C0%B0)
 * [サイコロを振るのは簡単である、しかし、ゲームでサイコロを実装するにはサイコロを知らなければならない](https://qiita.com/isonami/items/1cc278cbf2093d2d6abd)
 * [乱数調整 入門](http://mizdra.hatenablog.com/entry/2016/12/01/235954)

# バックアップ改ざん

## バックアップ改ざんとは?

主にiOS, Androidにて用いられる手法です。
まずiOS, Android端末のデータをPCにバックアップします。
バックアップしたデータを編集して端末に戻すことでアプリ内において意図しない挙動を再現させたり、機密にしたい情報を抜き取ることができます。
<font color="#FF0000">またこのバックアップ改ざんは脱獄(Root化)しなくても再現できる方法を紹介します。脱獄(Root化)した場合はアプリ内のデータにもアクセスすることできるので対策としては以下の限りではありません。</font>

## やり方
### iOSの場合

`iPhone`とPCを接続 → PCの`iTunes`よりBackupを行う

<img width="1440" alt="ipodbackup.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/d6ee6941-5d97-1d0d-dc06-8270ca9a1945.png">

バックアップが完了すると、Macの場合 `~/Library/Application\ Support/MobileSync/Backup/[端末ID]/` 以下にバックアップした内容が置かれています。
この中の各種ハッシュ値のファイル(ディレクトリ)がバックアップされているファイルの本体です(中身は暗号化されています。AES暗号で暗号化されているようです)
またバックアップをすると同時に `Manifest.db` というファイルも抽出されています。
このファイルはSQLiteで開いて操作することができます。`Manifest.db` にはアプリに該当するハッシュ値のファイルと暗号化している鍵の情報など記録されているので、解析して復号することでBackupの中身を抽出することができます。
あとはバックアップファイルを改ざんして同様に暗号化して、端末に戻せばアプリ内のデータを改ざんすることができます。

#### 参考
 * [嫁の携帯で怪しいものを発見した(のて調べ方手順メモしてみる)](https://qiita.com/hinoshiba/items/6c83871e7b521864ba60)
 * [How to read itunes backup file (Manifest.db)](https://stackoverflow.com/questions/40493631/how-to-read-itunes-backup-file-manifest-db)


### Androidの場合

やり方は以下の方法があります。

* [adbコマンド](https://developer.android.com/studio/command-line/adb)でfull backupを行う
* Android Studio → Device File Exporterからファイルをのぞき見る

#### [adbコマンド](https://developer.android.com/studio/command-line/adb)でfull backupを行う

[adbコマンド](https://developer.android.com/studio/command-line/adb)とは接続しているAndroidを操作するコマンドです。すでに[adbコマンド](https://developer.android.com/studio/command-line/adb)を実行できる状態であるとします。
`full backup` のやり方は以下のコマンドを実行することで全てのアプリの `full backupファイル` を取得することができます。

```shell
adb backup -f backup.ab -apk -obb-shared -all -system
```

これで `backup.ab` というバックアップファイルが取得できます。
また以下のコマンドで行うことで特定のアプリにおけるバックアップを取得できます。

```shell
adb backup -f app.ab アプリのパッケージ名
```

なお `full backup` するよりもパッケージ名を入力して抽出した方が完全なデータの抽出がうまくいくようです。

バックアップファイルである `abファイル` の中身を参照するためには
`AndroidBackupExtractor` というツールを用いて`tarファイル` に変換することで中身を見ることができます。詳しい使い方などは[こちら](https://github.com/nelenkov/android-backup-extractor)を参照してください。
[android-backup-extractor](https://github.com/nelenkov/android-backup-extractor)

#### Android Studio → Device File Exporterからファイルをのぞき見る

PCとデバイスをつないでいる状態で [Android Studio](https://ja.wikipedia.org/wiki/Android_Studio) を起動します。 [Android Studio](https://ja.wikipedia.org/wiki/Android_Studio) の右側にある `Device File Exporter` を選択し、`data/data/アプリのパッケージ名/` 以下のアプリケーションの保存領域を参照します。

<img width="1420" alt="AndroidData.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/c14cc787-fbf3-ee7b-378e-f5798e6fc4fb.png">

#### 参考
 * [Device File Explorer でデバイス上のファイルを表示する](https://developer.android.com/studio/debug/device-file-explorer)

## 対策

 * 端末に保存するデータを暗号化する
 * バックアップ時にアプリ内のデータが含まれないような設定にする

### 端末に保存するデータを暗号化する

端末内のデータを保存するときは処理の中でデータを暗号化した状態で保存します。
暗号化は[AES暗号](https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard)や[RSA暗号](https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7)などの暗号・複号アルゴリズムを用いてアプリケーション内の処理の中で行います。
例えば `Unity`を用いている場合は暗号化ライブラリを作成し、内容をこちらにて紹介しているので参照・活用してください。
[Unity(C#) で「正しい」暗号化処理をするライブラリを作成しました](https://qiita.com/taptappun/items/1a9dbc8dc62c072aabb5)

### バックアップ時にアプリ内のデータが含まれないような設定にする
#### iOSの場合

<font color="#FF0000">iOSには現状、開発時にバックアップできないようにする機能はありません (調査した限りでは見つかりませんでした。もし方法をご存知でしたら教えていただきますと幸いです)</font>

#### Androidの場合

以下のように `AndroidManifest.xml` に `application` の項目に `android:allowBackup="false"` の項目を追加します。

```AndroidManifest.xml
<manifest>
    ...
    <application
        android:allowBackup="false">
    ...
    </application>
</manifest>
```

この設定がされているアプリではバックアップを行なってもアプリ内に保存された情報を見ることができません。

<img width="1420" alt="androidpackage.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/66c2e0c2-4f6b-7112-887e-7db934a54860.png">

##### 参考
 * [AllowBackup/FullBackupContent Problems (AllowBackup)](https://github.com/recruit-tech/sec-guide-android-lint/blob/master/AllowBackup.md)

# まとめ
以上がよく行われる有名なアプリケーションハッキングになります。
まだ他にもあるかもしれませんがご希望ご指摘などいただけたらと思います。

# 関連記事
 * [ゲームでよく見落とされるテストケースとその再現手法](https://qiita.com/taptappun/items/1d3e2c9646cf7d4557db)
 * ゲームでよくされるチート手法とその対策 〜ネットワークハッキング編〜 ←現在執筆中