Slrr как собрать двигатель новичку
Добавляем в игру мультиплеер с помощью Node.JS и Frida. Часть 1
На Хабре уже есть пару упоминаний об инструменте Frida («Frida-node или немножко странного кода», «Точки соприкосновения JavaScript и Reverse Engineering»). В одной статье уже упоминается использование Frida на практике, однако почти везде инструмент используют как фреймворк для реверс-инжиниринга и исследования функционала программ (может даже взлом).
Я же хочу рассказать о процессе превращения одной любимой для меня однопользовательской игрушки в полноценную, многопользовательскую.
Сразу хочу предупредить: в подобном процессе я почти новичок, поэтому не удивлюсь, если в меня полетят гнилые помидоры от гуру системного программирования. С другой стороны я надеюсь, что моя статья позволит начать использовать Frida (и не только) другим новичкам, а из гневных комментариев гуру я почерпну что-то полезное для себя. Также продолжать написание статей (при положительных оценках конечно) я буду прямо в процессе разработки мультиплеера.
Дано:
Node.Js + Frida + frida-node
Игра Street Legal Racing: Redline
SLRR: java pack
Установка Frida
Что с игрой
Небольшая история игры.
Сама игра была выпущена в 2003 году компанией Activision. Игру разработала Венгерская компания Invictus Games. Случилось так, что игра, толи из-за плохого маркетинга, толи из за явного несоответствия интересам аудитории, не взлетела. Несмотря на это, у игры образовалось несколько сообществ фанатов, скорее всего из-за особенностей
геймплея: Автомобиль можно практически полностью разбирать и собирать, менять и настраивать детали, очень реалистичная (на тот момент) физика, деформация кузова, и поведение на дороге. Несколько раз сообщество пыталось заполучить исходные коды игры, подписывая петиции и отправляя их в Invictus. Разработчики отказывались их передавать, ссылаясь на проблемы с правами и компанией Activision, которой в данный момент принадлежат права. Несмотря на это, как-то случилось так, что в сеть утекло часть java кода игры. В саму игру встроена какая-то старая и урезанная версия JVM (Java Virtual Machine, даже без поддержки throw-catch), полностью отсутствуют функции для работы с сетью, а файлы сохраняются и открываются только во встроенном в игру формате.
На данный момент нашим земляком, с ником RAXAT был выпущен неофициальный патч 2.3.0LE, который стал
стандартом для этой игры. На базе этого патча я и решил добавить в игру поддержку мультиплеера.
Неудачные и удачные попытки
Сразу хочу заметить, что я не первый из сообщества, кто захотел реализовать мультиплеер в игре. Также я сам сделал несколько попыток добавить нужные функции в игру. Изначально я делал это с использованием dll, написанной на Delphi, которая инжектировалась в процесс. Такой метод работал, но добавлять что-либо в модуль было достаточно трудоемкой задачей.
На помощь пришел инструмент Frida, который позволяет внедрять в процесс JavaScript движок V8, и работать с процессом «изнутри». Самой важной задачей по началу являлось добавить обмен данными между JVM игры и внешним процессом Node.js.
Код проекта валяется на Github: https://github.com/lailune/SLRRMultiplayer однако представляет из себя исключительно тестовую версию, и то, что я имею на данный момент.
Ссылку на игру не привожу по понятным причинам.
Скрипт, загружаемый внутрь процесса будет называться injectScript.js. Название самого скрипта приложения не важно, я назвал его app.js.
Наш скрипт будет запускать бинарник игры, и передавать pid процесса модулю Frida для внедрения скрипта.
Сама функция AttachHook содержит весь код инициализации Frida:
- Подключаемся к процессу
- Загружаем наш внутренний скрипт во внедренный V8 (в это время V8 проверяет скрипт на ошибки и компилирует его в байт код)
- Ставим обработчик сообщений из скрипта
- Запускаем скрипт, и выводим сообщение об успехе или ошибке.
В случае прихода какой-либо «посылки» (payload) из нашего внедренного скрипта мы должны его обработать по своему. Для этого есть функция handleMessage, в которой, правда, пока реализован только прием информации о позиции игрока в виртуальном пространстве.
Теперь мы можем распоряжаться этими данными так, как нам захочется, например: передать их на сервер.
injectScript.js
Способ передачи данных из игры я выбрал крайне возмутительный: перехватываю вызов CreateFileA.
Почему:
- Так проще всего. Достаточно открыть файл с
«нужным названием», внутри которого будут данные, которые мы передаем. - Я так и не смог научить Frida искать нужный
текст в памяти приложения, для дальнейшего использования определенной
области памяти. - Это работает.
Важный момент: в этом скрипте надо тщательно следить за созданием переменных и выделением памяти. Если будет создано слишком много переменных, то в один прекрасный момент запустится сборщик мусора, и процесс подвиснет на время намертво. Также если постоянно выделять память, получится утечка, с которой даже GC не справится.
Возможно на этом этапе у многих появятся замечания, которые буду рад услышать.
Немного Java
Поскольку игра использует внутри себя урезанную версию Java, придется немного написать на этом великолепном языке.
Интересный момент: прямо внутрь игры встроен компилятор java в байт код для JVM, достаточно положить java файл в соответствующую директорию внутрь папки src, и при запуске игры будет создан class файл.
Для теста я использовал класс City (реализует базовые функции для управления городом в игре). В дальнейшем планирую вынести реализацию своего псевдо-сокета в отдельный глобальный класс.
Пока реализует только передачу данных наружу.