Недавно Battlestate Games, разработчики Escape From Tarkov, наняли BattlEye для реализации шифрования сетевых пакетов, чтобы мошенники не могли перехватить эти пакеты, разобрать их и использовать в своих интересах в виде радарных читов или иным образом. Сегодня, в преддверии старта нового потока курса Этичный хакер, на котором мы учим студентов искать уязвимости, делимся с вами кейсом взлома их шифрования за несколько часов. Мы начали с анализа самого "Escape from Tarkov". В игре используется Unity Engine, который, в свою очередь, использует C# — промежуточный язык, а это означает, что можно очень легко просмотреть исходный код игры, открыв его в таких инструментах, как ILDasm или dnSpy. В этом анализе мы работали с dnSpy. Unity Engine без опции IL2CPP генерирует игровые файлы и помещает их в GAME_NAME_Data\Managed, в нашем случае это EscapeFromTarkov_Data\Managed. Эта папка содержит все использующие движок зависимости, включая файл с кодом игры — Assembly-CSharp.dll, мы загрузили этот файл в dnSpy, а затем искали строку encryption и оказались здесь: Этот сегмент находится в классе EFT.ChannelCombined, который, как можно судить по переданным ему аргументам, работает с сетью: Правый клик по переменной channelCombined.bool_2, которая регистрируется как индикатор того, было ли включено шифрование, а затем клик по кнопке Analyze показывают нам, что на эту переменную ссылаются два метода: Второй из них — тот, в котором мы сейчас находимся, так что, дважды щёлкнув по первому, мы окажемся здесь: Вуаля! Есть вызов BEClient.EncryptPacket, клик по методу приведёт к классу BEClient, его мы можем препарировать и найти метод DecryptServerPacket. Этот метод вызывает функцию pfnDecryptServerPacket в библиотеке BEClient_x64.dll. Она расшифрует данные в пользовательском буфере и запишет размер расшифрованного буфера в предоставленный вызывающим методом указатель. pfnDecryptServerPacket не экспортируется BattlEye и не вычисляется EFT, на самом деле он поставляется инициализатором BattlEye, который в какой-то момент вызывается игрой. Нам удалось вычислить RVA (Relative Virtual Address), загрузив BattlEye в свой процесс и скопировав то, как игра инициализирует его. Код этой программы лежит здесь. В последнем разделе мы сделали вывод, что, чтобы выполнить все свои криптографические задачи, EFT вызывает BattlEye. Так что теперь речь идёт о реверс-инжинеринге не IL, а нативного кода, что значительно сложнее. BattlEye использует защитный механизм под названием VMProtect, который виртуализирует и изменяет указанные разработчиком сегменты. Чтобы правильно выполнить реверс-инжинеринг защищённого этим обфускатором бинарника, нужно распаковать его. Распаковка — это дамп образа процесса во время выполнения; мы сделали дамп, загрузив его в локальный процесс, а затем поработав в Scylla, чтобы сбросить его память на диск. Открытие этого файла в IDA, а затем переход к процедуре DecryptServerPacket приведут нас к функции, которая выглядит так: Это называется vmentry, она добавляет на стек vmkey, а затем вызывает обработчика виртуальной машины — vminit. Хитрость вот в чём: из-за того, что инструкции “виртуализированы” VMProtect, они понятны только самой программе. К счастью для нас, участник Секретного Клуба can1357 сделал инструмент, который полностью ломает эту защиту, — VTIL; его вы найдёте здесь. Созданный VTIL файл сократил функцию с 12195 инструкций до 265, что значительно упростило проект. Некоторые процедуры VMProtect присутствовали в дизассемблированном коде, но они легко распознаются и их можно проигнорировать, шифрование начинается отсюда: Вот эквивалент в псевдо-Си: VTIL использует свой собственный набор инструкций, чтобы ещё больше упростить код. Я перевёл его на псевдо-Си. Мы анализируем эту процедуру, войдя в 0x20e445, который является переходом к 0x1a0a4a, в самом начале этой функции они перемещают sr12 — копию rcx (первый аргумент в соглашении о вызове x64 по умолчанию) — и хранят его на стеке в [rsp+0x68], а ключ xor — в [rsp+0x58]. Затем эта процедура переходит к 0x1196fd, вот он: И вот эквивалент в псевдо-Си: Обратите внимание, что rsi — это rcx, а sr47 — это копия rdx. Так как это x64, они вызывают 0x3dccb7 с аргументами в таком порядке: (rcx, rdx, r8). К счастью для нас, vxcallq во VTIL означает вызов функции, приостановку виртуального выполнения, а затем возврат в виртуальную машину, так что 0x3dccb7 — не виртуализированная функция! Войдя в эту функцию в IDA и нажав F5, вы вызовете сгенерированный декомпилятором псевдокод: Этот код выглядит непонятно, в нём какие-то случайные ассемблерные вставки, и они вообще не имеют значения. Как только мы отменим эти инструкции, изменим некоторые типы var, а затем снова нажмём F5, код будет выглядеть намного лучше: Эта функция расшифровывает пакет в несмежные 4-байтовые блоки, начиная с 8-го байта, с помощью ключа шифра rolling xor. Примечание от переводчика: Rolling xor – шифр, при котором операция xor буквально прокатывается [отсюда rolling] по байтам: Первый байт остаётся неизменным. Второй байт — это результат xor первого и второго оригинальных байтов. Третий байт — результат XOR изменённого второго и оригинального третьего байтов и так далее. Реализация здесь. Продолжая смотреть на ассемблер, мы поймём, что она вызывает здесь другую процедуру: Эквивалент на ассемблере x64: Прежде чем мы продолжим разбирать вызываемую им функцию, мы должны прийти к следующему выводу: сдвиг бессмыслен из-за того, что флаг переноса не установлен, а это приводит к возвращаемому из инструкции sbb значению 0; в свою очередь, это означает, что мы не на правильном пути. Если поискать ссылки на первую процедуру 0x1196fd, то увидим, что на неё действительно ссылаются снова, на этот раз с другим ключом! Это означает, что первый ключ на самом деле направлял по ложному следу, а второй, скорее всего, правильный. Хороший Бастиан! Теперь, когда мы разобрались с реальным ключом xor и аргументами к 0x3dce60, которые расположены в таком порядке: (rcx, rdx, r8, r9, rsp+0x20, rsp+0x28). Переходим к этой функции в IDA, нажимаем F5 — и теперь прочитать её очень легко: Мы знаем порядок аргументов, их тип и значение; единственное, что осталось, — перевести наши знания в реальный код, который мы хорошо написали и завернули в этот gist. Это шифрование было не самым сложным для реверс-инжиниринга, и наши усилия, безусловно, были замечены BattlEye; через 3 дня шифрование было изменено на TLS-подобную модель, где для безопасного обмена ключами AES используется RSA. Это делает MITM без чтения памяти процесса неосуществимым во всех смыслах и целях. Если вам близка сфера информационной безопасности — то вы можете обратить свое внимание на наш специальный курс Этичный хакер, на котором мы учим студентов искать уязвимости даже в самых надежных системах и зарабатывать на этом. Узнайте, как прокачаться и в других специальностях или освоить их с нуля: Профессия Data Scientist Профессия Data Analyst Курс по Data Engineering ПРОФЕССИИ   Профессия Fullstack-разработчик на Python   Профессия Java-разработчик   Профессия QA-инженер на JAVA   Профессия Frontend-разработчик   Профессия Этичный хакер   Профессия C++ разработчик   Профессия Разработчик игр на Unity   Профессия Веб-разработчик   Профессия iOS-разработчик с нуля   Профессия Android-разработчик с нуля КУРСЫ   Курс по Machine Learning   Курс "Machine Learning и Deep Learning"   Курс "Математика для Data Science"   Курс "Математика и Machine Learning для Data Science"    Курс "Python для веб-разработки"   Курс "Алгоритмы и структуры данных"   Курс по аналитике данных   Курс по DevOps Рептилоид