Пересборка 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  )
Если у вас остались какие-либо вопросы или есть какие-нибудь замечания, предложения — оставляйте свои комментарии — я их обязательно прочту!

Источник:

Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями. Для этого воспользуйтесь предлагаемыми ниже кнопками:


Блог: http://romanlovetext.blogspot.com/