Всем привет,
Демо-сцена существует очень давно. Зачастую в процессе разработки очередной крутой демки приходится изобретать крутые алгоритмы: как для красивых анимаций и трекерной музыки, так и для кода. Иногда код получается большого объёма, поэтому его требуется сжать.
Понятно, что можно взять любой доступный алгоритм сжатия и использовать его у себя, но не существовало бы сейчас такого огромного количества различных упаковщиков, если бы всем хватало одного единственного алгоритма. Кому-то не нравится скорость работы, кому-то — качество сжатия, вот и изобретаются всё новые и новые алгоритмы. Одним из них и стал PowerPacker, исходные коды которого хотели получить многие, но удалось только мне.
Немного о PowerPacker
Кранчер (упаковщик) PowerPacker использовался во множестве старых игр (для AmigaOS в частности). Видимо, на то время он обладал очень хорошим сжатием и временем работы, по сравнению с другими кранчерами. К тому же он позволяет шифровать сжимаемые данные, давая возможность защитить ресурсы игры или программы (да, можно упаковывать и исполняемые файлы).
Сначала PowerPacker распространялся в виде самостоятельных исполняемых файлов: упаковщика и распаковщика. Затем, похоже, спрос на данный алгоритм сжатия вырос, и автор (Nico Francois) решил сделать своё творение платным, при этом перейдя на распространение уже в виде библиотеки powerpacker.library
.
Получение исходников
Для получения исходников, как и в случае с RNC ProPack, пришлось написать множество вспомогательного инструментария:
- Плагин-отладчик для IDA Pro (не работает, забросил)
- Загрузчик Amiga Hunk для Ghidra (помог)
- Загрузчик для library-файлов для Ghidra (очень помог)
- 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\<USER>\.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) и положить туда. Запускаю.
После выполнения данной команды произойдёт запуск winuae-gdb.exe
, открытие порта 2345
для работы с gdb
, и остановка на точке входа запускаемой программы. Остаётся только подключиться с помощью IDA и её Remote GDB debugger
к сессии WinUAE.
Меняем порт на 2345
, жмём Debugger
->Attach to process...
, затем выбираем процесс с id = 0
.
После этого у нас появляется окно отладки:
Как видим, адрес на котором мы стоим, отличается от адреса, на котором создавалась idb — 0x10000
, поэтому останавливаем отладку и делаем Rebase на 0x27D30
. Это поможет в дальнейшей отладке не терять изменений, сделанных в базе.
С этого момента можно спокойно заниматься пошаговой отладкой… до тех пор, пока вы не превысите количество брейкпоинтов равное 20. Сначала я не догадывался, в чём причина, но мои брейкпоинты вдруг становились неактивными, невалидными. Лишь посмотрев в исходник WinUAE (который, к тому же, собрать совершенно не просто), я нашёл ограничение в 20
брейкопоинтов. Собрав новую сборку с количеством, равным 999
, мне удалось наконец-то безболезненно заниматься самим процессом отладки.
Библиотека powerpacker.library
Тут пришлось изощряться, попутно найдя изящное решение, которое может помочь и вам при отладке загружаемых библиотек. Дело в том, что загруженные в память библиотеки (как и другие появляющиеся только во время отладки регионы памяти), можно сохранять прямо в idb
, и работать с ними, при желании, в статике. При этом, при перезапуске процесса отладки, вы не потеряете свои наработки по переименованию переменных, меток, и т.п. Для проворачивания этой хитрости необходимо на необходимом сегменте, после загрузки нужной библиотеки, зайти в его свойства (выбрав Edit segment...
):
Вы увидите, что там присутствует галка Debugger segment
, при снятии которой и нажатии OK
, данный сегмент будет сохранён в базу. Единственный момент: стоит следить за размером сегмента, иначе сохранение его в базу может растянуться, или вообще не закончиться.
Теперь можно входить в вызовы экспортируемых функций, и, при одном и том же адресе загрузки библиотеки, вы будете попадать в свой, уже проанализированный, код. Удобно.
В случае AmigaOS вызовы экспортируемых функций выглядят как вызовы по отрицательным смещениям относительно базы, по которой загружена библиотека:
Далее выяснилось, что у библиотеки имеется множество различных версий, которые, как оказалось, отличаются силой сжатия. Изначально, кое-как разреверсив одну версию библиотеки, я столкнулся с тем, что на выходе получались отличные от оригиналов файлы. Пришлось реверсить вторую библиотеку, более новую. Различие оказалось всего лишь в размере окна.
Результаты работы
Спустя три недели каждодневного реверс-инжиниринга по вечерам (а по выходным так и целые сутки) мне удалось всё таки получилось алгоритм, используемый в оригинальной библиотеке. К тому же я добавил флаг, позволяющий сжимать старым алгоритмом, где использовался меньший размер окна.
Протестировав всё на 210
файлах, найдя и исправив другие вылезающие баги (такие как выход за границы массива в оригинальном алгоритме), я готов опубликовать результаты своей работы:
vesper-bot
Интересно, насколько алгоритм отличается от скажем ACE или bz2, особенно по уровню сжатия.
(Когда-то давно я думал, что сжатие выкидывает из бинарного потока нули, оставляя только единицы. Сейчас как вспомнил, стыдно стало)
DrMefistO Автор
Надо будет сравнить, да выложить в данной статье результаты.
xMetalliCx
о каком-то крутом сжатии речи тут не идёт, PP предоставлял не самый плохой уровень сжатия при относительно небольшом времени распаковки (на типичных амижных процессорах типа 68000 7Мгц или 68020 14Мгц). плюс значительная часть амижного ПО умела прозрачно работать с файлами упакованными PowerPacker, так что немало людей использовало его для большинства файлов, как нечто типа штатного сжатия виндовой файловой системы.
если же требовалось лучшее сжатие — использовали архиваторы, стандартом де-факто был LZX, алгоритм из которого (до сих пор?) используют майкрософты в своих инсталяторах.