Всем привет! С вами снова Илья и мы продолжаем серию статей по разработке игр на Unity. Сегодня мы разберем процесс защиты ваших игр на примерах. Объяснять я буду исходя из нашей открытой библиотеки, созданной для Pixel Incubator — сообщества, в котором мы учим делать игры и не только.
Начало работы
Итак, все начинается с библиотеки. Естественно, в ходе статьи мы разберем, как работают элементы анти-чита, но пока я предлагаю просто взять себе готовый и бесплатный простой пример:
https://github.com/TinyPlay/Pixel-Anticheat
Данная библиотека включает в себя:
-
Несколько видов детектеров читов (Speed Hack, Wall Hack, Teleport Hack, Time Hack, Assembly Injection, Memory Hack);
-
Защищенные типы, шифрующие свои значения;
-
Классы для защищенного хранения данных в Player Prefs или файлах;
-
Библиотеки шифрования и хеширования (AES, RSA, SHA, MD5, Base64, xxHash);
-
Библиотека для получения сетевого и локального времени;
-
Библиотеки-утилиты;
-
Сцена-пример работы с UI анти-чита;
-
Единый интерфейс управления;
В ближайшее время также планируется добавить больше слоев защиты. Все они могут динамически подключаться / отключаться.
Детекторы читов работают в автоматическом режиме с минимальными настройками. Далее мы разберем, как они определяют читеров и защищают ваши игры.
Стоит сказать, что какой-бы античит не был у вас, в идеале не стоит хранить и обрабатывать какие-либо данные на стороне клиента. Если есть возможность — делайте все критические манипуляции с данными на сервере.
Подключение детекторов читов:
Инициализация любого детектора может быть выполнена следующим образом:
AntiCheat.Instance().AddDetector<MemoryHackDetector>().InitializeAllDetectors();
Также вы можете подключить все детекторы сразу:
AntiCheat.Instance() .AddDetector<MemoryHackDetector>() .AddDetector<InjectionDetector>() .AddDetector<SpeedHackDetector>() .AddDetector<WallHackDetector>() .AddDetector<TeleportDetector>() .AddDetector<TimeHackDetector>() .InitializeAllDetectors();
Некоторые из них могут содержать параметры. Например:
AntiCheat.Instance().AddDetector<SpeedHackDetector>(new SpeedhackDetectorConfig(){ coolDown = 30 }).InitializeAllDetectors();
Чит-детекторы
Данные классы позволяют перехватывать возможные способы нечестной игры и оповещать об этом код при помощи событий.
В нашей библиотеке мы предоставили пример, в котором при запуске таких событий отображается UI с подозрением читерства:
namespace PixelAnticheat.Examples { using UnityEngine; using System.Collections.Generic; using PixelAnticheat.Detectors; using PixelAnticheat.Models; public class SampleScript : MonoBehaviour { [Header("Anti-Cheat References")] [SerializeField] private Transform _playerTransform; [Header("UI Referneces")] [SerializeField] private AntiCheatUI _antiCheatUI; private void Start() { // Initialize All Detectors AntiCheat.Instance() .AddDetector<MemoryHackDetector>(new MemoryHackDetectorConfig()) .AddDetector<InjectionDetector>() .AddDetector<SpeedHackDetector>(new SpeedhackDetectorConfig(){ coolDown = 30, interval = 1f, maxFalsePositives = 3 }) .AddDetector<WallHackDetector>(new WallhackDetectorConfig(){ spawnPosition = new Vector3(0,0,0) }) .AddDetector<TeleportDetector>(new TeleportDetectorConfig(){ detectorTarget = _playerTransform, availableSpeedPerSecond = 20f }) .AddDetector<TimeHackDetector>(new TimeHackDetectorConfig(){ availableTolerance = 120, networkCompare = true, timeCheckInterval = 30f }) .InitializeAllDetectors(); // Add Detectors Handlers AntiCheat.Instance().GetDetector<MemoryHackDetector>().OnCheatingDetected.AddListener(DetectorCallback); AntiCheat.Instance().GetDetector<InjectionDetector>().OnCheatingDetected.AddListener(DetectorCallback); AntiCheat.Instance().GetDetector<SpeedHackDetector>().OnCheatingDetected.AddListener(DetectorCallback); AntiCheat.Instance().GetDetector<WallHackDetector>().OnCheatingDetected.AddListener(DetectorCallback); AntiCheat.Instance().GetDetector<TeleportDetector>().OnCheatingDetected.AddListener(DetectorCallback); AntiCheat.Instance().GetDetector<TimeHackDetector>().OnCheatingDetected.AddListener(DetectorCallback); } private void OnDestroy() { AntiCheat.Instance().GetDetector<MemoryHackDetector>().OnCheatingDetected.RemoveAllListeners(); AntiCheat.Instance().GetDetector<InjectionDetector>().OnCheatingDetected.RemoveAllListeners(); AntiCheat.Instance().GetDetector<SpeedHackDetector>().OnCheatingDetected.RemoveAllListeners(); AntiCheat.Instance().GetDetector<WallHackDetector>().OnCheatingDetected.RemoveAllListeners(); AntiCheat.Instance().GetDetector<TeleportDetector>().OnCheatingDetected.RemoveAllListeners(); AntiCheat.Instance().GetDetector<TimeHackDetector>().OnCheatingDetected.RemoveAllListeners(); } private void DetectorCallback(string message){ Debug.Log("Cheating Detected: " + message); if (_antiCheatUI != null) { _antiCheatUI.SetContext(new AntiCheatUI.Context { message = message, OnCloseButtonClicked = QuitGame, OnContactsButtonClicked = GoToSupport }).ShowUI(); } } private void QuitGame() { Application.Quit(); } private void GoToSupport() { Application.OpenURL("https://example.com/"); } } }
Если развить эту идею и объединить с серверной частью — можно отправлять репорты о чите модераторам, которые будут проверять честность/нечестность игры. Но в идеале, все данные лучше хранить на сервере и проверять там же.
А теперь, немного теоретической части.
Детектор Speed Hack
Спидхак — по своей сути чит, ускоряющий игровое время, за счет чего игрок начинает быстро перемещаться. Для того, чтобы искоренить это — мы сравниваем пройденное время внутри игрового цикла Unity через Time.deltaTime и время, прошедшее в системе.
Если это время не совпадает (с определенной погрешностью и с допустимым количеством пропусков), мы выдаем событие о читерстве.
Стоит отметить, что если вы программно меняете TimeScale, то анти-чит может ложно срабатывать, но для таких случаев можно ввести некий коэффицент изменения времени, либо временно отключать детектор.
Детектор Wall Hack
Wall Hack — грубо говоря хождение сквозь стены. Он же может быть включен и в NoClip хаки. У объектов отключаются некоторые (или все) коллайдеры. Чтобы защититься от этого — мы создаем сервисные объекты RB или Character Controller, с помощью которых постоянно проверяем возможность хождения сквозь стену при помощи сервисного объекта стены.
Таким образом, при помощи сервисных объектов (фейк-стене и фейк-игроках), мы проверяем работоспособность коллизий в игре.
Детектор изменения времени (Time Hack)
Дополнительный способ защиты, при котором проверяется наличие хака на отмотку времени (вперед или назад) для быстрого фарма ресурсов, привязанного ко времени. Особенно такое распространено в различного рода айдлерах, фермах и т.д.
Сверять время можно локально, либо при помощи интернета. У нас в библиотеке реализовано оба метода.
В чем же их смысл?
Мы берем время из интернета и локальное время, и через некоторый промежуток времени мы сверяем разницу, прошедшую для локального времени и для времени из интернета.
Если же через 10 секунд, мы получаем разницу в интернет-времени в 10 секунд, то при перемотке времени на телефоне — мы получаем разницу в 10 секунд + определенный промежуток времени, на который мы отмотались.
Таким образом мы можем вычислить перемотку времени и исключить её, создав дополнительный слой защиты.
Детектор внедрения зависимостей (Assembly Injection)
Здесь все достаточно просто — мы задаем белый список библиотек, которые могут быть подключены в финальный билд нашей игры и, если они не совпадает со списком, при запущенной игре, значит, либо её код был изменен (или код .dll библиотек), либо кто-то внедрился к нам в игру.
Опять же, для большей устойчивости, не забудьте провести обфускацию вашего кода, а также использовать IL2CPP вместо Mono среды.
Детектор изменения памяти (Memory Hack)
Данный детектор работает в связке с защищенными типами. Защищенные типы хранят в себе реальное значение и его зашифрованный хэш. Если же реальное значение изменяется из вне, то его хеш остается неизменным, а значит, доступ к памяти был произведен из вне (например, изменен через Cheat Engine).
Таким образом, мы можем безопасно работать с нашими данными, используя защищенные типы для хранения значений.
Детектор телепорта (Teleport Hack)
Данный способ, помогает частично избавиться как от некоторых видов спидхака, так и от хаков на телепорт. Его суть проста — каждый определенный промежуток времени (к примеру раз в 10 секунд) мы проверяем дистанцию между текущей позицией игрока и его новой позицией. Если эта позиция изменилась больше допустимого — игрок телепортировался.
Здесь же можно дополнительно использовать защищенные типы для шифрования векторов.
Защищенные типы
Суть проста — в них мы храним несколько значений. Реальное и шифрованное. Если они не совпадают, значит кто-то изменил их из вне. И нам нужно проверять регулярно эти изменения, что в нашем случае делает детектор памяти.
Защищенными могут быть как базовые типы (вроде float, int, string и пр.), так и какие-то кастомные (Vector2, Vector3, Color, Quaternion и др).
В заключении
Защищать вашу игру, несомненно важно. И чтобы максимально добиться этого — нужно использовать несколько слоев защиты. Однако, всегда взвешивайте пользу и вред, ведь защита — это дополнительная нагрузка на устройства, а иногда и неудобства для конечного пользователя.
Второй вывод, хотя я его писал во вводной, но все равно повторюсь. Используйте клиент только для отображения ваших данных. Реализуйте бизнес-логику и работу с данными на серверах, конечно же учитывая соотношение пользы/вреда.
Спасибо за прочтение статьи. Надеюсь, она была вам полезна, как и библиотека, которую я приложил выше.
Удачи в ваших проектах. И конечно же, буду рад обсудить с вами.
ссылка на оригинал статьи https://habr.com/ru/articles/589899/
Добавить комментарий