Unity の難読化方法とハッキング対策保護
熱心に取り組んできた ゲーム をついにリリースしました。さらに、ゲームに挑戦を加えるために リーダーボード も追加したかもしれません。しかし日が経つと、非現実的な高得点を記録するプレイヤーがスコアボードのトップに現れることに気づきます。もちろん最初に考えるのは、彼らがハッキングをしているということですが、どうやってハッキングを行うのでしょうか?
答えは、彼らは独自の値をメモリに挿入するプログラムを使用している可能性が高く、そのようなプログラムの中で最も人気のあるものは Cheat Engine です。シングルプレイヤー ゲームではハッキングはあまり問題になりませんが、他のプレイヤーが関与する マルチプレイヤー ゲームになると問題になります。
この投稿では、そのような攻撃に対してゲームの安全性を高める方法を説明します。これにより、ハッキングをしないプレイヤーのエクスペリエンスが向上します。
注: この記事では、最も一般的な攻撃とそれに対する基本的な保護について簡単に説明するだけです。さらにすぐに使えるソリューションが必要な場合は、この Asset Store パッケージ を自由にチェックしてください。
Cheat Engine を使用したハッキングに関しては、スピード ハッキングとバリュー スキャンという 2 つの最も一般的な攻撃があります。
スピードハック
実行が最も簡単 (2 回のクリックだけで完了) であるため、スピード ハックは通常、初心者ユーザーにとって最初の選択肢になります。
スピードハックは、ゲームの更新速度を上げてすべてを高速化することで機能し、通常の速度でプレイするプレイヤーよりもハッカーに優位性を与えます。
幸いなことに、Unity でこのハッキングを検出する方法があります。以下のスクリプトを確認してください。
注: 現在、この方法は機能しなくなっているため、シングルプレイヤー ゲームではスピード ハックを検出することがはるかに困難になっています。ただし、マルチプレイヤー ゲームでは、サーバー側のチェックに依存して、プレイヤーとサーバーの時間の不一致を検出し、適切なアクション (プレイヤーのキック/禁止など) を実行することで、これを行うことができます。
SC_SpeedhackDetector.cs
using UnityEngine;
using System;
public class SC_SpeedhackDetector : MonoBehaviour
{
//Speed hack protection
public int timeDiff = 0;
int previousTime = 0;
int realTime = 0;
float gameTime = 0;
bool detected = false;
// Use this for initialization
void Start()
{
previousTime = DateTime.Now.Second;
gameTime = 1;
}
// Update is called once per frame
void FixedUpdate()
{
if (previousTime != DateTime.Now.Second)
{
realTime++;
previousTime = DateTime.Now.Second;
timeDiff = (int)gameTime - realTime;
if (timeDiff > 7)
{
if (!detected)
{
detected = true;
SpeedhackDetected();
}
}
else
{
detected = false;
}
}
gameTime += Time.deltaTime;
}
void SpeedhackDetected()
{
//Speedhack was detected, do something here (kick player from the game etc.)
print("Speedhack detected.");
}
}
上記のスクリプトは、ゲーム内時間をコンピューター (システム) 時間と比較します。通常、両方の時間は同じ速度で更新されます (Time.timeScale が 1 に設定されていると仮定します)。ただし、SpeedHack がアクティブになると、ゲーム内の更新頻度が加速され、ゲーム内の時間が累積されます。もっと早く。
両方の時間の差が大きくなりすぎると (この場合は 7 秒ですが、任意の値を選択できますが、誤検知を避けるために小さすぎないことを確認してください)、スクリプトは Speedhack の存在を通知する SpeedhackDetected() メソッドを呼び出します。
スクリプトを使用するには、シーン内のオブジェクトに attached されていることを確認してください。
値のスキャン
値スキャンは、ゲームに割り当てられたメモリ内で関連する値を見つけて、それらを別の値で上書きするプロセスです。最も一般的には、プレイヤーの体力、武器の弾薬、またはゲーム内でハッカーに不当な優位性を与える値を増やすために使用されます。
技術的に言えば、ゲーム内のすべての値は上書き/変更できますが、すべての値を保護する必要があるということでしょうか? 必ずしも。一般に、初心者のハッカーは、画面に表示され、その用途がわかっている値 (たとえば、プレイヤーの健康状態、弾薬など) のみをターゲットにします。したがって、ほとんどの場合、保護する必要があるのは "exposed" 値のみです。
たとえば、上のスクリーンショットでは、画面上のすべての値がハッキングの対象となる可能性があります。
問題は、重要な値を値スキャン攻撃からどのように保護するかということです。答えは 難読化 です。
難読化 は、何かを不明瞭、不明瞭、または理解不能にする行為です。
変数を難読化する方法はたくさんありますが、ここでは Randomizer というメソッドを使用します。 最初に乱数値が生成され、そこから実際の値が減算され (その後、非表示になります)、必要に応じて、生成された乱数値から非表示の値が減算され、その差が元の数値となります。 重要なのは、画面に表示される値が変数とはまったく異なる値になるようにすることで、スキャン時にハッカーを完全に間違った方向に導くことになります。
- 新しいスクリプトを作成し、'SC_Obf' という名前を付け、その中に以下のコードを貼り付けます。
SC_Obf.cs
using UnityEngine;
public class SC_Obf : MonoBehaviour
{
static float random = -1;
public static void Initialize()
{
if(random == -1)
{
random = Random.Range(10000, 99999);
}
}
public static float Obfuscate(float originalValue)
{
return random - originalValue;
}
public static float Deobfuscate(float obfuscatedValue)
{
return random - obfuscatedValue;
}
}
上記のスクリプトは、乱数と、値を難読化および難読化解除するための 2 つの簡単なメソッドを生成するために使用されます。
- ここで、難読化を行わない通常のスクリプトの例に移りましょう。
using UnityEngine;
public class SC_Test : MonoBehaviour
{
public float health = 100;
public int ammo = 30;
public void Damage(float points)
{
health -= points;
}
void OnGUI()
{
GUI.Label(new Rect(5, 5, 150, 25), health + " HP");
GUI.Label(new Rect(5, 30, 150, 25), ammo + " Ammo");
}
}
上記のスクリプトには、health (float) と ammo (int) という 2 つの単純な変数が含まれています。両方の変数が画面に表示されます。
この方法はメンテナンスの観点から簡単で便利ですが、ハッカーは Cheat Engine または同様のソフトウェアを使用して値を簡単にスキャンし、上書きすることができます。
- 以下は同じスクリプトですが、'SC_Obf.cs' の難読化メソッドを使用しています。
using UnityEngine;
public class SC_Test : MonoBehaviour
{
public float health;
public int ammo;
void Awake()
{
SC_Obf.Initialize();
health = SC_Obf.Obfuscate(100);
ammo = (int)SC_Obf.Obfuscate(30);
}
public void Damage(float points)
{
health = SC_Obf.Obfuscate(SC_Obf.Deobfuscate(health) - points);
}
void OnGUI()
{
GUI.Label(new Rect(5, 5, 150, 25), SC_Obf.Deobfuscate(health) + " HP");
GUI.Label(new Rect(5, 30, 150, 25), SC_Obf.Deobfuscate(ammo) + " Ammo");
}
}
ヘルス変数と弾薬変数を直接初期化する代わりに、void Awake() の開始時に初期化します ( を使用して値を割り当てる前に、必ず SC_Obf.Initialize() を呼び出してください) SC_Obf.Obfuscate(値))。
次に、値を表示するときに、SC_Obf.Deobfuscate(value) を呼び出してオンザフライで難読化を解除し、実際の値を表示します。
ハッカーは 100 と 30 を検索しようとしますが、実際の値は完全に異なるため、これらを見つけることができません。
難読化された値を操作するには (例: 健康状態の減算)、まず値の難読化を解除し、次に必要な値を減算し、最終結果を難読化して戻します。
より高度なソリューションについては、この Asset Store パッケージ を自由にチェックしてください。