結論
- XOR演算の特性(数値が反転する)を利用してセキュアな数値をマスクしてメモリ上に保持する
- セキュアな値はマスクされた値とseedをセットでメモリ上に持つ
- セキュアな値をバイナリで書き出す際もこれらをセットで保存する(int,uint,floatを直接書き出さない)
- 平文での保存はギルティ
概要
こちらの記事を参考にしています。
チート対策について考えてみる(メモリ改竄編)
- クライアントでのメモリエディタによるメモリの改ざんを防ぐ必要がある
- HPやお金などのセキュアな値をメモリ上で書き換えられると、不正にゲームをプレイされてしまうから
- セキュアな値は暗号化してメモリ上に持ちましょう
理論
- 整数値にXOR演算を使うと、seedが同じなら二回演算すると値がもとに戻る
0x0010 ^ 0x1234 => 0x1224 (4644)
0x0010 ^ 0x1234 ^ 0x1234 => 0x0010 (16)
- これを使って、セッタとゲッタの中で値にxorマスクを施すことによってメモリ上にセキュアな値を保存しなくて良い
- seedを値ごとにランダムに生成することによって、実質的にメモリ上から値を探すのは不可能になる
工夫
- 上記の記事では、floatのマスキングにポインタを使っていたが、unsafeを使いたくなかったのでBitConverterを利用してbyte配列に変換して保存することにした。
- チェックサムの計算はしていない。改ざんの検知は簡単だが、改ざんがあったときにどうするのかは完全にアプリケーション依存なので。
コード
MITライセンスです。何かもっといい方法があればコメントを下さい。
https://gist.github.com/keroxp/7b6b4ca6a45f4067e9c0079b2a7255b5
使い方
public class Player : MonoBehaviour {
ISecureValue<uint> hp = SecureValues.UInt();
ISecureValue<float> score = SecureValues.Float();
void Awake() {
hp.Value = 100;
score.Value = 0;
}
public void OnDamaged() {
hp.Value -= 1;
}
public void OnScore(float s) {
score.Value += s;
}
}