Разбирая наследие Каина: Похититель душ

Итак, я недавно начал заниматься скоростным бегом, и когда я это сделал, я решил рекламировать себя как относительно компетентный в разборке программ и выяснении того, как они работают. В течение дня я получил этот ответ

Это сообщение было от одного из основных участников сообщества этой игры, и, конечно же, я принял вызов.

Итак, я провел небольшое исследование и ответил:

Оказывается, они уже знали об этом, как показано в этом ответе.

Итак, я покопался еще раз. К моему удивлению, в выпуске игры GOG.com есть файл с именем kain2.map, который, вместо того, чтобы быть файлом уровня, как я, на самом деле был картой адресов компоновщика, которая выглядит примерно так

kain2
Timestamp is 381a30c7 (Fri Oct 29 16:41:59 1999)
Preferred load address is 00400000
Start         Length     Name                   Class
 0001:00000000 000eab1eH .text                   CODE
 0002:00000000 000002b8H .idata$5                DATA
 0002:000002b8 00002603H .rdata                  DATA
 0002:000028bc 000000b4H .idata$2                DATA
 0002:00002970 00000014H .idata$3                DATA
 0002:00002984 000002b8H .idata$4                DATA
 0002:00002c3c 00000c3aH .idata$6                DATA
 0002:00003876 00000000H .edata                  DATA
 0003:00000000 00000004H .CRT$XCA                DATA
 0003:00000004 00000004H .CRT$XCZ                DATA
 0003:00000008 00000004H .CRT$XIA                DATA
 0003:0000000c 00000004H .CRT$XIC                DATA
 0003:00000010 00000004H .CRT$XIZ                DATA
 0003:00000014 00000004H .CRT$XPA                DATA
 0003:00000018 00000004H .CRT$XPX                DATA
 0003:0000001c 00000004H .CRT$XPZ                DATA
 0003:00000020 00000004H .CRT$XTA                DATA
 0003:00000024 00000004H .CRT$XTZ                DATA
 0003:00000030 00017a24H .data                   DATA
 0003:00017a58 007606b8H .bss                    DATA
 0004:00000000 00000178H .rsrc$01                DATA
 0004:00000180 000015f8H .rsrc$02                DATA
Address         Publics by Value              Rva+Base     Lib:Object
0001:00000000       _ALUKA_ControllersEnabled  00401000 f   aluka.obj
 0001:00000020       _ALUKA_SetPitch            00401020 f   aluka.obj
 0001:00000080       _ALUKA_EnableControllers   00401080 f   aluka.obj
 0001:00000170       _ALUKA_DisableControllers  00401170 f   aluka.obj
 0001:00000230       _ALUKA_SetSwimBodyTwist    00401230 f   aluka.obj
 0001:00000370       _ALUKA_NotDaylight         00401370 f   aluka.obj
 0001:000003a0       _ALUKA_CapDepth            004013a0 f   aluka.obj
 0001:00000460       _ALUKA_AngleTooWide        00401460 f   aluka.obj
 0001:000004e0       _ALUKA_VectorFromPitchYaw  004014e0 f   aluka.obj
 0001:00000590       _ALUKA_FacingVector        00401590 f   aluka.obj
 0001:00000660       _ALUKA_SimpleLineCheck     00401660 f   aluka.obj
 0001:00000720       _ALUKA_TerrainInPath       00401720 f   aluka.obj
 0001:00000850       _ALUKA_ApplyIncr           00401850 f   aluka.obj
 0001:00000890       _ALUKA_ApplyForwardAccel   00401890 f   aluka.obj
 0001:000008f0       _ALUKA_ApplyAngularAccel   004018f0 f   aluka.obj
 0001:00000990       _ALUKA_ApplyRots           00401990 f   aluka.obj
 0001:00000b10       _ALUKA_MoveForward         00401b10 f   aluka.obj
 0001:00000c00       _ALUKA_FixPitch            00401c00 f   aluka.obj

В файле компоновщика сначала объявляется имя исполняемого файла без расширения, затем метка времени, адрес загрузки, а затем разделы исполняемого файла.

В следующем разделе более подробно описывается, что находится в каждом разделе исполняемого файла. например 0001:00000000 00EAB1EH .text CODE - это раздел, начинающийся со смещения 0x0 с длиной 961310, которая содержала CODE. Однако большинство этих деклараций не совсем верны, поскольку .CRT$XXX разделы содержат данные и функции, относящиеся к Windows Common Run Time, ядру C / C ++ в Windows.

После этого идет то, что нам небезразлично, - имена адресов. Они принимают форму XXXX:OFFSET NAME OFFSET+BASE OBJFILE. Это было бесценно при работе с дизассемблированием, так как без отладочных файлов все функции именуются как-то вроде sub_offset, где смещение - это адрес функции. Этот файл позволил мне определить имена функций и их исходный файл (поле OBJFILE). Однако, несмотря на то, что изначально функции переименовывались вручную, мне это вскоре надоело. В конце концов, я решил использовать скриптовый интерфейс IDA Python для автоматизации процесса, поскольку существующие плагины не загрузили файл карты полностью. Скрипт немного хакерский, но работает надежно и выполнил свою работу.

Де-искажение и искривление

Итак, теперь я получил набор удобных описательных имен, а также, в некоторых случаях, наборов текста. Эти типы были получены из нечитаемой, но весьма полезной системы изменения имен C / C ++. Чтобы дать пример искаженного имени, вот функция обратного вызова DirectSound в искаженной форме ?DSCallback@@YGHPAU_GUID@@PBD1PAX@Z. Это куча искаженного мусора, лишь небольшая часть которого действительно что-то значит. Однако в этом сбивающем с толку хламе есть смысл. И GCC, и MSVC имеют свои собственные схемы изменения имен, при этом Microsoft легко идентифицируется благодаря использованию @ символов. Ниже приведен общий формат искажения

Сделав это самостоятельно или используя demangler.com, вы получите это, int __stdcall DSCallback(struct _GUID *, const char *, const char *, void *);, гораздо более полезное имя, чем DSCallback@@YGHPAU_GUID@@PBD1PAX@Z. К счастью, IDA справилась с этим для меня в целом, автоматически де-искажая имена из файла карты для меня, хотя в некоторых случаях мне приходилось делать это вручную. Это позволило мне избавиться от обфускации многих функций CRT и Direct3D. Среди этих искаженных имен есть и такие, RandomName@4. Это имя просто означает, что существует функция RandomName(x, x, x, x) или функция с именем RandomName с 4 аргументами. Еще одна интересная особенность этой схемы изменения имен заключается в том, что все строки будут оцениваться как string.

Проблема набора библиотечных функций

IDA имеет достойную поддержку для большинства версий Visual C ++, но та, с которой была скомпилирована Soul Reaver, была VC 6.0 вместе с DirectX 6.0, обе настолько старые, что мне было очень трудно найти копию SDK для каждой из них. Безусловно, DirectX был намного сложнее, и мне пришлось вместо этого использовать копию DirectX 6.1 SDK. Используя систему подписей FLIRT от IDA, я создал набор подписей для DirectX 6.1 и Visual C ++ 6.0 Common Runtime. Это дало мне еще больше информации о типах. В то время как IDA поставляется с довольно большим количеством встроенных библиотечных сигнатур, в ней отсутствуют многие другие. Существует хороший репозиторий FLIRTDB с сигнатурами для многих архитектур Visual C ++, Boost и некоторых других библиотек.

Остатки PSX SDK

На этом этапе я заметил массу функций с именами вроде gte_AVSZ4 и gte_NCLIP. Я сразу же подумал, что это должна быть какая-то оптимизированная библиотека, больше или равная библиотеке, но после некоторого поиска в Google, который привел меня к типу POLY_GT4, который затем привел меня к японскому зеркалу руководств PSX SDK, который затем привел меня получить фактический SDK для PSX и загрузить для него заголовки. Оказывается, что все эти функции связаны с рисованием графики на PSX, и многие функции в SR1 предназначены для преобразования этих POLY_GT4 в многоугольники DirectX, например DrawPolyF4, DrawPolyGT4 и многие другие с аналогичными названиями. Эти функции, кажется, берут многоугольные структуры PSX и визуализируют их с помощью DirectX. Кроме того, существует большое количество функций с именем spuXXXXXXXXX, все из которых ничего не возвращают и ничего не делают, явно предназначенных для работы с PSX и замененных на DirectSound в версии для ПК. Интересно, что большая часть звукового движка для игры на ПК была сломана, музыка окружающей среды не затухала, часто не воспроизводилась или часто воспроизводилась неправильная музыка. Скорее всего, это было вызвано необходимостью перехода на DirectSound.

Классы?

Некоторые из вас могут задаться вопросом об использовании классов в игре, и, к счастью для меня, игра была написана с использованием C и скомпилирована с использованием библиотек C ++, то есть присутствовали только структуры. Идентификация структур может быть сложной задачей, но если вы столкнетесь с декомпилированным кодом в IDA с большим количеством нечетных смещений, таких как variableName[variable2+4]; variablename[variable2+8];, вы обычно обнаружите, что имеете дело со структурой. Это также очевидно в сборке, где вместо этого используются смещения от указателя стека, такие как mov edx, [esp+0A0h+var_90], var_90 - смещение переменной внутри функции.

Заключение

Итак, на данный момент это все от меня, надеюсь, вам понравилась эта статья, моя первая статья на Medium, и если вы хотите взглянуть на прогресс, достигнутый на данный момент, работу можно найти здесь.

Бонус: интересные струны