Методи обфускації Unity та захист від злому

Нарешті ви випустили гру, над якою так наполегливо працювали, і, можливо, навіть додали таблицю лідерів, щоб додати випробування грі. Але минають дні, і ви помічаєте, як деякі гравці з’являються на табло з нереально високими результатами. Ваша перша думка, звичайно, вони зламують, але як вони це роблять?

Відповідь полягає в тому, що вони, швидше за все, використовують програму для введення власних значень у пам’ять, причому найпопулярнішою з таких програм є Cheat Engine. Зараз у іграх для одного гравця хакерство не має такого великого значення, але це стає проблемою, коли це багатокористувацька гра, де задіяні інші гравці.

У цій публікації я покажу, як зробити вашу гру більш захищеною від таких атак, що, у свою чергу, покращить досвід для гравців, які не хакерські.

ПРИМІТКА. Ця стаття лише коротко описує найпоширеніші атаки та основний захист від них. Якщо вам потрібне готове рішення, скористайтеся цим пакетом Asset Store.

Що стосується злому за допомогою Cheat Engine, існує 2 найпоширеніші атаки: швидкісний злом і сканування цінностей.

Speed ​​Hack

Будучи найпростішим у виконанні (потрібно лише 2 кліки), Speed ​​Hack зазвичай є першим вибором для початківців.

Speed ​​hack працює, прискорюючи швидкість оновлення гри, роблячи все швидше, таким чином даючи хакерам перевагу над гравцями, які грають на звичайній швидкості.

На щастя, є спосіб виявити цей злом у 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.

Щоб використовувати сценарій, переконайтеся, що він прикріплений до будь-якого об’єкта в сцені.

Сканування значень

Сканування значень — це процес пошуку відповідних значень у виділеній грі пам’яті та заміни їх іншими значеннями. Найчастіше використовується для збільшення здоров’я гравця, боєприпасів до зброї або будь-якого значення, яке дасть хакеру несправедливу перевагу в грі.

Технічно кажучи, кожне значення в грі можна перезаписати/змінити, але чи означає це, що всі вони повинні бути захищені? Не обов'язково. Як правило, хакери-початківці націлюються лише на значення, які відображаються на екрані та знають, для чого вони використовуються (наприклад, здоров’я гравця, боєприпаси тощо). Тому найчастіше потрібно захищати лише значення "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(value)).

Тоді під час відображення значень ми деобфускуємо їх на льоту, викликавши SC_Obf.Deobfuscate(value) таким чином відображаючи справжні значення.

Хакер спробував би знайти 100 і 30, але не зміг би їх знайти, оскільки реальні значення зовсім інші.

Щоб маніпулювати обфусцованими значеннями (наприклад, віднімання здоров’я), ми спочатку деобфусцуємо значення, потім віднімемо необхідне значення, а потім заплутаємо кінцевий результат назад.

Щоб отримати більш просунуте рішення, перевірте цей Asset Store пакет.