Приветствую, друзья!
В данном материале я расскажу Вам, как на протяжении нескольких лет занимался реверсом 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