[Unity] Как защитить данные игры на Unity в оперативной памяти?
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет! Не секрет, что существует множество программ для взлома игр и приложений. Способов взлома тоже много. Например, декомпиляция и модификация исходного кода (с последующей публикацией кастомных APK, к примеру, с бесконечной голдой и всеми платными покупками). Или самый универсальный способ — сканирование, фильтрация и редактирование значений в оперативной памяти. Как бороться с последним, расскажу под катом.
В общем случае мы имеем профиль игрока с кучей параметров, который сериализуется в Saved Game и загружается/сохраняется при запуске/завершении игры. И если добавить шифрование при сериализации довольно просто, то защитить этот же профиль в RAM несколько сложнее. Постараюсь привести простой пример:
var money = 100; // "100" is present in RAM now (as four-byte integer value). Cheat apps can find, filter and replace it since it was declared.
money += 20; // Cheat apps can scan RAM for "120" values, filter them and discover the RAM address of our "money" variable.
Debug.Log(money); // We expect to see "120" in console. But cheat apps can deceive us!
ProtectedInt experience = 500; // four XOR-encrypted bytes are present in RAM now. Cheat apps can't find our value in RAM.
experience += 100;
Debug.Log(experience); // We can see "600" in console;
Debug.Log(JsonUtility.ToJson(experience)); // We can see four XOR-encrypted bytes here: {"_":[96,96,102,53]}. Our "experience" is hidden.
Второй момент, на который стоит обратить внимание — внедрение новой защиты должно происходить с минимальным изменением исходного кода игры, где все уже отлично работает и протестировано много раз. В моем способе достаточно будет заменить типы int/long/float на ProtectedInt/ProtectedLong/ProtectedFloat. Далее я приведу комментарии и код.
Базовый класс Protected хранит зашифрованный массив байт в поле "_", он также отвечает за шифрование и дешифрование данных. Шифрование примитивное — XOR с ключом Key. Такое шифрование быстрое, поэтому с переменными можно будет работать даже в Update. Базовый класс работает с массивами байт. Дочерние классы отвечают за преобразование своего типа в массив байт и обратно. Но главное, они «маскируются» под простые типы с помощью implicit operator, поэтому разработчик может даже не заметить, что изменился тип переменных. Вы также можете заметить атрибуты на некоторых методах и свойствах, они нужны для сериализации с помощью JsonUtility и Newtonsoft.Json (оба способа поддерживаются одновременно). Если вы не используете Newtonsoft.Json, то нужно убрать #define NEWTONSOFT_JSON.
#define NEWTONSOFT_JSON
using System;
using UnityEngine;
#if NEWTONSOFT_JSON
using Newtonsoft.Json;
#endif
namespace Assets
{
[Serializable]
public class ProtectedInt : Protected
{
#if NEWTONSOFT_JSON
[JsonConstructor]
#endif
private ProtectedInt()
{
}
protected ProtectedInt(byte[] bytes) : base(bytes)
{
}
public static implicit operator ProtectedInt(int value)
{
return new ProtectedInt(BitConverter.GetBytes(value));
}
public static implicit operator int(ProtectedInt value) => value == null ? 0 : BitConverter.ToInt32(value.DecodedBytes, 0);
public override string ToString()
{
return ((int) this).ToString();
}
}
[Serializable]
public class ProtectedLong : Protected
{
#if NEWTONSOFT_JSON
[JsonConstructor]
#endif
private ProtectedLong()
{
}
protected ProtectedLong(byte[] bytes) : base(bytes)
{
}
public static implicit operator ProtectedLong(int value)
{
return new ProtectedLong(BitConverter.GetBytes(value));
}
public static implicit operator long(ProtectedLong value) => value == null ? 0 : BitConverter.ToInt64(value.DecodedBytes, 0);
public override string ToString()
{
return ((long) this).ToString();
}
}
[Serializable]
public class ProtectedFloat : Protected
{
#if NEWTONSOFT_JSON
[JsonConstructor]
#endif
private ProtectedFloat()
{
}
protected ProtectedFloat(byte[] bytes) : base(bytes)
{
}
public static implicit operator ProtectedFloat(int value)
{
return new ProtectedFloat(BitConverter.GetBytes(value));
}
public static implicit operator float(ProtectedFloat value) => value == null ? 0 : BitConverter.ToSingle(value.DecodedBytes, 0);
public override string ToString()
{
return ((float) this).ToString(System.Globalization.CultureInfo.InvariantCulture);
}
}
public abstract class Protected
{
#if NEWTONSOFT_JSON
[JsonProperty]
#endif
[SerializeField]
private byte[] _;
private static readonly byte[] Key = System.Text.Encoding.UTF8.GetBytes("8bf5b15ffef1f485f673ceb874fd6ef0");
protected Protected()
{
}
protected Protected(byte[] bytes)
{
_ = Encode(bytes);
}
private static byte[] Encode(byte[] bytes)
{
var encoded = new byte[bytes.Length];
for (var i = 0; i < bytes.Length; i++)
{
encoded[i] = (byte) (bytes[i] ^ Key[i % Key.Length]);
}
return encoded;
}
protected byte[] DecodedBytes
{
get
{
var decoded = new byte[_.Length];
for (var i = 0; i < decoded.Length; i++)
{
decoded[i] = (byte) (_[i] ^ Key[i % Key.Length]);
}
return decoded;
}
}
}
}
Если что-то где-то забыл или натупил, пишите в комментариях =) Удачи в разработке!
PS. Котик не мой, автор фотки CatCosplay.
===========
Источник:
habr.com
===========
Похожие новости:
- [Unity] Unity UIElements: первые впечатления в продакшн
- [Unity, Разработка игр] Улучшаем работу со сценами с помощью ScriptableObject (перевод)
- [Unity, Разработка игр] Инди-проект Tenebris, или геймдев без бюджета
- [Реверс-инжиниринг] Модифицируем Last Epoch — От dnSpy до Ghidra
- [Разработка игр, C#, Unity] Управление сценами в Unity без боли и страданий
- [Unity, Игры и игровые приставки, Разработка игр, Разработка мобильных приложений] Сказ о разработке амбициозного проекта 16-ти летним парнем (file547)
- [Amazon Web Services] AWS Community Builders
- [Будущее здесь, Дизайн, Космонавтика, Транспорт] Virgin Galactic показала дизайн интерьеров корабля для полета космических туристов
- [Unity, Работа с 3D-графикой, Работа с видео, Разработка игр] Создание панорамных видео в Unity (перевод)
- [IPv6, Сетевые технологии] Открыт прием заявок на соискание грантов от RIPE NCC
Теги для поиска: #_unity, #_unity_razrabotka_shifrovanie_zaschita (unity разработка шифрование защита), #_unity
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 16-Ноя 09:19
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет! Не секрет, что существует множество программ для взлома игр и приложений. Способов взлома тоже много. Например, декомпиляция и модификация исходного кода (с последующей публикацией кастомных APK, к примеру, с бесконечной голдой и всеми платными покупками). Или самый универсальный способ — сканирование, фильтрация и редактирование значений в оперативной памяти. Как бороться с последним, расскажу под катом. В общем случае мы имеем профиль игрока с кучей параметров, который сериализуется в Saved Game и загружается/сохраняется при запуске/завершении игры. И если добавить шифрование при сериализации довольно просто, то защитить этот же профиль в RAM несколько сложнее. Постараюсь привести простой пример: var money = 100; // "100" is present in RAM now (as four-byte integer value). Cheat apps can find, filter and replace it since it was declared.
money += 20; // Cheat apps can scan RAM for "120" values, filter them and discover the RAM address of our "money" variable. Debug.Log(money); // We expect to see "120" in console. But cheat apps can deceive us! ProtectedInt experience = 500; // four XOR-encrypted bytes are present in RAM now. Cheat apps can't find our value in RAM. experience += 100; Debug.Log(experience); // We can see "600" in console; Debug.Log(JsonUtility.ToJson(experience)); // We can see four XOR-encrypted bytes here: {"_":[96,96,102,53]}. Our "experience" is hidden. Второй момент, на который стоит обратить внимание — внедрение новой защиты должно происходить с минимальным изменением исходного кода игры, где все уже отлично работает и протестировано много раз. В моем способе достаточно будет заменить типы int/long/float на ProtectedInt/ProtectedLong/ProtectedFloat. Далее я приведу комментарии и код. Базовый класс Protected хранит зашифрованный массив байт в поле "_", он также отвечает за шифрование и дешифрование данных. Шифрование примитивное — XOR с ключом Key. Такое шифрование быстрое, поэтому с переменными можно будет работать даже в Update. Базовый класс работает с массивами байт. Дочерние классы отвечают за преобразование своего типа в массив байт и обратно. Но главное, они «маскируются» под простые типы с помощью implicit operator, поэтому разработчик может даже не заметить, что изменился тип переменных. Вы также можете заметить атрибуты на некоторых методах и свойствах, они нужны для сериализации с помощью JsonUtility и Newtonsoft.Json (оба способа поддерживаются одновременно). Если вы не используете Newtonsoft.Json, то нужно убрать #define NEWTONSOFT_JSON. #define NEWTONSOFT_JSON
using System; using UnityEngine; #if NEWTONSOFT_JSON using Newtonsoft.Json; #endif namespace Assets { [Serializable] public class ProtectedInt : Protected { #if NEWTONSOFT_JSON [JsonConstructor] #endif private ProtectedInt() { } protected ProtectedInt(byte[] bytes) : base(bytes) { } public static implicit operator ProtectedInt(int value) { return new ProtectedInt(BitConverter.GetBytes(value)); } public static implicit operator int(ProtectedInt value) => value == null ? 0 : BitConverter.ToInt32(value.DecodedBytes, 0); public override string ToString() { return ((int) this).ToString(); } } [Serializable] public class ProtectedLong : Protected { #if NEWTONSOFT_JSON [JsonConstructor] #endif private ProtectedLong() { } protected ProtectedLong(byte[] bytes) : base(bytes) { } public static implicit operator ProtectedLong(int value) { return new ProtectedLong(BitConverter.GetBytes(value)); } public static implicit operator long(ProtectedLong value) => value == null ? 0 : BitConverter.ToInt64(value.DecodedBytes, 0); public override string ToString() { return ((long) this).ToString(); } } [Serializable] public class ProtectedFloat : Protected { #if NEWTONSOFT_JSON [JsonConstructor] #endif private ProtectedFloat() { } protected ProtectedFloat(byte[] bytes) : base(bytes) { } public static implicit operator ProtectedFloat(int value) { return new ProtectedFloat(BitConverter.GetBytes(value)); } public static implicit operator float(ProtectedFloat value) => value == null ? 0 : BitConverter.ToSingle(value.DecodedBytes, 0); public override string ToString() { return ((float) this).ToString(System.Globalization.CultureInfo.InvariantCulture); } } public abstract class Protected { #if NEWTONSOFT_JSON [JsonProperty] #endif [SerializeField] private byte[] _; private static readonly byte[] Key = System.Text.Encoding.UTF8.GetBytes("8bf5b15ffef1f485f673ceb874fd6ef0"); protected Protected() { } protected Protected(byte[] bytes) { _ = Encode(bytes); } private static byte[] Encode(byte[] bytes) { var encoded = new byte[bytes.Length]; for (var i = 0; i < bytes.Length; i++) { encoded[i] = (byte) (bytes[i] ^ Key[i % Key.Length]); } return encoded; } protected byte[] DecodedBytes { get { var decoded = new byte[_.Length]; for (var i = 0; i < decoded.Length; i++) { decoded[i] = (byte) (_[i] ^ Key[i % Key.Length]); } return decoded; } } } } Если что-то где-то забыл или натупил, пишите в комментариях =) Удачи в разработке! PS. Котик не мой, автор фотки CatCosplay. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 16-Ноя 09:19
Часовой пояс: UTC + 5