Pull to refresh

Декомпиляция RNC ProPack длиной в 5 лет

Reading time 6 min
Views 8.4K

Приветствую, друзья!



В данном материале я расскажу Вам, как на протяжении нескольких лет занимался реверсом 46 КБ (кажется — всего то!) исполняемого файла от AmigaOS, узнал много нового для себя, испробовал множество разных технологий, и, в итоге, добился своего — превратил декомпилированный Motorola M68000 ассемблерный код в C-шный код, которым может воспользоваться любой желающий.


Предыстория


Есть такая штука, как RNC ProPack (Rob Northen Compression). Это очень распространённый в своё время упаковщик данных. Им были упакованы игры под MS-DOS, Sega Genesis/Mega Drive, Game Boy, SNES, Sony Playstation, и, возможно, какие-то другие платформы. Собственно, из-за Sega Mega Drive я и взялся за исследование упаковщика.


LHA-архив по ссылке содержит исполняемые файлы под AmigaOS, и MS-DOS, что в современных реалиях работы под 64-битными ОС является пусть и не большой, но преградой к нормальному использованию утилиты.


Да, существует исходник, и якобы работающий вариант декомпрессора RNC версии 1, но, на самом деле он работает криво, сжимает хуже, и делает другие нехорошие вещи, из-за которых использовать это поделие мне не хотелось.


Конечно, есть всякие DOSBox-ы, WinUAE, но это получается как-то очень громоздко на самом деле.


Поиски истины


В первую очередь я постарался найти автора, связаться с ним, найти готовые исходники, которые портировать под современные реалии было бы проще. Но автора оказалось найти довольно таки тяжело. Rob Northen сейчас уже немолодой дед, исходников у него больше нет, а та ссылка с Aminet ведёт на архив с программами, которые он даже не компилировал (по словам автора, исходники были на чистом ассемблере, а всё остальное — не его).


UPD.:


Как я нашёл деда
Сначала это было старое интервью с ним в wayback machine. Но мыло оттуда не было уже активно. Тогда я загуглил собственно сам домен Northen Computing, доклеил rob@ к домену, и попробовал написать — получил мыло автора алгоритма.

Насколько я понял, исходники упаковщика поставлялись разработчикам игр за деньги. Нашлись несколько разных версий RNC ProPack под MS-DOS, упакованных разными версиями протекторов исполняемых файлов. Версия под AmigaOS также была упакована (самим RNC ProPack).


Подход номер раз


Сначала я попробовал начать отлаживать это барахло. На тот момент (ну, где-то лет 5 назад) для меня ближе был MS-DOS, поэтому я решил начать с него, воспользоваться IDA Pro, проанализировать исполняемый файл и всё сделать тихо, никого не трогая. :) Не вышло...


Первая проблема: файл был защищён каким-то упаковщиком исполняемых файлов под MS-DOS. Большинство современных анализаторов PE (PEID, Exeinfo PE) понятия не имели о том, что же делать с досовскими раритетами. Поэтому отмалчивались в ответ:



Помог только Detect It Easy:



Точно уже не помню как, но, через DOSBox с включенным отладчиком мне удалось всё таки кое-как распаковать WWPack:



Но сигнатуры Borland C++ в IDA не очень хотели распознавать большинство стандартных библиотечных функций (как я это понял: поверх обычных вызовов fopen были функции-обёртки, которые не получали имён). Тогда я решил самостоятельно с помощью Flare-утилит IDA собрать сигнатуры. Это немного помогло.


Всё равно не так, как хотел я… Я хотел удобства, исходников, динамическую отладку кода…
Вот, кажется, IDA Pro умеет работать с сегментами, селекторами, 16-битным кодом… Да. Но, во первых, в стандартной поставке отладчика нет. Во вторых, единственный имеющийся на тот момент дебагер-плагин (который всем известный Ильфак впоследствии добавил в виде референса в SDK):


#define DEBUGGER_ID_X86_DOSBOX_EMULATOR             10 ///< Dosbox MS-DOS emulator

… очень криво работает, виснет, debugger-hints указывают не на те целевые адреса в relative-адресах типа: DWORD PTR DS:[EAX+58h] и т.д.


Я решил его переписать! Именно эту версию всем и рекомендую использовать при отладке MS-DOS приложений.


Но, позанимавшись с этим вариантом, я понял, что и это не то… Удобства прибавилось, но цель так же далека.


А вдруг во второй раз получится?


На этот раз, спустя какое-то время, я услышал о таком замечательном проекте, как amitools, и, в частности, об одном его компоненте: vamos.


Это такой себе самостоятельный эмулятор AmigaOS (если помните, второй исполняемый файл был как раз под неё) на python. В качестве эмулятора процессора M68k там используется Musashi.


Мне показалось это отличной идеей. Написать debugger-плагин для IDA, который бы дёргал код на python, исполняя внутри нужный мне код. Но, разобраться с embedded python в тот раз мне не удалось. Собственно сам питон тогда ещё я практически не знал/не применял.
Поэтому не срослось.


Да и сам исполняемый файл упаковщика работал плохо в эмуляторе: многократно пытался упаковать один и тот же файл, какие-то системные вызовы AmigaOS вообще не работали.


Я даже пытался переписать vamos на C, чтобы встроить его в дебагер. Но крутых декомпиляторов типа python2c нету, а руками переписывать мне быстро надоело. Дело было бесперспективное.


Пробовал даже отладку через WinUAE, написал UaeIDA. Встроил в него свой код серверной части debugger-модуля, пытаясь отлаживать, сообщая о багах. Вроде дело даже как-то более-менее приемлемо сдвинулось с места. Но, непроходимые баги самого WinUAE (а так же нежелание автора возиться с частью исходника, отвечающей за отладчик), в итоге я снова зашёл в тупик.


Теперь точно выйдет


В последний раз собравшись с силами, после всех этих дурацких попыток, не приведших к желаемому результату, переписавши к тому моменту на C уже другой Amiga Cruncher (Imploder) — WinImploder, я решил снова вернуться к Amitools-варианту.


Проект вроде бы нафиксил какие-то баги, упростилась сборка проекта… Поэтому старт был дан! В итоге у меня получился вот такой Франкеншейн: Amitools_dbg. Запускаешь python-часть с флагом -dbg, из IDA запускаешь дебагер-модуль, и… вуаля — мы на точке входа в приложение. И можем даже заниматься отладкой!


Только системые API-вызовы по-прежнему оставались строчкой в дизассемблере вида:
JSR -$3A(a6), где a6 — база системной библиотеки, а -$3A — смещение API-вызова.


Порывшись в интернете, я нашёл такой когда-то актуальный проект, как ReSource. Архивы с программой до сих пор можно найти на просторах интернета, и запустить их в WinUAE. Так вот в нём лежали fd-файлики для каждой библиотеки с указанием смещений. В Amitools как раз был парсер этих fd-файлов, и я взял его себе. В итоге мой дебагер обзавёлся ещё и распознаванием таких вот системных вызовов, что помогло мне в определении некоторых врапперов над API-вызовами.


За все предыдущие годы мне приблизительно удалось выяснить C-компилятор, который использовался кем-то для получения AmigaOS исполняемого файла: SAS/C какой-то древней версии. Но попытка натравить lib-файлы из поставки на IDA успехов не принесли, поэтому я сделал это руками (да, не совсем по-феншую, как я хотел, но тоже ничего).


Исходники


Если кто не в теме, то нельзя просто взять, и переписать ассемблерный код в C-шный:




Куча нюансов: имена переменных, функций, полей структур. И чтобы это всё ещё было красиво. Поэтому именование всего этого занимает так же достаточно много времени.

Как вам такое?


#pragma pack(push, 1)
typedef struct huftable_s {
    uint32 l1; // +0
    uint16 l2; // +4
    uint32 l3; // +6
    uint16 bit_depth; // +A
} huftable_t;

#pragma pack(pop)

typedef struct vars_s {
    uint16 max_matches;
    uint16 enc_key;
    uint32 pack_block_size;
    uint16 dict_size;
    uint32 method;
    uint32 pus_mode;
    uint32 input_size;
    uint32 file_size;

    // inner
    uint32 bytes_left;
    uint32 packed_size;
    uint32 processed_size;
    uint32 v7;
    uint32 pack_block_pos;
    uint16 pack_token, bit_count, v11;
    uint16 last_min_offset;
    uint32 v17;
    uint32 pack_block_left_size;
    uint16 match_count;
    uint16 match_offset;
    uint32 v20, v21;
    uint32 bit_buffer;

    uint32 unpacked_size;
    uint32 rnc_data_size;
    uint16 unpacked_crc, unpacked_crc_real;
    uint16 packed_crc;
    uint32 leeway;
    uint32 chunks_count;

    uint8 *mem1;
    uint8 *pack_block_start;
    uint8 *pack_block_max;
    uint8 *pack_block_end;
    uint16 *mem2;
    uint16 *mem3;
    uint16 *mem4;
    uint16 *mem5;

    uint8 *decoded;
    uint8 *window;

    uint32 read_start_offset, write_start_offset;
    uint8 *input, *output, *temp;
    uint32 input_offset, output_offset, temp_offset;

    uint8 tmp_crc_data[2048];
    huftable_t raw_table[16];
    huftable_t pos_table[16];
    huftable_t len_table[16];
} vars_t;

Выглядит ужасно, как по мне. Но сейчас код работает… Может, в дальнейшем, я ещё что-то исправлю в этом коде.


Да, баги были после того, как окинул всё взглядом, сделал пару тестов. Где-то <, а должно быть <=, константы не так адаптированы и т.д. Но, сейчас вроде как всё исправил.


Конечно, я не стал переписывать упаковку MS-DOS приложений, AmigaOS приложений этим пакером (был такой функционал). Мне хотелось именно сам алгоритм.


Выводы


Самые банальные: если вы чего-то хотите добиться, пробуйте, не бойтесь. Делайте это шаг за шагом. И у вас всё получится.


Ссылка на IDB-файлик для AmigaOS бинаря:
https://mega.nz/#!3Z1TBA6J!ZOOfQf4Jmp_I704yYynzMVI_3To3etMjqRC2eF9b0Jg


RNC Propack Source: https://github.com/lab313ru/rnc_propack_source

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+28
Comments 1
Comments Comments 1

Articles