Flash полезное / Упакованный SWF. Как распаковать?

Упакованный SWF. Как распаковать?:
Привет, друзья!

В этом посте я хотел бы рассказать о том, что из себя представляет упакованный SWF и как его можно распаковать динамически и статически.
Итак, что же такое упакованный SWF?
Если Вы никогда ранее его не видели, не поленитесь скачать простой классический пример упакованного SWF:

http://codestage.ru/files/flood/security/packed.swf

Запустив SWF, вы увидите немного текста в левом верхнем углу. Однако, если вы попробуете декомпилировать этот файл, то вы не увидите кода, который этот текст показывает.

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

Обычно все, что вы можете получить из таких SWF с помощью декомпиляторов — это код распаковщика\расшифратора и, возможно, сам зашифрованный SWF в виде набора байт (зависит от того, умеет ли используемый вами декомпилятор отображать тэг DefineBinaryData).

Такая «упаковка» на самом деле достигается с помощью простого Flex тэга [Embed].


Динамическая распаковка (dumping)

Динамическая — значит для распаковки потребуется исполнение SWF во Flash Player’е (далее — FP).

Т.к. FP не умеет проигрывать зашифрованную неведомо как SWF, то логично что перед проигрыванием, SWF должна расшифровываться и загружаться уже в чистом виде (например, с помощью Loader.loadBytes()).

Что более важно, распакованный и расшифрованный SWF будет в памяти все время, пока он исполняется FP’ром.
И это наш шанс достать его!

Существует два основных способа сделать это.
1) Динамическая распаковка с помощью утилит

Используя сторонние или даже самописные утилиты для поиска SWF в памяти процесса FP. Это самый быстрый, самый простой и обычно самый эффективный способ. Но он не вызывает ощущения что вы — тру кряке.

Существуют различные платные и бесплатные утилиты для этих целей.

Одна из наиболее продвинутых — это SWF Revealer, бесплатная утилита для владельцев лицензий на ASV. В некоторых случаях, она может обходить проверки на домен (которые не дают распаковаться и расшифроваться SWFке) и заставлять SWF запускаться в вашей среде.

Также достаточно легко гуглятся разные бесплатные дамперы.
2) Ручная динамическая распаковка

Поиск SWFки вручную, с помощью нахождения CWS (сжатый SWF) или FWS (несжатый SWF) заголовков, которые являются началом любого SWF. Если вы никогда не пробовали такой способ, я очень рекомендую попробовать! Он не только позволит немного подтянуть ваши скиллы в hex, но и подарит вам ощущение, что вы тру-крякер!)

Для поиска заголовков можно использовать любой HEX редактор, который умеет читать память процессов и имеет функцию поиска.

Если вы хотите искать упакованный SWF в памяти FP в котором проигрывается файл-пример по ссылке выше, то стоит начать с поиска FWS подписи (несжатый SWF) — просто ищите строку ‘FWS’, т.к. перед исполнением FP разжимает SWF, если он был сжат.

Если вы будете запускать и искать SWF в браузере, то закройте все лишние вкладки, чтобы снизить количество лишних SWF в памяти.
Обычно при таком поиске вы найдете несколько заголовков в памяти, т.к. сам FP держит там разные служебные SWF, например ту, что показывается после входа в полноэкранный режим.

Так что если вы сомневаетесь, то лучше проверить все найденные заголовки.

Итак, что же делать с найденным заголовками, спросите вы? Как их проверить, как узнать, где заканчивается SWF?

Пожалуйста, взгляните на этот скриншот:



Это заголовок одной из SWF в памяти FP при проигрывании файла-примера, найденный поиском по строке ‘FWS’ (совершенно случайно это оказался заголовок искомого упакованного файла, который мы и хотим найти ;) )

Что же дальше? А дальше необходимо посмотреть какой длины получается найденный SWF. Длина расположена в 4 байтах начиная с 4го:



Как я узнал? Я просто прочитал спецификацию формата SWF: «SWF File Format Specification» http://www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf_file_format_spec_v10.pdf (раздел «The SWF header»)

Т.к. это шестнадцатеричное число, записанное в память, вам стоит знать, что порядок записи его байт — справа налево. Поэтому в результате число такое:

00 00 04 DB в hex и 1243 десятичном представлении.

Теперь отмеряем эти 1243 байт начиная с FWS подписи.

Т.к. подпись начинается на 053DD020, окончание SWF файла должно находиться по адресу 053DD4FB (053DD020 + 4DB):



Пожалуйста, имейте ввиду, что адрес расположения SWF в памяти будет отличаться на разных ОС и на разном железе.
Итак, мы видим, что найденный SWF действительно заканчивается на 053DD4FB, так что мы можем смело выделить все байты начиная с 053DD020, заканчивая 053DD4FB и скопировать их в новый SWF файл.

После проделывания этой операции со всеми вхождениями FWS, которые вам покажутся подходящими, среди сохраненных SWF файлов будет один искомый, распакованный SWF!

Теперь у вас не должно возникнуть проблем с его декомпиляцией.
Некоторые виды пакеров усложняют поиск искомого SWF с помощью размещения множества фальшивых FWS заголовков в памяти. Так что вам следует более тщательно подбирать FWS — проверять его длину, и то как он в целом выглядит. Опытные в реверсинге люди (вроде меня :p) могут на глаз отличить фальшивый заголовок от настоящего, глянув на сам заголовок и на несколько десятков байт после него.
Иногда, упакованный SWF может не распаковываться в память до проверки каких-нибудь условий. Например, загрузчик может проверять текущий домен или наличие какого-нибудь файла с лицензией. В таком случае вам придётся пропатчить эти проверки (например, с помощью дизассемблеров байткода, таких как Yogda или RABCDasm) или предоставить необходимые файлы (в которых может находиться ключ для расшифровки), чтобы заставить SWF запуститься и распаковаться.

Статическая распаковка

Статическая — значит без запуска SWF во FP.

В целом, к этому типу распаковки прибегают когда не вышло распаковать SWF динамически (кто его знает, почему у вас не получилось запустить SWF?)

Статическая распаковка может быть очень сложной задачей, т.к. есть не один способ её усложнить и сделать мучительно долгой.
Итак, с чего начать при статической распаковке? Для начала, вам следует получить доступ как минимум к двум вещам в SWF:

1 — DefineBinaryData тэг(и).

2 — Декомпилированный AS или abc байткод распаковщика\загрузчика.

Также, в некоторых случаях понадобится

3 — SymbolClass тэг

Для этого используйте доступные утилиты (ASV, Adobe SWF Investigator, SWiX, и т.д.).
Как найти тэг DefineBinaryData в SWF?

Сначала отмечу, что некоторые утилиты, например, ASV, могут вам явно указать на наличие этого тэга, сразу после открытия SWF. В них же можно этот тэг сохранить в виде двоичного файла.

Также можно найти его вручную, с помощью различных инспекторов тэгов, вроде упомянутого выше Adobe SWF Investigator.

Для получения содержимого тэга DefineBinaryData из файла-примера с помощью Adobe SWF Investigator, просто откройте файл, перейдите на вкладку Tag Viewer, выберите тэг DefineBinaryData и нажмите на кнопку Dump To File.


Иногда в этом списке тэгов может быть множество фальшивых, чтобы сбить с толку незадачливого крякера. Для поиска необходимого тэга придётся немного изучить код загрузчика\распаковщика и отследить там обращение к упакованным данным.

Обычно оно выглядит так:
var someVar:ByteArray = new SomeClass();
Где SomeClass имеет тип Class и наследуется от класса ByteArrayAsset.
Давайте посмотрим в код загрузчика и поищем что-то похожее.

Ага, вот и оно!
private var content:Class;
//...
var _local3:ByteArray = new this.content();
Теперь нам следует поискать класс с именем оканчивающимся на «_content» и наследующийся от ByteArrayAsset.

А вот и он:
public class MainTimeline_focus_loader_content extends ByteArrayAsset
Чтобы выяснить, какой тэг DefineBinaryData связан с этим классом, нам следует заглянуть в тэг SymbolClass и поискать там запись с названием найденного класса «MainTimeline_focus_loader_content».

В нашем случае эта запись выглядит так (в Adobe SWF Investigator):
<Symbol idref='1' className='MainTimeline_focus_loader_content' />
Запомните значение поля idref. Это id нужного тэга DefineBinaryData!

Теперь ищите тэг с таким id среди всех тэгов DefineBinaryData.

После того, как вы его найдёте, его можно сохранить в файл и продолжать распаковку.

Почему я попросил запомнить idref, а не название класса? Потому что в том случае, если AS распаковщика обфусцирован, работать с именами классов может быть очень затруднительно.
Теперь успех вашего мероприятия зависит от количества времени, которое вы готовы потратить, сложности упаковщика и удачи)

Если повезёт, то полученные из DefineBinaryData данные окажутся чистым SWF без какого-либо шифрования и распаковку можно считать оконченной.

Но чаще всего, на этом этапе все самое интересное только начинается и впереди реверсинг загрузчика — разбор алгоритмов шифрования и написания собственного дешифровщика.
В нашем случае, код загрузчика намеренно сделан максимально простым и не обфусцированным, так что на этот раз нам повезло — мы легко находим функцию расшифровки:
private function decryptFile(_arg1:ByteArray):void
{
    _arg1.position = 0;
    var _local2:int = -1;
    var _local3:uint = _arg1.length;
    var _local4:uint = uint("55");
    while (_local2++ < _local3)
    {
        _arg1[_local2] = (_arg1[_local2] ^ _local4);
    };
}
И узнаем, что для получения оригинального SWF достаточно каждый его байт поксорить на 55.

Теперь вы можете написать свой декриптор, или скрипт, который сделает все операции для расшифровки SWF. Вот и все, распаковка закончена. После расшифровки вы получите оригинальный SWF, код и ресурсы которого видны в декомпиляторе.

Остаётся надеяться, что это так и распакованный SWF не окажется точно так же расшифровщиком SWF, который вы только что ковыряли, хахаха!)

Иногда при упаковке используют принцип матрёшки — запихивают один расшифровщик в другой — и так десятки раз, да ещё и алгоритмы расшифровки везде разные.

В любом случае, динамическая распаковка все это обходит.

Запомните — в мире Flash, ничего, кроме названий, нельзя скрыть от глаз профессионала высокого класса с достаточным уровнем мотивации ;)
Есть вопросы, идеи, комментарии? Оставляйте всё в виде комментариев к посту, пишите!