Unity混淆方法和反黑客保护

您终于发布了您一直在努力开发的游戏,甚至可能还添加了排行榜来增加游戏的挑战性。但日子一天天过去,你会注意到一些玩家以不切实际的高分出现在记分牌的顶部。你的第一个想法当然是他们在黑客攻击,但他们是如何做到的呢?

答案是,他们很可能使用程序将自己的值注入内存,其中最流行的程序是 Cheat Engine。现在,在单人游戏中,黑客攻击实际上并不那么重要,但当它是一个涉及其他玩家的多人游戏时,它就成为一个问题。

在这篇文章中,我将展示如何让您的游戏更安全地抵御此类攻击,从而改善非黑客玩家的体验。

注意:本文仅简要介绍最常见的攻击以及针对这些攻击的基本防护。如果您需要更多开箱即用的解决方案,请随时检查此 Asset Store 包 。

当谈到使用作弊引擎进行黑客攻击时,有两种最常见的攻击:速度黑客攻击和价值扫描。

速度黑客

Speed Hack 是最容易执行的(只需点击 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 秒,但您可以选择任何值,只需确保它不会太小以避免误报),脚本就会调用 SpeedhackDetected() 方法,该方法表明 SpeedHack 的存在。

要使用该脚本,请确保它 attached 到场景中的任何对象。

价值扫描

值扫描是在游戏分配的内存中查找相关值并用不同的值覆盖它们的过程。最常用于增加玩家生命值、武器弹药或任何会给黑客在游戏中带来不公平优势的价值。

从技术上讲,游戏中的每个值都可以被覆盖/更改,但这是否意味着所有这些值都需要受到保护?不必要。一般来说,新手黑客只会瞄准屏幕上显示的值,并且知道它们的用途(例如玩家生命值、弹药等)。所以大多数时候只有 "exposed" 值需要保护。

Unity FPS 游戏截图

例如,在上面的屏幕截图中,屏幕上的每个值都是黑客的潜在目标。

那么问题是,如何保护重要值免受值扫描攻击?答案是混淆。

混淆 是使某些内容变得模糊、不清楚或难以理解的行为。

混淆变量的方法有很多,但我将使用一种称为 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");
    }
}

上面的脚本包含 2 个简单变量:生命值(float)和弹药(int)。两个变量都显示在屏幕上:

这种做法在维护方面简单方便,但黑客可以轻松扫描这些值并使用 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) 对它们进行动态反混淆,从而显示真实值。

黑客会尝试搜索 10030 但无法找到它们,因为实际值完全不同。

为了操纵混淆值(例如减去健康值),我们首先对值进行反混淆,然后减去所需的值,然后将最终结果混淆回来。

如需更高级的解决方案,请随时查看此 Asset Store 包 。