Пересборка SWF с помощью библиотеки as3swf

Привет!
Давненько у нас не было хорошего разбойничьего налёта тематического контента — пора бы это исправить!)

Итак, как вы уже наверняка догадались из темы этого поста — речь пойдёт о применении библиотеки as3swf для редактирования SWF файла.

Для начала, расскажу немного о самой библиотеке.
Она предназначена для расковыривания SWF файлов в реальном времени — с помощью as3swf вы можете распарсить SWF файл из массива байт, пробежаться по всем его тэгам, выяснить все его характеристики (такие как частота кадров, версия и т.д.), а также, что весьма приятно, вы можете внести изменения в распарсенный SWF — удалить, добавить, заменить или изменить любой тэг, и после всех изменений сохранить SWF в массив байт. Можно даже создать SWF с нуля.
as3swf доступна для всевозможных действий на гитхабе: https://github.com/claus/as3swf

Библиотека поддерживается, автор (Claus Wahlers) доступен для общения, так что ваши Pull реквесты он заметит, если таковые будут 😉
К слову, не так давно (буквально вчера) автор принял мой Pull реквест на добавление поддержки LZMA сжатия (подробнее про LZMA — тут)
Правда попробовать работу с LZMA в деле можно будет лишь после релиза 11.4.

Единственное, чего мне не хватало в этой библиотеке для ряда задач, так это полноценной поддержки парсинга и изменения ABC байткода, который содержится в тэге DoABC.
Автор as3swf начинал когда-то реализовывать подобный функционал в качестве отдельной библиотеки, но затем прекратил её поддерживать, и в данный момент этот проект уже не актуален.

С другой стороны, мне вполне понятны причины его решения — ведь для этих же целей есть замечательная библиотека as3commons bytecode, которая хоть как-то, но развивается и поддерживается: http://as3-commons.googlecode.com/svn/trunk/as3-commons-bytecode/changelog.txt
Её-то я и использовал для своих целей, но сейчас речь не о том случае.

Я расскажу, зачем мне понадобилась такая библиотека, как as3swf — у меня была задача реализовать замену одного тега на полностью новый аналог, с другим содержимым, если говорить конкретнее — то заменялся тэг DefineBinaryData.

Итак, вот как выглядит код, распарсивающий SWF файл:

var swf:SWF = new SWF(swfByteArray);

Где swfByteArray — это массив байт с целевым SWF файлом.
Куда уж проще, да?

После распарсивания SWF, мне необходимо было заменить определенный контент, заэмбеденный во флэшку с помощью флексового тэга

[Embed(source="./data.dat", mimeType="application/octet-stream")]
private var EmbeddedDataClass:Class;

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

Во-первых, появляется тэг DefineBinaryData со своим ID символа.
Также в результате использования тэга Embed появляется as3 класс с названием = НазваниеКласса_НазваниеПеременной и расширяющий класс mx.core.ByteArrayAsset.
Если представить, что мы использовали тэг Embed в классе MySuperClass пакета ru.codestage, то полное название будет ru.codestage.MySuperClass_EmbeddedDataClass.
И наконец,, появляется или дополняется тэг SymbolClass, который связывает символы с as3 классами по их id. В этот же тэг добавляются записи, когда вы прописываете линкейдж у символов библиотеки во Flash Pro.

Зная все это, нам не составит труда заменить заэмбеденный файл и ничего не сломать.
Вот, как это можно сделать средствами as3swf:

var newSWFByteArray:ByteArray = new ByteArray();

// эта строка нам уже знакома
var swf:SWF = new SWF(swfByteArray);

var symbolClass:TagSymbolClass;
var defineBinaryData:TagDefineBinaryData;

// пробегаемся по всем тэгам SWFки ...
var leni:uint = swf.tags.length;
var i:uint;

outerLoop: for (i = 0; i < leni; i++ )
{
// ... и ищем тэг SymbolClass
if (swf.tags[i].type == TagSymbolClass.TYPE)
{
symbolClass = (swf.tags[i] as TagSymbolClass);

// после обнаружения тэга SymbolClass, начинаем бегать
// по списку символов в нём
var lenj:uint = symbolClass.symbols.length;
var j:uint;

for (j = 0; j < lenj; j++ )
{
// проверяем имя каждого символа, пока не найдём подходящий
var symbol:SWFSymbol = symbolClass.symbols[j];
if (symbol.name == "ru.codestage.MySuperClass_EmbeddedDataClass")
{
// как только встретился нужный нам символ, мы получаем его ID из поля symbol.tagId
// и тут же пробуем получить символ с таким id из SWF с помощью метода swf.getCharacter()
defineBinaryData = (swf.getCharacter(symbol.tagId) as TagDefineBinaryData);
break outerLoop;
}
}
}
}

// если swf.getCharacter() вернул нам что-то стоящее, то мы перезаписываем данные в нем
if (defineBinaryData)
{
defineBinaryData.binaryData.clear();
defineBinaryData.binaryData.writeUTFBytes(_dataString);

// и сохраняем все изменения в некий ByteArray
swf.publish(newSWFByteArray);
trace("Done!");
}
else
{
trace("[ERROR] No ru.codestage.MySuperClass_EmbeddedDataClass symbol found!");
}

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

Не так давно я с его помощью реализовал для себя систему автоматического сайт-лочинга примерно за один день (бОльшая часть которого ушла на GUI :D)

Если у вас остались какие-либо вопросы или есть какие-нибудь замечания, предложения — оставляйте свои комментарии — я их обязательно прочту!

Комментарии

Пересборка SWF с помощью библиотеки as3swf — 16 комментариев

  1. Было необходимо сделать генерацию swf с embed-тегами прямо в коде, а материала очень мало в сети. Спасибо большое за статью)

  2. Согласна с автором выше, материала очень мало, но документация adobe http://www.adobe.com/devnet/swf.html (спецификации байт кода) с успехом заменяют документацию по этим продуктам (т.к. названия все те же). Вопрос такой, as3-commons bytecode сайт почему-то отвалился, надолго ли это (swc вроде нашелся, но документации или исходников или примеров нет)? И насколько надежна as3abc в нынешнем виде? Нужно модифицировать именно as3 код. Спасибо

    • Вы желаете изменить код «на лету», или требуется статичное изменение? Во втором случае можно использовать дизассемблеры и другие инструменты.
      На счёт первого случая — я давно не слежу за ситуацией с соответствующими библиотеками и к сожалению не смогу ничего подсказать.

      • Мне надо динамически генерировать swf на основе введенных пользователем данных (пишу конструктор учебных тренажеров). Сейчас программа пишет код выходной программы и код исполняемого файла jsfl (который загружает картинки и запускает компиляцию во fla документе). Все нормально, но для этого нужно 1) air (настольное приложение, т.к. flash с файловой системой работать так близко отказывается); и 2) запускать компилятор через jsfl вручную. Если б сразу флешку генерировал, было бы просто супер.

        • Понятно, да, для таких целей вам нужно что-то типа as3commons для генерации тэга DoABC, который затем можно добавить во флэшку с помошью as3swf.

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

  3. а можно ли таким способом заменить заэмбеженый фонт? у меня не получилось, но может я как-то не так делаю.

  4. это я видел, но мне надо заменять шрифт в заэмбеженой флешке рантайм.

    • Не совсем понял задачу. Вам нужно во время выполнения флэшки подменить шрифт в другой, заэмбеженной флэшке? И ни в первую ни во вторую нельзя вмешиваться до запуска?

  5. Да. Вообще задача такая — грузить шрифт в рантайме.

    • Ну тогда надо подправить код, чтобы шрифт подгружался и удостовериться что текстовые поля смогут его отобразить. В FFDec можно эмбедить новые шрифты и менять наборы символов у текстовых полей.
      Наверное лучше переместиться в почту\скайп а то в коментах беседу трудно вести.