1. Введение в архитектуру Unity и инструменты декомпиляции кода
Введение в архитектуру Unity и инструменты декомпиляции кода
Добро пожаловать в курс «Архитектура и код Among Us: от анализа до тестирования». Мы начинаем погружение в мир реверс-инжиниринга и анализа игрового кода на примере одной из самых популярных инди-игр последних лет. Чтобы понять, как работает Among Us, как устроена её сетевая логика и как писать для неё моды или тесты, нам необходимо сначала разобраться с фундаментом, на котором она построена — движком Unity.
В этой статье мы разберем, чем отличается архитектура Among Us от простых Unity-проектов, что такое IL2CPP, почему вы не найдете привычного кода C# в папке с игрой и какие инструменты нам понадобятся, чтобы превратить набор байтов обратно в читаемую структуру.
Архитектура Unity: Mono против IL2CPP
Когда разработчик нажимает кнопку «Build» в Unity, движок должен превратить написанный на C# код в то, что сможет запустить компьютер игрока. Существует два основных способа, которыми Unity делает это: Mono и IL2CPP.
Mono (JIT-компиляция)
В старых или простых проектах Unity часто используется среда выполнения Mono. При этом код C# компилируется в промежуточный язык CIL (Common Intermediate Language) и упаковывается в файлы .dll (обычно Assembly-CSharp.dll).
Когда игра запускается, Mono использует JIT (Just-In-Time) компиляцию, чтобы переводить CIL в машинный код прямо во время работы программы. Для исследователя это «легкий режим»: такие игры декомпилируются почти до исходного кода с сохранением имен переменных и логики методов.
IL2CPP (AOT-компиляция)
Among Us, как и большинство современных мобильных и кроссплатформенных игр, использует бэкенд IL2CPP (Intermediate Language to C++). Это технология, которая меняет правила игры.
!Схема преобразования C# кода в нативный машинный код через промежуточную генерацию C++.
Процесс выглядит так:
В результате мы получаем не аккуратную библиотеку .dll с байт-кодом, а исполняемый файл, содержащий чистый машинный код. В папке с игрой Among Us вы увидите файл GameAssembly.dll. Это огромная библиотека, где слиты воедино все скрипты игры, но в виде ассемблерных инструкций, а не C# классов.
Роль Metadata в IL2CPP
Если весь код превратился в машинные инструкции, как Unity понимает, где какой класс и метод? Здесь на сцену выходит файл global-metadata.dat.
Этот файл находится в папке Among Us_Data/il2cpp_data/Metadata/. Он содержит карту всей игры:
Однако сам код методов (тело функций) находится в GameAssembly.dll. Метаданные лишь говорят: «Метод PlayerControl.FixedUpdate находится по смещению 0x123456».
Для понимания того, как мы находим нужный код, рассмотрим простую формулу вычисления адреса функции в памяти:
где — это абсолютный адрес функции в оперативной памяти запущенного процесса, — базовый адрес загрузки модуля (в нашем случае GameAssembly.dll), а — смещение (offset) функции, которое мы узнаем благодаря анализу метаданных.
Без global-metadata.dat файл GameAssembly.dll был бы просто набором безымянных функций, и анализ занял бы годы.
Инструменты исследователя
Для работы с архитектурой IL2CPP нам понадобится специфический набор инструментов. Обычные декомпиляторы C# здесь бессильны без предварительной подготовки.
1. Il2CppDumper
Это самый важный инструмент в нашем арсенале. Его задача — взять GameAssembly.dll и global-metadata.dat и восстановить структуру проекта.
Что делает Il2CppDumper:
Dummy DLL — это файлы .dll, которые содержат все классы, методы и поля игры, но тела методов в них пусты. Они выглядят примерно так:
Кроме того, Il2CppDumper создает файл script.json (для использования в IDA/Ghidra) и dump.cs (текстовое представление всех классов).
2. dnSpy
dnSpy — это мощный отладчик и редактор сборок .NET. Мы будем использовать его для просмотра тех самых Dummy DLL, которые сгенерировал Il2CppDumper.
С помощью dnSpy мы сможем:
GameData, PlayerControl, ShipStatus).isImpostor — это boolean).!Интерфейс dnSpy, отображающий структуру классов игры.
3. Ghidra или IDA Pro (для продвинутых)
Если нам нужно узнать, как именно работает метод (например, по какой формуле рассчитывается дистанция убийства), нам придется смотреть машинный код внутри GameAssembly.dll. Для этого используются дизассемблеры вроде Ghidra (бесплатный) или IDA Pro.
Мы загружаем в них GameAssembly.dll и применяем скрипт, сгенерированный Il2CppDumper. Скрипт переименовывает безымянные функции sub_182A40 в понятные PlayerControl$$FixedUpdate.
Практический подход к анализу Among Us
В рамках этого курса мы сосредоточимся на структурном анализе. Наша цель — понять логику, не обязательно вникая в каждую ассемблерную инструкцию.
Алгоритм действий, который мы будем применять в следующих статьях:
Assembly-CSharp.dll (из папки DummyDll) в dnSpy.Почему это законно?
Важно отметить, что мы занимаемся анализом в образовательных целях. Мы не взламываем игру для получения преимущества в онлайне, не распространяем пиратские версии и не нарушаем целостность работы серверов. Мы изучаем архитектуру кода, что является стандартной практикой в сфере кибербезопасности и разработки ПО.
Структура данных Among Us
Игра построена на классической компонентной системе Unity, но имеет свои особенности. Весь геймплейный код сосредоточен в сборке Assembly-CSharp.
Ключевые элементы, которые мы будем искать:
> «Понимание структуры данных — это 80% успеха в реверс-инжиниринге. Если вы знаете, где лежат данные, вы знаете, как ими управлять».
Заключение
Мы выяснили, что Among Us использует технологию IL2CPP, что делает её код недоступным для простого чтения, но не защищает от анализа полностью. Используя связку GameAssembly.dll + global-metadata.dat и инструмент Il2CppDumper, мы можем восстановить карту классов игры.
В следующей статье мы перейдем от теории к практике: настроим рабочее окружение и детально разберем структуру класса PlayerControl, чтобы понять, как игра обрабатывает движения и действия игроков.
Готовы к погружению в код? Тогда переходим к проверке знаний.