Макросы и виртуализация в обфускации

В данной статье я хочу рассказать про обфускацию, макросы, виртуализацию и как это может помочь в защите от злоумышленников (в том числе в роблоксе), затрону также препроцессор и его работу.

Для начала следует рассказать что такое обфускация, обфускация это намеренное усложнение какого либо кода, не меняя его действие.
Применяется обфускация чтобы код было труднее восстановить в исходное состояние, читать и анализировать (декомпилировать) (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)
  1. Взять число 45
  2. Взять число 2
  3. Сложить эти числа
  4. Вывести их сумму

При виртуализации обфускатор может превратить любой код в свои собственные инструкции, что еще сильнее усложнит его будущий анализ и декомпилирование для злоумышленника.

Простой пример виртуализации без самого кода виртуализации:
local program = { { 83, 45 }, { 83, 2 }, { 17 }, { 44 }, { 0 }}

в этой короткой программе некоторые числа имеют свое значение, то-есть они задают инструкцию. Например:
83 - засунуть число 45 и число 2 в стек программы
17 - cложить числа
44 - вывести сумму
0 - HALT/END/STOP, то-есть означает остановку выполнения байткода

все числа которые означают инструкции называются opcodes.

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

(Учтите, что виртуализировать нужно не все подряд, а только некоторые функции и блоки кода, так как например если каждый кадр у вас что то виртуализируется то могут появится лаги)

Совокупность особенностей виртуализации и делает ее хорошим препятствием для декомпиляции и чтения кода, однако все же стоить помнить, что это все равно можно будет изучить, однако повторюсь что вопрос стоит в том займет это 1 минуту или даже месяцы.

Поэтому подводя итог, виртуализация это мощный, но не бесплатный инструмент в плане производительности. Ее нужно использовать не везде, а там, где защита действительно важнее скорости и простоты кода.

Для роблокса используют разные обфускаторы, однако хороших и которые действительно сложно обойти мало, в пример могу привести MoonVeil, который использует множество разных техник для обфускации, включая те о которых я уже рассказал.

Чем больше слоев разных техник происходит при обфускации - тем лучше этот обфускатор (зависит конечно, от самих техник)
пример слоев:

  1. превращение кода в абстрактное синтаксическое дерево (нужно для последующих этапов, не является защитным слоем)
  2. переименование переменных, функций и локальных идентификаторов
  3. изменение control flow, то-есть порядка выполнения кода с помощью разных техник
  4. запутывания строк
  5. изменения структуры кода, добавление разных функций для его запутывания
  6. виртуализация отдельных функций
  7. итоговая генерация кода

Обфускация конечно помогает замедлить анализ клиентского кода, но не должна быть основной защитой. Про это все говорят но остается единственным лучшим решением для роблокс разработчиков - держать все самое важное на сервере и валидировать все что говорит клиент.

Можно подвести итог: обфускация не делает код невзламываемым, она повышает стоимость анализа. Хороший обфускатор работает слоями: запутывает имена, строки, числа, структуру кода, control flow и тд, но в роблоксе это должно использоваться вместе с серверной валидацией, а не вместо неё.

Вставил бы больше ссылок но новые пользователи могут вставлять только 2.

4 лайка