В данной статье я хочу рассказать про обфускацию, макросы, виртуализацию и как это может помочь в защите от злоумышленников (в том числе в роблоксе), затрону также препроцессор и его работу.
Для начала следует рассказать что такое обфускация, обфускация это намеренное усложнение какого либо кода, не меняя его действие.
Применяется обфускация чтобы код было труднее восстановить в исходное состояние, читать и анализировать (декомпилировать) (1 в 1 - невозможно) злоумышленниками.
пример обфускации:
local a = 45
print(a + 2) -- **до обфускации**
local _ = 45
local __ = 2
local function ___(s, s1)
return s+s1
end
print(___(_, __)) – **после**
Однако показанная выше обфускация является слишком простой (для злоумышленика понадобится секунда чтобы код расшифровать), именно поэтому его можно как либо усложнять все дальше и дальше.
более сложный пример:
local _ = {
[676963] = function(_s, __, ___)
local _nil = utf8.len(___)
return _s[_nil][17](_s, __)
end,
[2] = {
[17] = function(_s, ____)
return ____ + 2
end,
},
[1] = {
[17] = function(_s, ____)
local s = ____
local _t = _s[129](____)
_t[1] = 5
_t[2] = 8
return _t
end,
},
[129] = function(__)
return table.create(__)
end,
}
local __ = _[676963](_, 45, "/?")
print(__)
(во всех случаях в оутпуте покажет 47)
Для обфускации часто применяется виртуализация и макросы, о чем я и хочу рассказать.
В луау как таковых макросов нету, поэтому для начала расскажу на примере другого языка (C, C++)
Что такое макросы?
Чтобы понять макросы нужно сначала узнать что такое препроцессор.
В C, C++ и других языках есть препроцессор, он анализирует код перед тем как его скомпилировать, и в этом анализе он выполняет команды команды директив (#include, #define, #ifdef и все что начинается с #), убирает комментарии и подставляет вместо диграфов и триграфов эквивалентные их значения.
Макросы (также константы), используются чтобы определить какой либо фрагмент кода.
Простейший пример константы:
#define IMPORTANT_NUMBER 2949498568137247
После такого определения в коде можно писать:
long long value = IMPORTANT_NUMBER;
А перед компиляцией препроцессор подставит вместо IMPORTANT_NUMBER заданное число:
long long value = 2949498568137247;
Макросы же можно легко спутать с обычной функцией, однако она не компилируется как функция, а просто текстово подменяется при анализе препроцессора.
пример:
#define ADD(A, B) ((A) + (B))
Дальше в коде это можно будет использовать как:
int result = ADD(45, 2);
printf("%d\n", result);
А препроцессор преобразует этот код в:
int result = ((45) + (2));
printf("%d\n", result);
Как же тогда связаны макросы и обфускация кода, особенно в луау, в котором их нету?
Макросы интересны для обфускации тем, что для злоумышленника они создают дополнительную сложность, так как он не видит никакого макроса, а только сгенерированный код после процесса который сделал препроцессор.
Теперь следует рассказать зачем нужны макросы роблокс разработчику, который в теории хочет хоть немного защитить свою игру.
Тут уже речь заходит о виртуализации.
Что такое виртуализация в контексте обфускации?
Под виртуализацией в контексте обфускации я не подразумеваю - запуск программы внутри виртуальной машины. Виртуализация означает другое, при ней обычный код превращается в набор специальных инструкций, которые выполняются собственной виртуальной машиной внутри самого скрипта.
Если говорить простым языком, то любой код можно представить как инструкции.
local a = 45
print(a + 2)
- Взять число 45
- Взять число 2
- Сложить эти числа
- Вывести их сумму
При виртуализации обфускатор может превратить любой код в свои собственные инструкции, что еще сильнее усложнит его будущий анализ и декомпилирование для злоумышленника.
Простой пример виртуализации без самого кода виртуализации:
local program = { { 83, 45 }, { 83, 2 }, { 17 }, { 44 }, { 0 }}
в этой короткой программе некоторые числа имеют свое значение, то-есть они задают инструкцию. Например:
83 - засунуть число 45 и число 2 в стек программы
17 - cложить числа
44 - вывести сумму
0 - HALT/END/STOP, то-есть означает остановку выполнения байткода
все числа которые означают инструкции называются opcodes.
Однако в хороших виртуализаторах существуют разные опкоды для разных блоков кода, например в одном блоке который подлежит виртуализации число 83 значит сложение, а в другом может не значить ничего.
(Учтите, что виртуализировать нужно не все подряд, а только некоторые функции и блоки кода, так как например если каждый кадр у вас что то виртуализируется то могут появится лаги)
Совокупность особенностей виртуализации и делает ее хорошим препятствием для декомпиляции и чтения кода, однако все же стоить помнить, что это все равно можно будет изучить, однако повторюсь что вопрос стоит в том займет это 1 минуту или даже месяцы.
Поэтому подводя итог, виртуализация это мощный, но не бесплатный инструмент в плане производительности. Ее нужно использовать не везде, а там, где защита действительно важнее скорости и простоты кода.
Для роблокса используют разные обфускаторы, однако хороших и которые действительно сложно обойти мало, в пример могу привести MoonVeil, который использует множество разных техник для обфускации, включая те о которых я уже рассказал.
Чем больше слоев разных техник происходит при обфускации - тем лучше этот обфускатор (зависит конечно, от самих техник)
пример слоев:
- превращение кода в абстрактное синтаксическое дерево (нужно для последующих этапов, не является защитным слоем)
- переименование переменных, функций и локальных идентификаторов
- изменение control flow, то-есть порядка выполнения кода с помощью разных техник
- запутывания строк
- изменения структуры кода, добавление разных функций для его запутывания
- виртуализация отдельных функций
- итоговая генерация кода
Обфускация конечно помогает замедлить анализ клиентского кода, но не должна быть основной защитой. Про это все говорят но остается единственным лучшим решением для роблокс разработчиков - держать все самое важное на сервере и валидировать все что говорит клиент.
Можно подвести итог: обфускация не делает код невзламываемым, она повышает стоимость анализа. Хороший обфускатор работает слоями: запутывает имена, строки, числа, структуру кода, control flow и тд, но в роблоксе это должно использоваться вместе с серверной валидацией, а не вместо неё.
Вставил бы больше ссылок но новые пользователи могут вставлять только 2.