четвер, 29 березня 2012 р.

Основы разбора и распаковки архивов неизвестных форматов

В данном посте я постараюсь показать основы анализа неизвестных архивов на примере разбора  ресурсов игры 7 Days (ссылку не даю чтобы не оказаться в пиратах :)) под Symbian. Сама потребность разбора возникла из-за отсутствия распаковщика архива данной игры. Распаковка же была нужна для перевода игры с китайской (распаковать я распаковал, а вот файлов содержащих символы так и не нашел :)).
 
Приступим. 

Сначала нам нужно распаковать установочный файл игры формата .sis. Это можно сделать программой Siscontents. Распаковав sis пакет, видим среди всех ресурсов игры только один файл res.spk размером 50 мб, который уже своим размером наводит на мысль о том, что это игровой архив, а так как этот архив (как потом выяснилось) без сжатия и неизвестного формата назовем его - псевдоархив (так обычно их называют). Открываем наш псевдоархив в hex редакторе (я выбрал HxD), и сразу в начале файла замечаем текстовые строки - это Fat таблица (таблица размещения файлов в архиве), которая подтверждает наши догадки о том, что это псевдоархив.














В Hex редакторе один символ (в колонке справа) соответствует одному байту бинарных данных (колонка по центру). Обратим внимание на одинаковый размер повторяющихся участков текста  - а именно 68 байт. Взглянув на hex значение первого байта перед повторяющимся участком (т.е. перед символом \), видим что он везде одинаков и имеет значение - 2E - это ни что иное как байт идентификатор начала одной записи Fat таблицы. Первые 2 байта файла пока трогать не будем. Чтобы они не мешали скопируем Fat таблицу без первых двух байт в новый документ. Таблица должна заканчиваться последним 68-м байтом последней записи Fat таблицы. Найдем последнюю запись, за которой начинаются данные (перейдите на offset 00225824). Далее копируем эту таблицу в новый документ.












На панели инструментов задаем количество символов в строке - 68, чтобы одна Fat запись умещалась в одну строку.












Теперь стало видно, что это пути к файлам содержащиеся в псевдо архиве. В каждой строке последние четыре байта всегда разные, начало всех строк одинаково - байт идентификатор, путь к файлу, точки до последних четырех байт. Количество точек различна. Hex значение точки - 00. Так как байт 00 не содержит информации и количество этих байт в строке разная, то не трудно догадаться что это трэш - заполнитель пустоты для одинакового размера записи.
В большинстве случаев Fat таблица начинается числом которое содержит количество записей таблицы. Проверим так ли это. Для этого нужно подсчитать количество записей Fat таблицы. Берем offset последнего байта последней записи Fat (смещение последнего байта от начала, которое фактически и будет количеством байт) и делим на размер одной записи. 

offset            record size              record count225896     /          68          =             3322          –   записи.

Для анализа байт нам понадобится стандартный калькулятор. Возвращаемся к предыдущему документу и копируем hex значение наших первых 2 байт (FA 0C). Переводы калькулятор в hex режим и копируем значение этих байт (FA 0C) в поле калькулятора. Жмем dec режим калькулятора и получаем десятичное значение этих двух байт - 64012. Хм что-то не совпадает с количеством записей которую мы определили делением офсета на размер записи.
Хотя! Попробуем скопировать эти два байта наоборот сначала второй (0C), а тогда первый (FA) и перевести в десятичное число. И что же мы получили? Именно так это количество полученных нами записей - 3322! Так что мы не ошиблись первые 2 байта это количество записей Fat таблицы, но они записаны наоборот, это означает что файл записан в кодировке little-endian а не big-endian, и все последующие значения нужно читать так же. Но у вас может возникнут вопросы почему мы брали два байта с начала таблицы. Все очень просто двумя байтами кодируется значение типа short (C++), а определить так ли это можно методом проб и догадок ).

Теперь проанализируем последние четыре байта записи. Для этого используя наш метод проб и догадок ) проверим  не число типа int (4 байта в C++) ли это. Копируем все четыре байта поочередно наоборот в калькулятор (потому что файл в кодировке little-endian), переводим в десятичное число и получаем - 225 898. Пока нам это число ни о чем не говорит. Попробуем перевести последние четыре байта следующей записи, получаем - 1168187. И теперь третьей записи - 1498793. Разница между этими числами невелика (если перевести в килобайты) примерно 300 кб, что вполне может быть размером файла. Но в записи присутствует не размер файла а число. Обычно в Fat таблицах указывается offset до начала данных самого файла в архиве. Проверим. Перейдем на offset указанный в первой записи. Точно, это первый байт за последней записью Fat таблицы. Значит последние четыре байта записи это смещение от начала архива до начала файла в архиве. 







Теперь можно составить таблицу структуры псевдо архива.
 
Первые 2 байта (short) файла это количество записей FAT таблицы и соответственно количество файлов в архиве; Далее идет FAT таблица в которой все записи по 68 байт. Структура записи: Байт идентификатор начала записи - (2E), путь и имя файла, треш, последние 4 байта - смещение от начала архива в начале файла. После FAT таблицы сразу начинаются файлы. 



На основе этих данных можно написать распаковщики.
Количество файлов - первые два байта. Смещение к началу файла - последние четыре байта записи. Имя файла содержит также папки в которых он находится в архиве. Размер определяется вычитанием двух соседних смещений. Архив заканчивается последним файлом, далее никаких данных нет.
Алгоритм:

  1. считать количество записей n
  2. цикл повторить n раз   
                -  если считан байт идентификатор
                           -  считать имя файла
                           -  считать смещение к файлу 

     3.  цикл повторить n раз  
 
                - отнять соседние два смещения получив размер первого файла
                - начиная с байта номер которого равен смещение к началу текущего файла считать  
                   количество байт равных разнице смещений этого и следующего файла
                - записать эти байты в файл с названием считанной в имени файла записи с 

                  соблюдением папок


Вот ссылка на исходники моего распаковщика написанного на Qt. 

Буду рад комментариям ).
 


3 коментарі:

  1. как распаковать архивы игр движка Chrome Engine имеющие расширение *rpack ?

    ВідповістиВидалити
  2. По этой игре и распаковщику есть ряд вопросов, пожалуйста ответьте. Ваша почта указанная в программе не работает.

    ВідповістиВидалити
  3. 01 49 83 11 00 01 9B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 44 FA 00 00 44 FA 04 19 44 7A 00 00 44 7A 08 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3E 10 13 A9 44 FA 00 00 42 9D E9 BA 45 7A 00 00 43 1D C1 76 3F 26

    ВідповістиВидалити