Jump to content
Korean Random
Конь-Огонь

Извлечение(распаковка) архивов неизвестных форматов

Recommended Posts

Автор заметки: WarChief, копипаста с его блога.
 

В данном посте я постараюсь показать основы анализа неизвестных архивов на примере разбора  ресурсов игры 7 Days (ссылку не даю чтобы не оказаться в пиратах :)) под Symbian. Сама потребность разбора возникла из-за отсутствия распаковщика архива данной игры. Распаковка же была нужна для перевода игры с китайской (распаковать я распаковал, а вот файлов содержащих символы так и не нашел :)).
Приступим.
Сначала нам нужно распаковать установочный файл игры формата .sis. Это можно сделать программой Siscontents. Распаковав sis пакет, видим среди всех ресурсов игры только один файл res.spk размером 50 мб, который уже своим размером наводит на мысль о том, что это игровой архив, а так как этот архив (как потом выяснилось) без сжатия и неизвестного формата назовем его - псевдоархив (так обычно их называют). Открываем наш псевдоархив в hex редакторе (я выбрал HxD), и сразу в начале файла замечаем текстовые строки - это Fat таблица (таблица размещения файлов в архиве), которая подтверждает наши догадки о том, что это псевдоархив.
 
post-1923-0-61602700-1385857924_thumb.png
 
В Hex редакторе один символ (в колонке справа) соответствует одному байту бинарных данных (колонка по центру). Обратим внимание на одинаковый размер повторяющихся участков текста  - а именно 68 байт. Взглянув на hex значение первого байта перед повторяющимся участком (т.е. перед символом \), видим что он везде одинаков и имеет значение - 2E - это ни что иное как байт идентификатор начала одной записи Fat таблицы. Первые 2 байта файла пока трогать не будем. Чтобы они не мешали скопируем Fat таблицу без первых двух байт в новый документ. Таблица должна заканчиваться последним 68-м байтом последней записи Fat таблицы. Найдем последнюю запись, за которой начинаются данные (перейдите на offset 00225824). Далее копируем эту таблицу в новый документ.
 
post-1923-0-21113100-1385857945_thumb.png
 
На панели инструментов задаем количество символов в строке - 68, чтобы одна Fat запись умещалась в одну строку.
 
post-1923-0-75856300-1385857945_thumb.png
 
Теперь стало видно, что это пути к файлам содержащиеся в псевдо архиве. В каждой строке последние четыре байта всегда разные, начало всех строк одинаково - байт идентификатор, путь к файлу, точки до последних четырех байт. Количество точек различна. Hex значение точки - 00. Так как байт 00 не содержит информации и количество этих байт в строке разная, то не трудно догадаться что это трэш - заполнитель пустоты для одинакового размера записи.
В большинстве случаев Fat таблица начинается числом которое содержит количество записей таблицы. Проверим так ли это. Для этого нужно подсчитать количество записей Fat таблицы. Берем offset последнего байта последней записи Fat (смещение последнего байта от начала, которое фактически и будет количеством байт) и делим на размер одной записи.

offset            record size              record count 
225896     /      68              =        3322
Для анализа байт нам понадобится стандартный калькулятор. Возвращаемся к предыдущему документу и копируем hex значение наших первых 2 байт (FA 0C). Переводы калькулятор в hex режим и копируем значение этих байт (FA 0C) в поле калькулятора. Жмем dec режим калькулятора и получаем десятичное значение этих двух байт - 64012. Хм что-то не совпадает с количеством записей которую мы определили делением офсета на размер записи.
post-1923-0-61026800-1385857946_thumb.png
 
Хотя! Попробуем скопировать эти два байта наоборот сначала второй (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 таблицы сразу начинаются файлы.
post-1923-0-06738900-1385857947_thumb.png
 
На основе этих данных можно написать распаковщики.
Количество файлов - первые два байта. Смещение к началу файла - последние четыре байта записи. Имя файла содержит также папки в которых он находится в архиве. Размер определяется вычитанием двух соседних смещений. Архив заканчивается последним файлом, далее никаких данных нет.
Алгоритм:
      1. считать количество записей n
      2. цикл повторить n раз  
                -  если считан байт идентификатор
                           -  считать имя файла
                           -  считать смещение к файлу
     3.  цикл повторить n раз 
 
                - отнять соседние два смещения получив размер первого файла
                - начиная с байта номер которого равен смещение к началу текущего файла считать 
                   количество байт равных разнице смещений этого и следующего файла
                - записать эти байты в файл с названием считанной в имени файла записи с
                  соблюдением папок
Вот ссылка на исходники моего распаковщика написанного на Qt: https://sourceforge.net/projects/spkextractor/files/

 

Edited by Конь-Огонь
  • Upvote 2

Share this post


Link to post

Short link
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...