Получение исходного кода PowerPacker Cruncher от AmigaOS

0mjaxnw4wl5z-5piua2jip66lec.jpeg

Всем привет,

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

Понятно, что можно взять любой доступный алгоритм сжатия и использовать его у себя, но не существовало бы сейчас такого огромного количества различных упаковщиков, если бы всем хватало одного единственного алгоритма. Кому-то не нравится скорость работы, кому-то — качество сжатия, вот и изобретаются всё новые и новые алгоритмы. Одним из них и стал PowerPacker, исходные коды которого хотели получить многие, но удалось только мне.

Кранчер (упаковщик) PowerPacker использовался во множестве старых игр (для AmigaOS в частности). Видимо, на то время он обладал очень хорошим сжатием и временем работы, по сравнению с другими кранчерами. К тому же, он позволяет шифровать сжимаемые данные, давая возможность защитить ресурсы игры или программы (да, можно упаковывать и исполняемые файлы).

Сначала PowerPacker распространялся в виде самостоятельных исполняемых файлов: упаковщика и распаковщика. Затем, похоже, спрос на данный алгоритм сжатия вырос, и автор (Nico François) решил сделать своё творение платным, при этом перейдя на распространение уже в виде библиотеки powerpacker.library.

qv6apjwuln399pkyfnd-bnaixt4.png

Для получения исходников, как и в случае с RNC ProPack, пришлось написать множество вспомогательного инструментария:


  1. Плагин-отладчик для IDA Pro (не работает, забросил)
  2. Загрузчик Amiga Hunk для Ghidra (помог)
  3. Загрузчик для library-файлов для Ghidra (очень помог)
  4. gdb-сервер для AmigaOS, работающий на ней же (не работал на моих файлах)

Отдельным пунктом идёт покупка kickstart rom (это что-то типа биоса, нутрянки AmigaOS, без него работать ничего не будет).

Потом у IDA появилась возможность отлаживать через GDB в том числе и для m68k. Правда серверной части, которая могла бы при этом эмулировать и мои файлы, и AmigaOS, у меня не было. WinUAE не умеет в gdb до сих пор.

Затем, спустя несколько лет, появилось расширение для Visual Code: https://github.com/BartmanAbyss/vscode-amiga-debug, которое позволяет отлаживать исходные файлы на C, при помощи модифицированного WinUAE с добавленным в него gdb-сервером. Вот здесь я и осознал — шанс на декомпиляцию есть.

Этот процесс без собственно самого декомпилятора превращается в долгое и мучительное преобразование ассемблерных инструкций в сишный код. И, если с кодом, который генерировался C-компилятором, проблем обычно не возникает, то вот с вручную написанным ассемблерным кодом проблем достаточно. Вот самые основные из них:


  • циклы (бесконечные goto)
  • использование одного и того же регистра как для хранения 16-битных значений, так и для хранения 32-битных. А ещё они в какой-то момент становятся знаковыми, хотя до этого использовались как беззнаковые.


Отладочный стенд

Для начала пришлось понять, как именно работает указанное выше расширение. Устанавливается оно в следующий каталог:

C:\Users\\.vscode\extensions\bartmanabyss.amiga-debug-1.0.0

Создаём и компилируем тестовый пример (да, у расширения он имеется). В подпапке .\bin имеется следующий список файлов:


  • dh0\
  • dh0\runme.exe
  • dh0\s\
  • dh0\s\startup-sequence
  • opt\
  • default.uae
  • elf2hunk.c
  • elf2hunk.exe
  • gnumake.exe
  • winuae.ini
  • winuae-gdb.exe

Подкаталог .\dh0\s содержит файл startup-sequence, в котором указываются команды, запускаемые при старте операционной системы. У меня он выглядит вот так:

:runme.exe

Здесь можно добавить нужные аргументы или команды. Для моих целей необходимо заменить файл runme.exe на исполняемый файл от PowerPacker-а, который затем будет загружать ту самую powerpacker.library. А вот куда класть эту библиотеку я понял не сразу. Оказывается, нужно было создать в каталоге .\dh0\ подкаталог Libs (я подсмотрел эту структуру в уже запущенной AmigaOS) и положить туда. Запускаю.

7varn0evzqmvfsekmbltz_ojvui.png

После выполнения данной команды произойдёт запуск winuae-gdb.exe, открытие порта 2345 для работы с gdb, и остановка на точке входа запускаемой программы. Остаётся только подключиться с помощью IDA и её Remote GDB debugger к сессии WinUAE.

adzyabjj46iktmmrrwtptayfumo.png

Меняем порт на 2345, жмём DebuggerAttach to process..., затем выбираем процесс с id = 0.

bu5gq96hredfi2mn5ks8rskvyps.png

После этого у нас появляется окно отладки:

y0jlfmkpe2yopa19umf6gjxfiky.png

Как видим, адрес на котором мы стоим, отличается от адреса, на котором создавалась idb — 0x10000, поэтому останавливаем отладку и делаем Rebase на 0x27D30. Это поможет в дальнейшей отладке не терять изменений, сделанных в базе.

С этого момента можно спокойно заниматься пошаговой отладкой… до тех пор, пока вы не превысите количество брейкпоинтов равное 20. Сначала я не догадывался, в чём причина, но мои брейкопоинты вдруг становились неактивными, невалидными. Лишь посмотрев в исходник WinUAE (который, к тому же, собрать совершенно не просто), я нашёл ограничение в 20 брейкопоинтов. Собрав новую сборку с количеством, равным 999, мне удалось наконец-то безболезненно заниматься самим процессом отладки.


Библиотека powerpacker.library

Тут пришлось изощряться, попутно найдя изящное решение, которое может помочь и вам при отладке загружаемых библиотек. Дело в том, что загруженные в память библиотеки (как и другие появляющиеся только во время отладки регионы памяти), можно сохранять прямо в idb, и работать с ними, при желании, в статике. При этом, при перезапуске процесса отладки, вы не потеряете свои наработки по переименованию переменных, меток, и т.п. Для проворачивания этой хитрости необходимо на необходимом сегменте, после загрузки нужной библиотеки, зайти в его свойства (выбрав Edit segment...):

kk3h-odkzyltgblz6xqzf70c4-g.png

u3ikga9m9mqcc9yhfhuifgi9fna.png

Вы увидите, что там присутствует галка Debugger segment, при снятии которой и нажатии OK, данный сегмент будет сохранён в базу. Единственный момент: стоит следить за размером сегмента, иначе сохранение его в базу может растянуться, или вообще не закончиться.

Теперь можно входить в вызовы экспортируемых функций, и, при одном и том же адресе загрузки библиотеки, вы будете попадать в свой, уже проанализированный, код. Удобно.

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

bb7qd2b_z8rnyongdsres_gjsxc.pngucthjcgsn_w1x5_-t6_rye7cu8c.png

Далее выяснилось, что у библиотеки имеется множество различных версий, которые, как оказалось, отличаются силой сжатия. Изначально, кое-как разреверсив одну версию библиотеки, я столкнулся с тем, что на выходе получались отличные от оригиналов файлы. Пришлось реверсить вторую библиотеку, более новую. Различие оказалось всего лишь в размере окна.

Спустя три недели каждодневного реверс-инжиниринга по вечерам (а по выходным так и целые сутки) мне удалось всё таки получилось алгоритм, используемый в оригинальной библиотеке. К тому же, я добавил флаг, позволяющий сжимать старым алгоритмом, где использовался меньший размер окна.

Протестировав всё на 210 файлах, найдя и исправив другие вылезающие баги (такие как выход за границы массива в оригинальном алгоритме), я готов опубликовать результаты своей работы:

4p5z57jxwkbqjrczpqlj0nollly.png

Исходники: https://github.com/lab313ru/powerpacker_src
Релизы: https://github.com/lab313ru/powerpacker_src/releases

© Habrahabr.ru