CODE STAGE http://blog.codestage.ru/ru DEVELOPMENT & RESEARCH Mon, 06 May 2013 14:53:09 +0000 ru-RU hourly 1 Продвинутое шифрование в DoSWF? Вызов принят! http://blog.codestage.ru/ru/2013/05/06/decrypting-doswf/ http://blog.codestage.ru/ru/2013/05/06/decrypting-doswf/#comments Mon, 06 May 2013 14:53:09 +0000 focus http://blog.codestage.ru/ru/?p=718 Читать далее ]]> Привет!
Я ранее уже писал про распаковку флэшек, в большинстве случаев все сводится к дампу рабочей флэшки из памяти, или к статической распаковке после анализа алгоритма расшифровки в загрузчике. Но бывают и исключения. Об одном таком я и расскажу в этой статье.

Разработчик китайского протектора DoSWF применил несколько простых приемов, из-за которых процесс распаковки приятным образом усложняется) Думаю, многие горе-хакеры, умеющие дампить с помощью сторонних утилит, входят в ступор, не обнаружив ни одной рабочей флэшки на выходе.

Предлагаю ознакомиться со всеми нюансами на конкретном примере (в статье много картинок!)…
Для начала, возьмем какую-нибудь подопытную флэшку, мне под руку попалась демка частиц движка Stardust:
Stardust

Декомпилируется она без проблем:
Clean SWF

Теперь о самом протекторе. Я для своих экспериментов использовал последнюю триальную версию (на данный момент 5.2.9), обычно доступную онлайн на сайте разработчика: http://www.doswf.cn/swfencrypt/
Так как наш академический интерес всецело сосредоточен на функционале упаковки, то при использовании протектора необходимо включить опции в разделе Encrypt:
DoSWF Encrypt
Кстати, я не использовал обфускацию оригинальной флэшки, дабы пример получился нагляднее.

На выходе получаем SWF, при декомпиляции которого можно увидеть нечто подобное:
DoSWF Loader

Вполне такое стандартное положение дел для любой упакованной флэшки — видим немного классов, которые по сути представляют собой загрузчик, расшифровывающий и подготавливающий флэшку к запуску. Также неподалёку обычно валяется включенный в ролик набор байт (или наборы байт), в котором и находится искомая флэшка, или её части ;)
Стоит отметить, что далеко не каждый декомпилятор в состоянии справиться с обфускацией DoSWF — некоторые падают или виснут при попытке прожевать такую кашу из байткода. Я рекомендую использовать Action Script Viewer (ASV) или AS3 Sorcerer — эти инструменты скушают такой байткод даже не поперхнувшись — автор знает своё дело! Привет, Burak! xD

Защищённая SWF работает нормально, при запуске виден ватермарк, который добавляется при использовании триальной версии протектора:
DoSWF Watermark
А также, через некоторые промежутки времени, флэшка открывает родной сайт протектора.

Теперь приступим непосредственно к распаковке — ролик проиграть можем — препятствий для дампа быть не должно.
Вы можете использовать любые устраивающие вас инструменты или сделать все вручную, я для этих целей могу порекомендовать SWF Revealer Ultimate — прокачанный дампер/анпакер от автора ASV. Если ролик работает в standalone проигрывателе, то SWFRUL сумеет вытащить из такого SWF все необходимое.
Кстати, SWFRUL — это лимитированное издание оригинального SWF Revealer’а, идущего в комплекте с ASV (но работает он совсем иначе). Всего будет продано лишь 15 лицензий по всему миру, так что спешите, если вас заинтересовал этот инструмент.

SWFRUL находит два SWF файла, которые можно вытянуть из защищенной флэшки:
SWFRUL

Сохраним их и изучим повнимательнее.

При запуске этих флэшек мы не увидим рабочий оригинал. В одной — лишь белый фон, в другой — глючащий интерфейс искомой флэшки. Ну что-ж, давайте разберемся что к чему.
Открыв первую флэшку в декомпиляторе можно увидеть код, очень похожий на то, что мы видели в оригинальном SWF:
Dumped SWF with code
В глаза сразу же бросается странность — декомпилятор не нашел document класс. Более того, в библиотеке этой флэшки пусто! Выходит, флэшка эта — лишь часть того, что нам надо и на первый взгляд содержит только код…
При беглом осмотре в любом инспекторе SWF, например, в Adobe SWF Investigator, сразу становится понятно почему декомпилятор не нашел document класс — нет необходимого для этого тэга SymbolClass, а если точнее, то там вообще нет никаких тэгов кроме трёх — FileAttributes, DoABC, ShowFrame:
SWF with code tags

Выходит, что всё остальное — во второй сдампленной флэшке, ведь как раз в ней мы видели глючный интерфейс оригинального SWF. Это легко подтверждается после просмотра в инспекторе:
Investigating second SWF
Заодно убеждаемся, что тут есть тэг SymbolClass, где указан document класс (Symbol с нулевым id).

Похоже, нам следует попытаться объединить оба SWF файла. На мой взгляд, проще перенести тэг DoABC из первой флэшки во вторую, чем переносить все остальное из второй в первую. Сделать это можно разными способами, я для этого использовал старый добрый SWiX — удобный XML-based редактор SWF файлов. Достаточно открыть в нем обе флэшки, а потом скопипастить то что надо. Копируем тэг DoABC и сохраняем:
Editing SWF in SWiX

Пробуем запустить сохраненный ролик и видим следующее:
Corrupted SWF

С одной стороны, зрелище весьма неприятное — наблюдаются баги в интерфейсе, не работают частички, отладочный проигрыватель сыпет ошибками. С другой стороны — появился ватермарк протектора и работают Stats — значит код подцепился таки и теперь это не просто мёртвый интерфейс!

Теперь следует разобраться что же у нас сломалось и почему. Откроем SWF в декомпиляторе и посмотрим на код конструктора:
Decompiled corrupted SWF

Умный ASV заменил страшные обфусцированные имена на _SafeStr*. Так как флэшка не была обфусцирована, то в данном случае весьма просто отличить инородный код, который в неё добавил обфускатор, в реальной жизни все несколько сложнее, однако в нашем случае лишняя наглядность не помешает.

Итак, в конструкторе мы видим подозрительный вызов метода _SafeStr4(), а так же пустой статический метод _SafeStr3(), похожий на мусор.
Если просмотреть остальной код в главном классе, то будет заметно довольно много методов, добавленных протектором, но судя по всему, всё это отключается удалением вызова _SafeStr4() из конструктора.
Так что так и сделаем — почикаем все лишнее из конструктора с помощью какого-нибудь редактора байткода. Я обычно для таких целей использую RABCDAsm и какой-нибудь GUI для него, например WinRABCDAsm.

Итак, дизассемблируем флэшку и ищем байткод конструктора в файле главного класса (Waypoints.class.asasm). А вот и он:

     getlocal0
     pushscope

     getlocal0
     callpropvoid        QName(PackageNamespace(""), "@doswf__mnɿ"), 0

     findproperty        QName(PrivateNamespace(null, "Waypoints#0"), "matrix")
     findpropstrict      QName(PackageNamespace("flash.geom"), "Matrix")
     pushdouble          0.5
     pushbyte            0
     dup
     pushdouble          0.5
     constructprop       QName(PackageNamespace("flash.geom"), "Matrix"), 4
     initproperty        QName(PrivateNamespace(null, "Waypoints#0"), "matrix")

     getlocal0
     constructsuper      0

     findpropstrict      QName(PackageInternalNs(""), "__setProp_xml_btn_Scene1_XMLbtn_1")
     callpropvoid        QName(PackageInternalNs(""), "__setProp_xml_btn_Scene1_XMLbtn_1"), 0

     returnvoid

В самом начале листинга виден вызов того подозрительного метода: @doswf__mnɿ (ASV переименовал его в _SafeStr4).
Удаляем вызов (как правильно править байткод я рассказывать не буду, достаточно почитать спеку и все станет понятно), пересобираем флэшку, смотрим.
Почти ничего не изменилось — все те же баги и т.д., только вот ватермарк пропал ;)

Кроме глюков все еще остался неприятный эффект от триальной версии протектора — через разные промежутки времени открывается его страница.
Найти код, который это делает, можно с помощью поиска вызовов метода navigateToURL по всему исходнику. Совпадений нашлось немного, и просматривая окрестности вызовов, вскоре можно наткнуться на вызов инлайн метода, в котором и будет код, который периодически открывает главную страницу протектора. В моем случае он был добавлен в класс idv.cjcat.stardust.common.particles.Particle. В вашем случае это может быть любой другой класс, т.к. судя по всему, расположение этого кода выбирается случайно на этапе обработки SWF протектором.
Кстати там есть такая вот лепота:

var _local2 = "ht" + "tp:/" + "/ww" + "w.d" + "osw" + "f.c" + "om";

Низкая эффективность компилятора во всей красе.

Итак, надо бы посмотреть на дизасм этого класса. Стоит обратить внимание на статический конструктор — именно там и происходит вызов этого мерзкого инлайн-метода =)

    getlocal0
    pushscope

    findpropstrict      Multiname("Particle", [PackageNamespace("idv.cjcat.stardust.common.particles")])
    getlex              QName(PackageNamespace(""), "Object")
    pushscope

    getlex              QName(PackageNamespace(""), "Object")
    newclass            "idv.cjcat.stardust.common.particles:Particle"
    popscope
    initproperty        QName(PackageNamespace("idv.cjcat.stardust.common.particles"), "Particle")

    newfunction         "idv.cjcat.stardust.common.particles:Particle.sinit/inline_method#0"
    pushnull
    call                0
    pop
    returnvoid

Вот он и попался — вызов происходит прямо перед выходом из статического конструктора. Убедиться, что это именно тот самый инлайн, можно изучив его дизасм. Удаляем вызов, пересобираем — voilà! Флэшка больше не открывает сайт протектора.
Кстати, мы только что удалили ограничения триальной версии протектора ;) Правда в коде осталось много уже не используемых ошмётков, по которым можно понять, что накрыта флэшка была именно триальной версией.

Все это хорошо, но нас больше интересует восстановление полной работоспособности флэшки. Пришло время присмотреться к ошибкам, которыми сыпет отладочный проигрыватель. В моём случае окончание стэктрейса первой ошибки, которую показывает Flash Player, такое:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at fl.core::UIComponent/setSize()
at …

Итак, стоит проверить в декомпиляторе, что же может быть не так в fl.core::UIComponent/setSize().
код там такой:

_width = _arg1;
_height = _arg2;
invalidate(InvalidationType.SIZE);
dispatchEvent(new ComponentEvent(ComponentEvent.RESIZE, false));

Судя по ошибке, где-то у нас ссылка на null, значит первые две строки вне подозрения. Смотрим на 3тью — nullом там может быть константа SIZE.
Смотрим на класс InvalidationType:

    public class InvalidationType 
    {

        public static const ALL:String = "all";
        public static const SIZE:String = "size";
        public static const STYLES:String = "styles";
        public static const RENDERER_STYLES:String = "rendererStyles";
        public static const STATE:String = "state";
        public static const DATA:String = "data";
        public static const SCROLL:String = "scroll";
        public static const SELECTED:String = "selected";

        public function InvalidationType()
        {
            if (!ApplicationDomain.currentDomain.hasDefinition("Ȋ"))
            {
                return;
            };
            super();
        }

    }

    import flash.system.ApplicationDomain;

    if (!ApplicationDomain.currentDomain.hasDefinition("Ȋ"))
    {
        return;
    };

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

Стоит посмотреть на это чудо китайской мысли в дизассемблере:

    getlocal0
    pushscope

    findpropstrict      Multiname("InvalidationType", [PackageNamespace("fl.core")])
    getlex              QName(PackageNamespace("flash.system"), "ApplicationDomain")
    getproperty         QName(PackageNamespace(""), "currentDomain")
    pushstring          "Ȋ"
    callproplex         QName(PackageNamespace(""), "hasDefinition"), 1
    iftrue              L9

    returnvoid

L9:
    getlex              QName(PackageNamespace(""), "Object")
    pushscope

    getlex              QName(PackageNamespace(""), "Object")
    newclass            "fl.core:InvalidationType"
    popscope
    initproperty        QName(PackageNamespace("fl.core"), "InvalidationType")

    returnvoid

Прекрасно видно, что проверка находится в самом начале статического конструктора, не позволяя ему отработать при отрицательном результате.
Удаляем проверку, пересобираем — этой ошибки больше нет, а так же заметны некоторые изменения во флэшке (как минимум пропало полупрозрачное окошко):
Fixed error

Можно предположить, что таких проверок во флэшке много — вот почемы мы видим все эти ошибки и неработающий в итоге файл.
Так что теперь можно поискать обращения к ApplicationDomain, ну или создать фейковый класс, чтобы проверки его находили ;) Я сторонник чистоты в коде, так что предпочитаю первый вариант, который, кстати, очень легко автоматизировать.
Таким образом разбираемся со всеми ошибками и восстанавливаем работоспособность SWF файла. Других подлянок данная версия протектора не предполагает.

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

Резюмируя, можно сказать, что даже такие хитрости как разбиение SWF файла на части с последующей их загрузкой в один домен, внедрение дополнительных проверок в защищаемый SWF и прочие приемы, использованные в данном протекторе не способны надежно защитить SWF файл. Единственная необратимая защита в as3 — была, есть и будет обфускация имён — все остальное обратимо и лишь увеличит трудозатраты на взлом.
Если у вас есть что добавить или спросить — пишите! Любые комментарии, как всегда, приветствуются!

И да, чуть не забыл, с прошедшей Пасхой! ;)

]]>
http://blog.codestage.ru/ru/2013/05/06/decrypting-doswf/feed/ 0
Потоки в Unity3D — измеряем производительность http://blog.codestage.ru/ru/2013/03/27/unity3d-threads/ http://blog.codestage.ru/ru/2013/03/27/unity3d-threads/#comments Wed, 27 Mar 2013 00:42:50 +0000 focus http://blog.codestage.ru/ru/?p=697 Читать далее ]]> Привет!
Иногда меня спрашивают об отложенных действиях или о многопоточности в Unity3D и обычно я советую обратить внимание на класс Coroutine, благо он позволяет решать большинство повседневных задач, связанных с задержками и проч.
Однако, иногда при разработке действительно требуется настоящая многопоточность, при поиске пути, например.
Поэтому я решил сделать на скорую руку небольшой тест, в котором можно было бы пощупать многопоточность и сравнить производительность исполнения кода в одном (главном) потоке и в многопоточной реализации. Для начала я поискал какие-нибудь менеджеры потоков, чтобы не велосипедить и наткнулся на простецкий (но в моем случае, удобный) класс Loom от whydoidoit.

Далее я по-быстрому слепил приложение, скомпилировал его под разные платформы (в архиве в конце статьи есть бинарники для всех основных платформ и сорцы самого проекта) и посмотрел на результаты. . Некоторые мне показались интересными, поэтому я решим ими поделиться, может быть кому-то это будет полезно.

Вообще код, который пытался нагружать CPU прост как пробка — я просто пробегаюсь по приличному массиву экземпляров Vector3D (10 000 000 элементов под десктопами и 1 000 000 под мобилами):

private void Run()
{
	const float scale = 432.654f;

	for (int j = 0; j < arrayLength; j++)
	{
		Vector3 v = vertices[j];

		v = Vector3.Lerp(v * scale / 123f * v.magnitude, v / scale * 0.0123f * v.magnitude, v.magnitude);

		vertices[j] = v;
	}
}

Как видите, «начинка» цикла по сути ничего конкретного не делает — просто случайный набор всяких медленных вещей)
Так же я добавил на сцену модельку муравья (привет создателям примеров для Away3D! :) ), которая крутится каждый Update(), дабы можно было сразу увидеть — когда приложение подвисает, «задумываясь», а когда — нет.

JTMLjQ0

Как я ранее сказал, я провел некоторые тесты на подручном оборудовании, проверял скорость работы как в синхронном режиме (выполнение в главном потоке), так, и в асинхронном (массив бьется на столько равных частей, сколько ядер доступно приложению и каждая часть обрабатывается в отдельном потоке), и получил я такие результаты (S — синхр, A — асинхр):

PC (Intel Core i7 2600K 4.6Ghz, 4-core + Hyper Threading)

Standalone PC build (Fastest quality, 1024×768 windowed)
S: 920.8 мс
A: 198.2 мс (8 потоков, x4.64)

WebPlayer build (Chrome 25.0.1364.172, Unity WebPlayer Release Channel Plugin v. 4.1.1f)
S: 938.1 мс
A: 200.4 мс (8 потоков, x4.68)

PC (Intel Core i7 Q740 1.73Ghz, 4-core + Hyper Threading)

Standalone PC build (Fastest quality, 1024×768 windowed)
S: 1732.1 мс
A: 584.4 мс (8 потоков, x2.96)

WebPlayer build (Chrome 25.0.1364.172, Unity WebPlayer Release Channel Plugin v. 4.1.1f)
S: 1780.0 мс
A: 555.3 мс (8 потоков, x3.21)

PC (Intel Atom N455 1.66GHz 1-Core + Hyper Threading)

Standalone PC build (Fastest quality, 1024×600 windowed)
S: 7835.3 мс
A: 6533.9 мс (2 потока, x1.20)

WebPlayer build (Chrome 25.0.1364.172, Unity WebPlayer Release Channel Plugin v. 4.1.1f)
S: 7701.6 мс
A: 6778.3 мс (2 потока, x1.14)

Mobile

Galaxy Tab 10.1 (1.4GHz OC 2-core CPU, Tegra 2, Android 4.0.4, GT-P7510)
S: 1479.0 мс
A: 831.1 мс (2 потока, x1.78)

iPad 1 (1Ghz 1-core CPU)
S: 5364.0 мс
A: 6865.7 мс (1 поток, x0.78)

Не дурно, да? На платформах с двух- и более ядерными CPU прирост производительности может достигать хороших показателей (до x4.68 по сравнению с исполнением в главном потоке в нашем случае!), в целом, на всех устройствах кроме первого iPad производительность «расчетов» так или иначе возросла при использовании многопоточности.
Во всех случаях тест асинхронного выполнения кода не вызывал зависания приложения или другого дискомфорта, за исключением теста на Intel Atom, где просел FPS. Ему простительно, он старый и одноядерный :)

Обратите внимание на разницу в производительности двух разных поколений Intel Core i7 — оба имеют 4 ядра с HT на борту, однако более свежее поколение демонстрирует гораздо бОльшую эффективность применения многопоточности, что конечно хорошо.
Также весьма интересные получились результаты тестов на камне Intel Atom — у него всего 1 ядро, но зато есть HT, которая и позволила несколько улучшить производительность кода в многопоточном исполнении, мелочь, а приятно!)
Кстати, следуем заметить, что хоть и «проседает» FPS при тестировании этого Атома, приложение все ещё остается отзывчивым.

Результаты тестов на мобильных платформах тоже интересные.
Galaxy Tab 10.1 показал себя с лучшей стороны, показав почти двукратный прирост скорости выполнения асинхронного теста по сравнению с синхронным, при этом уверенно обогнав первый iPad по скорости выполнения обоих тестов, хотя, думаю тут помог разгон до 1.4 ГГц =P
Первый iPad имеет одноядерный CPU, поэтому дополнительные потоки он не сумеет обработать на HW уровне, вот почему выполнение нашего кода в отдельном потоке тут медленней. Ведь, по сути мы заставляем его работать над расчетами и обработкой сцены одновременно, переключаясь то в главный поток, то обратно в наш дочерний. Если бы на сцене было что-то посущественней, то думаю FPS бы просел. С другой стороны, асинхронное выполнение кода позволяет избежать зависания приложения, иногда это даже более важная задача, чем сама скорость расчетов (и вот тут кстати обычно справляются Coroutine’ы).

Кстати, приятно, что потоки работают на мобильных платформах — они могут быть отличным подспорьем Coroutin’ам!

А теперь посмотрим на сравнение общего времени выполнения всех проведенных тестов:

chart1
Общая разница не очень большая… Но некоторый прирост в производительности все же есть + мы получаем работу нашего кода без зависания приложения, не так уж плохо!

Теперь оставим в сравнении только железо с двух- и более ядерными CPU:
chart2
Ого! А вот теперь разница хорошо заметна. Так что можно смело использовать потоки на математически сложных или очень объемных алгоритмах — вы не только получите плавную работу приложения во время просчётов, но и неслабый прирост производительности на многоядерных устройствах!

Можно встроить простую проверку в код — если

SystemInfo.processorCount

будет возвращать 2 или более — можно смело переключаться на многопоточное исполнение сложного алгоритма.

Некоторые из вас наверняка заметили, что я не проводил тестов на добре, которое можно получить с помощью Flash Exporterа. Причина проста — потоки не поддерживаются (пока?) экспортером. Я полагаю, что задача слишком не тривиальна — ведь настоящие потоки во флэше возможны лишь при использовании Workers, а их концепция слишком далека от таковой в .NET, в общем, сделать это очень трудно, если вообще возможно.

Не забудьте, что дополнительные потоки в вашем мобильном приложении могут быстро убить батарею на устройствах с многоядерными процессорами — будьте осторожны!
И, как верно заметил DbIMok в комментариях, при работе с потоками — помните, настоящие потоки не будут работать с объектами Unity-движка (кроме некоторых исключений, например Debug.Log будет работать и из отдельных потоков), в то время как coroutine’ы будут, т.к. работают по принципу «green threads» — исполняют код в течение нескольких «кадров», но в основном потоке.

Думаю, пока это все, вы можете скачать архив со скомпилированными примерами и исходниками проекта тут (~27МБ).
Это стандартный LZMA2 архив, сжатый в 7-zip. Используйте 7-zip 9.x или аналог для распаковки.

PS: И да, я буду рад услышать от вас любые идеи или замечания по данной теме, а так же было бы интересно получить результаты тестов на других устройствах и железе!

]]>
http://blog.codestage.ru/ru/2013/03/27/unity3d-threads/feed/ 2
AIR vs. Unity3D. Кто быстрее? (Обновление 1) http://blog.codestage.ru/ru/2013/03/20/air-vs-unity3d/ http://blog.codestage.ru/ru/2013/03/20/air-vs-unity3d/#comments Wed, 20 Mar 2013 19:16:11 +0000 focus http://blog.codestage.ru/ru/?p=620 Читать далее ]]> Обновление 1: вылил немного сорцов (см. в конце статьи).

Привет!
Да, давно не писал, блаблабла.. К делу! =)

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

В последнее время я все чаще замечаю как выходцы из мира Flash-разработки начинают интересоваться Unity3D, да и сам я в последнее время работаю с Unity всё больше и всё плотнее.
И как по мне — так это очень здорово — расширять свой кругозор, изучать новые языки (надеюсь, большинство пришедших из Flash-разработки делают правильный выбор в пользу C#), вливаться в новое комьюнити, решать новые задачи и т.д.!
Однако, многие нынешние флэшеры всё ещё сомневаются — стоит ли пробовать Unity3D, тратить время на изучение технологии… Зачастую, эти сомнения основаны на неясности относительно разницы в производительности приложений на Unity3D по сравнению с AIR, вот о ней я и расскажу в этой статье, чтобы помочь им определиться.

Примеры в статье я буду компилировать с помощью AIR 3.6 и последней Unity3D 4.1 с максимально приближённым функционалом и внешним видом.
Я сравню их производительность на относительно медленном GalaxyTab 10.1, и некоторые тесты проведу в десктопном режиме.

Интро

Итак, начнем с пустых проектов.
В Юнити проекте для измерения FPS будем использовать самописный fps-ометр, который рисует количество FPS на GUIText.
В AIR проекте будем использовать разные fpsометры — в основном будем брать статсы, встроенные в используемые фреймворки для максимальной совместимости.
Собираем релизные версии apk (во FlashDevelop’е собираем captive-runtime вариант).
Выливаем обе apk на девайс, смотрим:

FPS: в обеих стабильные 60 (разрешение у Galaxy Tab 10.1 — 1280×800).
RAM: померить полный объем потребляемой памяти приложением на AIR с помощью стандартных API насколько я помню не выйдет (поправьте, если я не прав), в Unity3D есть возможность обращаться к встроенному профайлеру для этих целей. Для AIR можно использовать какой-нибудь внешний профайлер, но т.к. они будут разные с разным оверхедом, то нам это не подходит — нас интересуют наиболее близкие условия окружения.
Объем файлов:
AIR — 9223 кб
Unity3D — 7406 кб
Если Stripping Level в Unity3D поднять до Strip Bytecode, получим вообще 6946 кб.

Скорость кода

Теперь добавим текстовое поле в AIR приложение и OnGUI текстовое поле в Unity3D — для вывода времени выполнения ряда следующих тестов.
Начнем с цикличного создания и заполнения массивов случайными числами.
В случае AIR это будет фиксированный вектор заданной длины, т.к. это самый быстрый полноценный массив в as3.
Будем сравнивать его со стандартным массивом в C#, также наиболее быстрым.

Код теста на as3 такой:

private const VECTOR_LENGTH:int = 100000;
//...
private function callFewTests():void
{
	var timeBegin:int = getTimer();
	for (var i:int = 0; i < 100; i++)
	{
		makeACake();
	}
	outputTextField.text = String(getTimer() - timeBegin) + " ms";
}

private function makeACake():void
{
	var v:Vector.<int> = new Vector.<int>(VECTOR_LENGTH, true);

	for (var i:int = 0; i < VECTOR_LENGTH; i++)
	{
		v[i] = int(Math.random());
	} 
}

Код на C# для Unity3D:

 
private const int ARRAY_LENGTH = 100000;
private float totalTime = 0;

void Start ()
{
 	CallFewTests();
}

void OnGUI()
{
 	if (totalTime > 0)
	{
		GUILayout.Label(totalTime.ToString("0."));
	}
}

private void CallFewTests()
{
	float timeBegin = Time.realtimeSinceStartup;

	for (int i = 0; i < 100; i++)
	{
		MakeACake();
	}

	totalTime = (Time.realtimeSinceStartup - timeBegin)*1000;
}

private void MakeACake()
{
	int[] a = new int[ARRAY_LENGTH];

	for (int i = 0; i < ARRAY_LENGTH; i++)
	{
		a[i] = (int)Random.value;
	}
}

Как можно заметить из кода, мы 100 раз создаем целочисленный массив из 100000 элементов и заполняем его в цикле случайными значениями, используя для этого встроенные средства для получения случайных чисел.

После установки APK, приложения запускались несколько раз, чтобы исключить возможные тормоза первого запуска.
Средние результаты:

AIR — 1965 мс
Unity3D — 1600 мс

Результат оказался практически одинаковым, Unity3D выполняет задачу немного быстрее.
Попробуем заменить получение случайного числа на вызов метода, который возвращает сумму пары чисел, дабы исключить неизвестную нам разницу в производительности рандомов:

private int GetTotalGeeksCount()
{
	int a = 23;
	int b = 41;
	return a + b;
}

Я удостоверился (с помощью декомпиляторов), что этот метод честно компилируется без оптимизаций на обоих испытуемых, вылил на Galaxy. Вот результаты:

AIR — 837 мс
Unity3D — 224 мс

То есть на довольно простых операциях, таких как сложение целых чисел, создание и заполнение одномерных массивов, вызов методов, выполнение цикла for и т.п. — разница в производительности более чем в 3 раза!

Попробуйте сравнить сами, может вы получите другие результаты (буду рад об этом узнать в комментариях) — см. файлы
Random*, Clean*

Окей, а что если попробовать выгрузить из Unity3D флэшку с этими вычислениями и сравнить скорость на десктопе?) Давайте посмотрим: увеличиваем количество итераций с 100000 до 1000000, компилируем, выгружаем из Unity3D под FP 11.4, берём swf из папки bin нашего мобильного FlashDevelop проекта, и запускаем их в последней (на данный момент — 11.6) релизной версии Projector проигрывателя.

AIR — 573 мс
Unity3D — 2580 мс (!)

Ещё в процессе обнаружился баг в SWF, выгруженном из Unity — после такой строки myFloat.ToString(«0″); код просто переставал работать. Забавно, что экспортер в состоянии компилировать в swf более-менее сложные проекты, а такое простое выражение вызывает проблемы.

Видим, что по производительности выгруженный из Unity3D swf все-таки значительно хуже, чем созданный в привычных условиях.

Для порядка, надо бы сравнить производительность выгруженного проекта из Unity3D под десктопный WebPlayer с производительностью нашего swf в Projector проигрывателе.

AIR — 573 мс (измеряли ранее)
Unity3D — 102 мс

Видим превосходство выгруженного из Unity3D в более чем 5 раз. Неплохо!
Попробуем выгрузить из Unity3D десктопные (exe) варианты:

x86 — 103 мс
x64 — 124 мс

Ситуация почти не поменялась. Интересно, что x64 вариант работает немного медленней.

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

AIR — 4181 мс (http://pastebin.com/yNr3aVyc)
Unity3D — 281 мс (http://pastebin.com/btz405XS)

Я попробовал собрать AIR проект с помощью ASC2 в надежде увидеть значительные изменения, но в итоге время сократилось лишь до 3991 мс.

Резюмируя, можно заметить, что производительность as3 кода всё ещё остается ахиллесовой пятой AIR, как и много лет назад. Надеюсь, в будущем ситуация изменится к лучшему.
Unity3D Calculations*, Simple Code*

2D

Ок, перейдём к рендеру, начнем с 2D. Делаем простой тест — создаем и крутим вокруг своей оси 3000 простых квадов небольших размеров (дабы не иметь большой overdraw).
Для AIR используем Starling (знаю, что есть и более производительные фреймворки, однако этот является наиболее известным и поддерживается Adobe, то есть это выбор большинства).
Также используем 16bit, родной старлинговский FPSометр.

В Unity3D Камера ортогональная, Solid Color. Шейдер — простой самописный (цвет без альфы).
Use 32-bit Display Buffer и Use 24-bit Depth Buffer отключены.

eAhNVDy

Компилируем, выгружаем на девайс.. Результат, FPS:
AIR — 32
Unity3D — 20

Весьма интересные результаты, но вполне обоснованные. В Юнити все вычисления производятся исходя из того, что объекты могут быть 3D и находятся в 3D пространстве.
В старлинге же применены различные оптимизации благодаря изначально двухмерной природе фреймворка. Кстати, в данный момент идет работа над 2D режимом в Unity ;)
Quads*

Теперь натянем текстуру на плоскости, уменьшим количество объектов до 1000 и добавим к вращению движение.
Текстурку (и сопутствующий код загрузки ресурсов для AIR теста) я взял из Starling Benchmark’а Валеры Бохана (привет, Валера! =) ).
Сжатие в Unity3D было выставлено в RGBA DXT5, благо Гэлекси на тегре и текстура с прозрачностью.

cA6t0eU

Компилируем, выливаем. Результат, FPS:
AIR — 43
Unity3D — 47

Ну, тут результат несколько неоднозначный. Думаю, Юнити удалось тут немного обогнать AIR только за счет DXT5 сжатия, которое оптимально для устройств на Tegra, но не универсально. При использовании RGBA32 (единственный формат среди оставшихся, при котором визуально текстура не отличается от таковой в AIR) FPS в тесте Unity3D проседает до 30. С другой стороны, у меня нет под рукой других Андроид девайсов не на Tegra, так что я не могу проверить производительность других форматов сжатия в новых условиях. В любом случае, можно загрузить в Google Play билды под разные устройства — с разным сжатием текстур например, тем самым позволяя пользователям работать с наиболее подходящими.
Texture*

Выходит, по совокупности тестов и нюансов, производительность 2D на GPU в AIR выше, чем в Unity3D. Полагаю, основная причина такого различия описана мной выше — 2D фреймворк, выбранный в качестве представителя лагеря AIR хорошо оптимизирован под двухмерное применение, в то время как Unity3D манипулирует объектами и производит расчёты в 3D пространстве. Возможно, 2D режим в будущем позволит ей тягаться с AIR 2D фреймворками на равных.
Кстати, на этом примере хорошо заметна мощь Stage3D — благодаря низкоуровневости, создатели фреймворков могут производить различные оптимизации под конкретные задачи, очень гибкий подход.

3D

Думаю, достаточно 2D, перейдём к 3D!
Для начала — стандартный тест с кучей кубиков без освещения. Добавляем 1000 кубиков так, чтобы они не сильно перекрывали экран, дабы не упереться в fillRate, используем самый простой шейдер.
Для AIR будем использовать Away3D (4.1.0 Alpha), по тем же причинам, что и Starling.

fskxVfR

Компилируем, выливаем. Результат, FPS:
AIR — 10
Unity3D — 49

Безоговорочная победа Unity3D. И нет, AIR билд не отладочный — релизный. Вот на всякий случай исходный код основного класса AIR проекта: http://pastebin.com/iNzPZTnM
Simple Cubes*

Теперь немного усложним задачу. Загрузим более сложную модельку, размножим в количестве 10 штук, добавим 1 направленный источник света без теней и поменяем шейдер. В Unity будем использовать обычный Specular шейдер без оптимизаций. Сцена получилась из 173840 треугольников.

gzCy14h

Компилируем, выливаем. Результат, FPS:
AIR — 27
Unity3D — 51

Вполне ожидаемые результаты.
Lighting*

PS

В заключение хочу сказать, что высокая производительность Unity3D совместно с интегрированной и очень гибкой IDE делают её хорошим кандидатом на роль заменителя AIR.
Так что, если вы пожелаете попробовать что-то новое для создания кроссплатформенных игр, я бы не задумываясь посоветовал познакомиться с Unity3D.
Кстати, при создании тестовых проектов я использовал не все возможности по оптимизации, но это и к лучшему — задача была сравнить производительность платформ при использовании с настройками по умолчанию и некоторыми общими оптимизациями.
Ну и я долгое время не касался библиотеки Away3D, так что мог забыть что-то важное. Если заметите — дайте знать, исправлюсь!)

Буду рад услышать ваши мысли на эту тему!
На а если вы уже проводили подобные сравнения и получили иные результаты — пишите, буду рад ознакомиться!

Архив с примерами приложений:
AIRvsUnity3D.7z (38 МБ)
Сжато в LZMA2 (используйте 7-zip 9 или аналог)

Обновление 1:
Архив с Unity3D исходниками:
UnityPerformanceTest.7z (750 КБ, LZMA2)
Это Unity3D 4.1 проект. Там есть по одному тесту (в отдельных сценах) из каждой части статьи.


Все приведенные в архиве примеры тестировались исключительно под 1280 на 800 (landscape), на Tegra 2 устройстве (Samsung Galaxy Tab 10.1) под управлением Android OS 4.0.4. Корректная работа в других условиях не гарантируется.

]]>
http://blog.codestage.ru/ru/2013/03/20/air-vs-unity3d/feed/ 5
Пересборка SWF с помощью библиотеки as3swf http://blog.codestage.ru/ru/2012/07/12/as3swf-code-library/ http://blog.codestage.ru/ru/2012/07/12/as3swf-code-library/#comments Thu, 12 Jul 2012 01:48:28 +0000 focus http://blog.codestage.ru/ru/?p=527 Читать далее ]]> Привет!
Давненько у нас не было хорошего разбойничьего налёта тематического контента — пора бы это исправить!)

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

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

]]>
http://blog.codestage.ru/ru/2012/07/12/as3swf-code-library/feed/ 2
Flash GAMM Moscow 2012 http://blog.codestage.ru/ru/2012/06/24/flash-gamm-moscow/ http://blog.codestage.ru/ru/2012/06/24/flash-gamm-moscow/#comments Sun, 24 Jun 2012 15:08:37 +0000 focus http://blog.codestage.ru/ru/?p=519 Читать далее ]]> Всем привет!
Расскажу немного о поездке на московский Flash GAMM и выложу свою презентацию вместе с сорцами (кому нужна только презентация — мотайте в конец поста).

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

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

Если кто не знает, блиц-доклад — это такой микро доклад, который длится в среднем около 5 минут. В связи с этим возникла проблема — как всё то, что я хотел рассказывать целый час, уместить в 5 минут?
Что уж там говорить, 90% материала из черновика пришлось выкинуть, а остальные 10% переработать, оставив лишь всё самое основное и сократив список инструментов до 3: Flash Console, TheMiner, XFLTool. Причем даже после такой «чистки» при скоростном прочтении с листочка уходило всё равно около 6 минут. Но дальше резать уже было некуда, так что решил действовать на блице по обстоятельствам)

В результате, чистовик презентации был отправлен и билеты в оба конца (двухместное купе, т.к. ехал с супругой) куплены и получены.

Сама поездка на поезде прошла отлично — было очень уютно вдвоём в купе, не говоря о том, что соседним вагоном оказался вагон-ресторан, в посещении которого мы не смогли себе отказать).
По прибытии заселились в отель и стали ждать начала события.

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

Первый доклад, который мы посетили — «Век ускорения Away3D», Rob Bateman:

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

Затем послушали Enrique Duvos, он рассказывал про разработку игр и про Flash платформу в целом, огласил основные из ранее анонсированных изменений в свежих и грядущих версиях Flash Player:

А после этого мы застали Евгения Затепякина и Rob’а, о котором я писал выше, за некой беседой.
Евгений Затепякин — разработчик известной библиотеки для добавленной реальности — in2ar, а также множества других полезных библиотек и алгоритмов.
Rob Bateman — один из основателей Away3D — он коммитил в ядро на пару c Александром Задорожным!

Мне было пора бежать на секцию блиц докладов со своим скромным вкладом в общее дело, так что договорились пересечься с ребятами на обеде.

На блицы пришло не так уж мало народу, что меня несколько удивило, учитывая что параллельно проходили весьма занятные доклады известных личностей из известных фирм — AlternativaPlatform, Nevosoft и др.
Первым довелось выступать мне, по этому после краткого экскурса в слайдопереключатель (который в моём случае оказался бесполезен), меня пригласили к микрофону и начались те самые несколько минут, ради которых изначально и была затеяна вся эта поездка) Незаметно для меня, но заметно для остальных, 5 минут плавно растянулись до семи с половиной, однако я успел рассказать и показать всё, что планировал, хоть и весьма сжато:

Надеюсь, что у меня ещё будет возможность продемонстрировать весь потенциал тех инструментов, о которых я говорил и рассказать о некоторых других на полноценном докладе)

После блиц докладов начался двухчасовой перерыв на обед, во время которого мы вновь встретились с Робом и Евгением, познакомились и немного пообщались. Я впервые вживую попытался поговорить на английском, вроде бы ребята даже понимали о чём я (надеюсь, это было не совсем ужасно! :) ).
С Евгением я периодически переписывался ещё до поездки, а вот с Робом общался совсем впервые.

Я, Евгений и Роб

Почти всю оставшуюся половину мероприятия мы провели в зале «Вечерний Космос», т.к. там шли доклады на английском и Робу было проще засыпать под разговоры на родном языке =) Он прилетел в город весьма рано, сильно устал и успешно задремал на одном из докладов.

Последний час флэшгамма мы провели на докладах Евгения и Валерия Бохана, оба доклада шли по пол часа и были интересными — Женя показывал демки с добавленной реальностью и свою свежую библиотеку Stage3D фильтров (GPUImage):

А Валера рассказал про Starling и показал статистику, собранную его мобильным Starling бенчмарком:

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

Было очень интересно и приятно пообщаться со всеми с кем я виделся на конференции и после неё. Рад был наконец-то посетить и выступить на этом мероприятии.
Спасибо организаторам и участникам — было здорово.

Отдельное большое спасибо Евгению и Робу за отлично проведённое время и интересное, живое общение.
Ну и конечно же, спасибо моей супруге Танюше за поддержку и фоторепортирование =)

Выкладываю слайды (требуется Flash Player 11 и выше; все ссылки там кликабельные, переключаются слайды стрелками курсора) и сорцы презентационного движка, в котором можно подглядеть пример использования Genome2D и Nape, как и обещал.

И немного фоток с конфы и прогулке по Москве.

PS: все доклады записывались на видео и не так давно их выложили таки на youtube, так что если вы пропустили интересные вам доклады или совсем не присутствовали, то рекомендую ознакомиться.

Успехов!

]]>
http://blog.codestage.ru/ru/2012/06/24/flash-gamm-moscow/feed/ 2
Собираюсь на FlashGAMM Moscow 2012 http://blog.codestage.ru/ru/2012/05/15/going-to-flashgamm/ http://blog.codestage.ru/ru/2012/05/15/going-to-flashgamm/#comments Mon, 14 May 2012 21:23:47 +0000 focus http://blog.codestage.ru/ru/?p=460 Читать далее ]]>

Привет, друзья!
Я готовлюсь к короткой поездке в Москву, на конференцию FlashGAMM, где я выступлю со своим небольшим блиц-докладом.
Я расскажу о нескольких продвинутых инструментах для отладки и оптимизации.
Специально для своей презентации я написал пару простых эффектов с использованием Nape и Genome2D, делать эти эффекты было куда веселее, чем готовить слайды)
Секция блиц докладов начнётся в 12:00 по Московскому времени в зале «Сатурн».

Если вы будуте в Москве с 16 по 19 мая и желаете встретиться со мной — пишите!

В ближайшие несколько недель я выложу свою презентацию, а заодно с ней и отчёт о поездке на конференцию.
Так что у вас будет возможность поиграться с эффектами, которые я написал для презентации и посомтреть на их исходный код!

Пока!

]]>
http://blog.codestage.ru/ru/2012/05/15/going-to-flashgamm/feed/ 2
Flash Player: планы на будущее — много замечательного! http://blog.codestage.ru/ru/2012/03/28/flash-player-roadmap-updated/ http://blog.codestage.ru/ru/2012/03/28/flash-player-roadmap-updated/#comments Wed, 28 Mar 2012 10:57:10 +0000 focus http://blog.codestage.ru/ru/?p=455 Читать далее ]]> Снова привет!
Сегодня вместе с анонсом premium функционала и релизом Flash Player 11.2 / AIR 11.2, Adobe обновили свои планы на будущее относительно Flash и показали массу отличного функционала который появится в этом году!

Взгляните на переведённый быстрый копипаст из их поста:

Flash Player «Cyril»
После релиза Flash Player 11.2, во второй половине 2012го, Adobe планирует выпустить релиз Flash Player’а под кодовым названием «Cyril».

Некоторые из запланированных нововведений:

  • Поддержка клавиатурного ввода в полноэкранном режиме
  • Улучшенная поддержка аудио для работы с аудио с меньшими задержками
  • Возможность прогрессивной поточной передачи текстур для Stage 3D контента
  • Поддержка LZMA сжатия для ByteArray
  • События для кадров
  • Поддержка сжатия BitmapData в JPEG и PNG форматы
  • Поддержка требований к «песочнице» для Mac OS X App Store приложений

Здорово, не так ли?

Flash Player «Dolores»
Кроме того, вслед за «Cyril», Adobe планирует релиз ещё одной версии Flash Player под кодовым названием «Dolores».

Некоторые из запланированных нововведений:

  • ActionScript workers (позволяет исполнять ActionScript в нескольких параллельных потоках — многопоточность проще говоря)
  • Поддержка продвинутого профайлинга (Telemetry)
  • Расширение поддержки видео карт для Stage3D (начиная с 2005/2006)
  • Увеличение производительности ActionScript при компиляции под Apple iOS
  • API для получения индекса производительности — для получения сведений о производительности текущей среды
  • Событие мыши Release outside
  • ActionScript 3 API для прямого доступа к быстрым опкодам работающим с памятью (те, что используются в Alchemy) прямо из as3 кода! (премиум при использовании вместе со Stage3D)

Ещё больше отличного функционала!
В этом году Flash в ударе (снова)!

Ссылка на полную версию планов Adobe: http://www.adobe.com/devnet/flashplatform/whitepapers/roadmap.html

]]>
http://blog.codestage.ru/ru/2012/03/28/flash-player-roadmap-updated/feed/ 9
Быстрое сравнение трех 2D библиотек работающих на Stage3D *Обновление 1* http://blog.codestage.ru/ru/2012/03/26/comparison-stage3d-2d-frameworks/ http://blog.codestage.ru/ru/2012/03/26/comparison-stage3d-2d-frameworks/#comments Mon, 26 Mar 2012 08:15:40 +0000 focus http://blog.codestage.ru/ru/?p=448 Читать далее ]]> Привет, друзья!

Я бы хотел показать вам результаты беглого сравнения трех наиболее популярных библиотек для 2D рендера, работающих на Stage3D:
Starling, ND2D, Genome2D.

Я скачал последние доступные билды и исходники этих библиотек и написал простое AIR приложение, в котором отображается некоторое количество изображений. Все они вращаются, двигаются, скейлятся и меняют альфу каждый кадр
Изображения размещаются по всей видимой области в несколько итераций (каждый кадр добавляется заданное количество изображений, пока их общее количество не достигнет заданного предела).
Для всех изображений используется одна и та же текстура — квадрат 25 на 25 с небольшим количеством альфы.
Я прогнал это приложение на своем стационарном PC и на двух мобильных устройствах — iPad1 с iOS 5.0.1 и GalaxyTab 10.1 с Android 3.1.

Итак, вот что я получил:

Starling изGitHub’а Commit:9aa93a8195c47252253fd4d2b671115e19d16b6f, 24.03.2012:

iPad 1:
1350 изображений на 30 FPS
RAM: 6.4 МБ

GalaxyTab 10.1 (OC 1.4ГГц):
1350 изображений на 30 FPS
RAM: 6.2 МБ

PC (i7 4.8 ГГц, Radeon 6790):
10000 изображений на 30 FPS
RAM: 19.7 МБ
RAM на 1000 изображений: 9.1 МБ

Starling — отличный опенсорсный фреймворк. Он имитирует классический DisplayList, имеет большое сообщество (во многом благодаря Adobe) и очень подробную API документацию. По этому изучать и вносить изменения в эту библиотеку достаточно просто.

ND2D из GitHub’а Commit:453531f7aee46b00a112c6f0d5d8b46d62075287, 05.03.2012:

Эта библиотека позволяет отрисовывать большое количество спрайтов в одинаковой текстурой двумя разными «способами» — добавляя спрайты в Sprite2DCloud или в Sprite2DBatch. Я протестировал оба.

Sprite2DCloud:

iPad 1:
~1150 изображений на 30 FPS
RAM: 6.4 МБ

GalaxyTab 10.1 (OC 1.4ГГц):
~1150 изображений на 30 FPS
RAM: 6.2 МБ

PC (i7 4.8 ГГц, Radeon 6790):
16000 изображений на 30 FPS с падением до 16 FPS во время добавления изображений (добавлялось по 100 штук в кадр), этого падения не должно быть заметно если добавлять все спрайты сразу.
RAM: 37 МБ
RAM на 1000 изображений: 9.5 МБ

Sprite2DBatch:

iPad 1:
~1150 изображений на 30 FPS
RAM: 6.6 МБ

GalaxyTab 10.1 (OC 1.4ГГц):
~1150 изображений на 30 FPS
RAM: 6.1 МБ

PC (i7 4.8 ГГц, Radeon 6790):
10800 изображений на 30 FPS
RAM: 23.2 МБ
RAM на 1000 изображений: 9.1 МБ

ND2D это еще один прекрасный опенсорсный фреймворк для отрисовки 2D с использованием Stage3D. У него также имеется большое сообщество (хоть и меньше, чем у Starling), однако API документация страдает — она заполнена очень поверхностно (однако, зачастую и этого достаточно) и при этом фреймворк сравним по производительности со Starling.
Данную библиотеку также просто использовать, т.к. своей архитектурой визуальных объектов она тоже напоминает классический DisplayList, который мы привыкли видеть во Flash, к тому же для библиотеки доступны примеры использования.

Genome2D Nightly 0.9.0.1093:

Эта библиотека отчетливо выделяется. Достаточно посмотреть на результаты бенчмарков!

iPad 1:
1900 изображений на 30 FPS
RAM: 6.5 МБ

GalaxyTab 10.1 (OC 1.4ГГц):
3800 изображений на 30 FPS
RAM: 10 МБ

PC (i7 4.8 ГГц, Radeon 6790):
~40000 изображений на 30 FPS
RAM: 68.7 МБ
RAM на 1000 изображений: 9.1 МБ

Это просто хардкорный фреймворк! Библиотека работает с потрясающей скоростью, однако для нее нет вообще никакой API документации, у нее скромное сообщество и нет актуальных примеров для последних Nightly билдов (на данный момент последний nightly билд ушел далеко вперед по сравнению с последним релизным).
Новичку использование данного вреймворка может показаться чересчур сложным.

Итак, что у нас есть для создания 2D приложений на Stage3D:
Starling — самый приятный. Однако, в некоторых тестах проигрывает ND2D в производительности.
ND2D — компромиссный вариант — в некоторых случаях он быстрее Starling’а, но отсутствие (на данный момент) адекватной API документации немного портит впечатление (so far).
Genome2D — в некоторых случаях (как в моем, например) может показывать небывалую производительность и оставлять конкурентов далеко позади, однако использовать его я бы порекомендовал лишь хардкорным разработчикам с навыками «ниндзжениринга») Я надеюсь, этот фреймворк станет дружелюбнее к разработчику в будущих релизах.

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

Так же, хотелось бы отметить, что iPad1 и разогнанный до 1.4 ГГц GalaxyTab 10.1 во многих случаях держались наравне. Я подозреваю, что дело в разрешении и каким-то образом компенсирующих друг друга других HW и SW отличиях.
Так что если вы сейчас задумались о портировании своей (не требовательной к ресурсам) игры с комфортом и без лишних сложностей, то я бы смело рекомендовал попробовать Starling. Если же вам требуется максимум производительности (и приключений) — посмотрите в сторону Genome2D. А если что-то между — то я бы посоветовал ознакомиться с ND2D.

В качестве бонуса привожу ссылку на архив со всеми бенчмарками для Android (и AIR 3.2 p6 Runtimes для Android):
http://codestage.ru/files/flood/android/benchmark.zip

Если вы считаете, что я где-то ошибся в тестах — пишите — я буду рад исправить свою ошибку!

*Обновление 1*

Недавно я обновил расширение для Starling — starling-imagebatch, автором которого является Philippe, чтобы оно заработало с последней версией Starling’а. Заодно проверил его в деле.

Вот результаты:

iPad 1:
2000 изображений на 30 FPS
RAM: 6.4 МБ

GalaxyTab 10.1 (OC 1.4ГГц):
2000 изображений на 30 FPS
RAM: 5.1 МБ

PC (i7 4.8 ГГц, Radeon 6790):
>16000 изображений на ровных 60 FPS (больше изображений добавить не получилось из-за ограничения размеров буфера)
RAM: 14.9 МБ
RAM на 1000 изображений: 8.4 МБ

Баг с распуханием памяти)

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

Вот первый кусок кода:

var k:Number = (premultipliedAlpha ? item.alpha : 1) / 255;

А вот второй:

var k:Number = premultipliedAlpha ? item.alpha : 1;
k /= 255;

По сути никакой разницы, верно?
Только вот при очень многократном выполнении первого куска происходит распухание памяти со скоростью где-то 5 мб/сек и примерно какждые 3-5 секунды оно чистится — получается такая гармошка на графике потребления памяти)
А если первый кусок заменить на второй — всё работает как часы без каких-либо проблем с памятью.

]]>
http://blog.codestage.ru/ru/2012/03/26/comparison-stage3d-2d-frameworks/feed/ 8
TheMiner и FlashDevelop http://blog.codestage.ru/ru/2012/03/22/theminer-and-flash-develop/ http://blog.codestage.ru/ru/2012/03/22/theminer-and-flash-develop/#comments Thu, 22 Mar 2012 10:53:01 +0000 focus http://blog.codestage.ru/ru/?p=433 Читать далее ]]> В этом руководстве речь пойдёт о TheMiner‘е (отличный Flash профайлер) и о FlashDevelop‘е (лучшая и бесплатная среда разработки для Action Script / haXe), соответственно подразумевается, что вы с ними знакомы и желаете использовать их совместно. Если не знакомы — рекомендую ознакомиться.

Всего есть два пути использования TheMiner во FlashDevelop:

- Используя TheMiner SWC, как любую другую внешнюю библиотеку.
- Добавляя TheMiner SWF в список профайлеров FlashDevelop.

Почему существует два пути? Потому что у каждого есть сови преимущества и недостатки, и, в зависимости от обстановки, тот или иной путь может оказаться наиболее подходящим.
Итак, давайте попробуем оба пути шаг-за-шагом. Пожалуйста запустите FlashDevelop и откройте \ создайте в нём as3 проект чтобы продолжить.

TheMiner в качестве внешней SWC

1. Для начала, купите TheMiner или скачайте бесплатную версию для некоммерческого использования.

2. Распакуйте скачанный архив, зайдите в папку «SWCs» и выберите подходящую версию (в зависимости от требуемого языка), например TheMiner_en.swc

Если вы знакомы с использованием SWC во FlashDevelop, смело пропускайте следующие 2 пункта руководства и продолжайте читать с 5го пункта.

3. Скопируйте выбранную SWC в папку «lib» вашего проекта (или в любое другое удобное для вас место, где вам привычнее хранить внешние библиотеки).

4. Добавьте скопированную SWC в библиотеку проекта кликнув на SWC правой кнопкой и используя пункт контекстного меню «Add To Library«.

5. Чтобы запустить TheMiner в своём проекте, следует добавить его в DisplayList. Выберите подходящее место в проекте для добавления (например, там где вы настраиваете stage) и напишите там пару строк кода.

// первая строка - прописываем TheMiner в импортах класса:
import com.sociodox.theminer.TheMiner;

//вторая строка - добавляем TheMiner в DisplayList с помощью добавления на stage
//если вы используете версию TheMiner 1.3.10 и ранее, то не забудьте
//передать параметр "true" в конструктор
stage.addChild(new TheMiner());

Вот пример пустого проекта с подключенным TheMiner:

package
{
	import com.sociodox.theminer.TheMiner;
	import flash.display.Sprite;
	import flash.events.Event;

	/**
	 * ...
	 * @author focus
	 */
	public class Main extends Sprite
	{

		public function Main():void
		{
			if (stage) init();
			else this.addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private function init(e:Event = null):void
		{
			this.removeEventListener(Event.ADDED_TO_STAGE, init);
			//если вы используете версию TheMiner 1.3.10 и ранее, то не забудьте
			//передать параметр "true" в конструктор
			this.addChild(new TheMiner());
		}
	}
}

Это всё, что требуется для подключения TheMiner SWC! Как вы видите, подключать TheMiner данным способом весьма просто.
Скомпилируйте проект, запустите его в Debug версии Flash Player (по умолчанию во FlashDevelop) и вы увидите TheMiner в действии!

Итак, в каких случаях следует выбирать способ подключения TheMiner в качестве SWC?
- если мы хотим иметь возможность запускать TheMiner везде, где можно запустить нашу SWF в Debug версии Flash Player или AIR, включая мобильные устройства
- если мы хотим иметь дополнительный контроль того, когда именно показывать TheMiner
- если мы хотим иметь возможность взаимодействовать c TheMiner прямо из кода, для более гибкого и эффективного профайлинга

TheMiner как один из профайлеров FlashDevelop

1. Для начала, купите TheMiner или скачайте бесплатную версию для некоммерческого использования.

2. Распакуйте скачанный архив, зайдите в папку «SWFs» и выберите подходящую версию (в зависимости от требуемого языка), например TheMiner_en.swf

3. Поместите этот SWF в удобное место на вашем PC и запомните путь до него. Пусть для примера путь будет таким: «D:\Programming\Flash\Tools\TheMiner.swf».

4. Откройте меню «Tools->Program Settings» во FlashDevelop.

5. Перейдите в настройки плагина AS3Context и найдите там строку Custom Profilers.

6. Нажмите кнопку «…» в правой части найденной строки и вставьте на новую строку путь до TheMiner SWF в появившемся окне. Нажмите OK.

7. Закройте окно Settings и нажмите на кнопку «SWF Profiler» на панели инструментов (или выберите пункт меню «View» с таким же названием).

8. Нажмите кнопку «Active Profiler«, которая находится в левой части открывшегося окна «Profiler» и в появившемся списке выберите «TheMiner».

9. Чтобы TheMiner запускался при запуске SWF, нажмите кнопку «Auto-start Profiler» если она выглядит как красный флаг, чтобы она стала зелёным флагом.

Вот и всё — теперь вы можете скомпилировать проект и увидеть что TheMiner работает!

Такой способ использования TheMiner во FlashDevelop может показаться не таким удобным, как использование TheMiner в качестве SWC, однако все эти действия вам придётся проделать лишь один раз! Когда вы в следующий раз захотите использовать TheMiner, подключенный таким образом — достаточно будет лишь проверить, включен ли флаг Auto-start profiler!

Итак, в каких случаях следует использовать TheMiner в качестве одного из профайлеров FlashDevelop?
- если мы не хотим включать код TheMiner в наш SWF или AIR приложение после окончания его отладки / разработки во FlashDevelop и при передаче его для тестирования (отправка на ftp / устройство и т.д.)
- если мы хотим простого подключения профайлера, без необходимости что-то менять или добавлять в свой код, сохраняя его чистым от дополнительных библиотек

PS: Сегодня вышло обновление для TheMiner с множеством исправлений и некоторыми интересными нововведениями.

]]>
http://blog.codestage.ru/ru/2012/03/22/theminer-and-flash-develop/feed/ 3
Упакованный SWF. Как распаковать? http://blog.codestage.ru/ru/2012/03/07/packed/ http://blog.codestage.ru/ru/2012/03/07/packed/#comments Wed, 07 Mar 2012 18:23:03 +0000 focus http://blog.codestage.ru/ru/?p=428 Читать далее ]]> Привет, друзья!
В этом посте я хотел бы рассказать о том, что из себя представляет упакованный 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, ничего, кроме названий, нельзя скрыть от глаз профессионала высокого класса с достаточным уровнем мотивации ;)

Есть вопросы, идеи, комментарии? Оставляйте всё в виде комментариев к посту, пишите!

]]>
http://blog.codestage.ru/ru/2012/03/07/packed/feed/ 11