Содержание статьи
info
Если тебе тоже интересно создание модификаций для «Мора» — предлагаю объединить наши усилия. Добавляйся в чат по реверсу игры!
Сбор информации
Скрипты хранятся в файле Scripts.
. Формат самодельный и простейший, можешь почитать, как их вскрывают, в одной из моих старых статей, где я показывал, как вскрыть и изменить ресурсы «Ядерного титбита». Я решил не тратить на это время и распаковал архив готовым софтом VFS Explorer.
Внутри лежит куча бинарных файлов с расширением .
. Первые четыре байта не содержат никакой заметной сигнатуры.
Обычно разработчики встраивают проверенные скриптовые языки вроде Lua или Python. Раз файл бинарный, значит, скрипт был скомпилирован. Однако по разреженному (полному нулей) байт‑коду стало ясно, что формат скриптов проприетарный. Только самодельные языки так расточительно используют пространство файла — сравни с оптимизированными процессорными опкодами. Нули в байт‑коде могут означать, что команда занимает фиксированное количество байтов, а аргументов к ней не завезли.
Первым делом я пошел гуглить моды и наткнулся на частичный разбор формата файла. Он помог на первоначальном этапе, но ближе к середине списка заголовков документ обрывается, не оставляя никаких ссылок на продолжение. Так что пришлось взять в руки старый добрый отладчик и выяснить все самостоятельно.
Этого исследования не было бы, если бы недавно не была опубликована альфа‑версия игры. Примечательна она тем, что исполняемые файлы поставляются вместе с файлами .
, которые содержат прототипы функций, а также все используемые структуры. Именно отладочные символы позволили сократить время исследования с нескольких месяцев до пары недель.
Ищем точку входа
Чтобы узнать, какие поля есть в закрытом формате на самом деле, поищем место в коде игры, где читаются скрипты. Для этого запустим игру в x64dbg, современной альтернативе OllyDbg. Отладчик автоматически подгружает с диска символы, расставляя адресам в памяти оригинальные названия.
Но сначала отключим полноэкранный режим, для этого изменим config.
:
[Video]
XRes = 1024
YRes = 768
Fullscreen = 0
Теперь с игрой можно работать в отладчике, не опасаясь зависаний. Посмотрим, какие файлы вообще открываются игрой. Для этого используем условные точки останова. Переходим на WinAPI-функцию CreateFileA
и создаем такой бряк:
Текст журнала: OPEN: {ansi@[ESP+4]}
Условие остановки = 0
Условие добавления в журнал = 1
В текст журнала можно вставлять форматирование вместе с обрабатывающими функциями. В данном случае мы просим отладчик добавлять в журнал запись о каждом открытом файле, получая путь к файлу через стек, то есть, по сути, просто берем строку из первого аргумента функции CreateFileA
.
Запускаем игру и видим в журнале полотно из игровых ассетов:
OPEN: C:\games\pathologic\alpha\data\scripts\fire.bin
Вот это уже интересно. Скрипты запакованы в архиве, но в целях отладки или патчинга игра сначала пытается прочитать их с диска. И только если там их нет, читает содержимое файлов из игрового архива. Это значительно облегчает создание модов.
Допустим, я хочу остановить отладчик на конкретном файле. Для этого слегка изменим точку останова:
Условие остановки = strstr(ansi([ESP+4]), "fire.bin")
Функция strstr
возвращает единицу, если в строке, которую вернет ansi
, будет подстрока fire.
. Перезапустив отладчик, ловим остановку на нужном файле. Смотрим в стеке адрес, откуда пришел вызов:
возврат к engine.CScriptManager::RunScript+88 из ???
Похоже на точку запуска скрипта! Самое время открыть Engine.
в IDA Pro.
Исследуем заголовки
IDA показывает не так много входящих ссылок на CreateFileA
, быстро находим фрагмент кода внутри RunScript
, читающий файл скрипта через виртуальную файловую систему (VFS).
v8 = __v.second.m_pManager->m_pFS->CreateMappedLoadObject(__v.second.m_pManager->m_pFS, pszScriptName);// (...)ScriptDataPtr = Script_2->GetMemoryPointer(Script_2);CScript::CScript(ScriptDataPtr, __v.second.__vftable, v43)
Тело скрипта передается в конструктор объекта CScript
. IDA весьма ограниченно умеет работать с программами, написанными на C++, поэтому CScript::
— это просто имя функции, никак не связанное с классом в целом. Многие классы имеют таблицу виртуальных функций, по которой можно восстановить их принадлежность, даже если у нас под рукой нет символов.
void __userpurge CScript::CScript(CScript *this@<ecx>, CScript *pScript, unsigned int ulSize);
Согласно прототипу функции, первый аргумент — это файл скрипта и он же является телом класса. Такое решение часто встречается в старых играх. Они экономили на сериализации и просто дампили тела классов на диск, например для создания сохранений. Посмотрим на определение класса CScript
:
struct __cppobj __unaligned __declspec(align(4)) CScript{ unsigned int m_ulGlobalVarCount; boost::scoped_array<unsigned char> m_pGlobalVarTypes; _STL::map<CEString,unsigned long,_STL::less<CEString>,_STL::allocator<_STL::pair<CEString const ,unsigned long> > > m_Properties; unsigned int m_ulDataPoolSize; boost::scoped_array<char> m_pDataPool; unsigned int m_ulGlobalCount; boost::scoped_array<CScript::GLOBAL_FUNCTION> m_pGlobals; unsigned int m_ulTaskCount; boost::scoped_array<CScript::TASK> m_pTasks; _STL::map<unsigned long,CScript::EVENT,_STL::less<unsigned long>,_STL::allocator<_STL::pair<unsigned long const ,CScript::EVENT> > > m_GlobalEvents; unsigned int m_ulCodeSize; boost::scoped_array<boost::scoped_ptr<IInstruction> > m_pCode; unsigned int m_ulRunTask; unsigned int m_ulRunOp; _STL::set<IScriptNotify *,_STL::less<IScriptNotify *>,_STL::allocator<IScriptNotify *> > m_Notify[3];};
Структура прочитана из PDB-файла. IDA не умеет нормально работать с шаблонами, поэтому эти страшные длинные строчки с кучей запятых и угловых скобок — не более чем названия сишных структур. В глаза бросается схожесть тела класса со структурой скрипта, описанной на фанатском сайте. С ней можно работать.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее