Разработчик, исследователь
0,0
рейтинг
10 января 2011 в 23:02

Разработка → Способы «защиты» flash-приложений


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

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


— введение


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

Борьба между системами защиты и системами обхода\снятия защит ведётся постоянно. Это связано с тем, что любому желающему доступна спецификация формата SWF и байткода (тут, здесь и ещё тут), в который компилируется ActionScript3 (далее AS3) код. Это позволяет свободно писать собственные библиотеки и приложения для разбора SWF файлов на составляющие (графику, звуки, байткод и т.д.), и делать что угодно с байткодом, например:
  • писать анализаторы байткода для декомпиляции ABC (ActionScript ByteСode) файлов, которые можно найти в тэгах "DoABC" в скомпилированном AS3 SWF файле (а в AS2 код обычно находится в тэгах "DoAction" и "DoInitAction");
  • писать приложения, модифицирующие байткод, например, для его обфускации с помощью различных приёмов, которые могут использовать специфику декомпиляции в своих целях;
  • писать приложения, позволяющие изменять (патчить) байткод в уже скомпилированных SWF файлах;
  • модифицировать FP для вывода (трейса) исполняющегося байткода;
В области защиты бывают разные цели и средства. Выбирайте только то, что подходит для Вашего проекта, не занимайтесь лишней работой.


— содержание


Мы рассмотрим несколько способов (они разные не только по своей реализации, но и по целевому назначению) с примерами обхода некоторых из них, а также ряд инструментов, которыми можно пользоваться при исследовании (своего) кода:
  1. URL-Lock (привязка к одному или нескольким доменам), защита от локального запуска
  2. Переменные, ресурсы и классы
  3. Водяные знаки (watermarks)
  4. "Упаковывание" SWF
  5. Динамическая генерация кода и редактирование SWF
  6. Запутывание кода (obfuscation) и скрытие данных
  7. Защита от сохранения SWF файла из памяти
  8. Инструментарий исследователя flash

— 1. URL-Lock, защита от локального запуска


Цель – заставить SWF файл работать в определенных условиях, например, только под одним или несколькими доменами, либо запретить локальный запуск.
После определения того факта, что SWF запущен не там, где должен бы, можно запрограммировать его поведение как угодно. Вы можете отправлять на сервер неугодные домены\URL (если это не localhost) и заносить в какой-нибудь лог.
Подлинные домены (под которыми должен работать SWF файл) лучше держать в зашифрованном виде или не держать в клиенте вообще (принимать от сервера также в зашифрованном виде).
Рассмотрим несколько способов привязки к доменам и определения локального запуска.

Привязка к доменам с помощью получения текущего URL\домена


Это самый распространённый способ привязки к домену. Для получения текущего URL или домена обычно используют:

а) loaderInfo:
  • root.loaderInfo.url (_root._url – для AS2) – содержит путь до самого SWF файла, если SWF загружен с помощью другого SWF, то root.loaderInfo.url у загруженного SWF останется прежним
  • root.loaderInfo.loaderURL – содержит путь до самого SWF файла, либо до его загрузчика, если он был загружен с помощью другого SWF файла
  • root.loaderInfo.loader — содержит ссылку на экземпляр класса Loader, либо null, если SWF не был загружен внешним загрузчиком (заметьте, что root.loaderInfo.loader == root.parent, а root.parent == stage, если SWF не имеет загрузчика). Причём, если загрузчик не находится в доверенной зоне (находится в другом SecurityDomain), то при обращении к root.loaderInfo.loader или к root.parent из загружаемого SWF, будет возникать исключение SecurityError, в сообщении которого также будет содержаться ссылка до загрузчика
Пути не изменятся, если SWF встроить в HTML страницу на "неродном" домене без перемещения самого SWF файла.

б) LocalConnection: позволяет получить имя домена, под которым в данный момент запущен SWF файл с помощью LocalConnection.domain.
Если чужой SWF находится в домене, отличном от домена Вашего SWF, и Вы загрузили тот SWF в текущий домен безопасности так:
var context:LoaderContext = new LoaderContext(true);
context.securityDomain = SecurityDomain.currentDomain;
loader.load(new URLRequest(url), context);
или с помощью Loader.loadBytes, то LocalConnection.domain загруженного SWF будет содержать домен Вашего SWF.
Пути не изменятся, если SWF встроить в HTML страницу на "неродном" домене без перемещения самого SWF файла.

в) ExternalInterface: позволяет использовать JavaScript (далее JS). Например, путь до текущей страницы, в которую встроен SWF, можно получить с помощью window.location.href. Для вызова JS используйте ExternalInterface.call. Т.к. call в качестве первого параметра принимает название метода, а window.location.href – свойство, то следует использовать toString():
var myHTMLUrl:String = ExternalInterface.call("window.location.href.toString");
Кстати, домен тоже можно получить через ExternalInterface.call:
var domain:String = ExternalInterface.call ("eval", "document.domain");

ExternalInterface можно использовать и для вызова своих JS функций на той странице, где находится SWF – как изначально существующих, так и созданных Вами с помощью eval:
var customJavaScriptCode:String = "function customJS(){alert('Yes, this is a custom JS');}";
ExternalInterface.call("eval", customJavaScriptCode);
ExternalInterface.call("customJS");
И не стоит забывать, что этот способ требует выполнения JS, что не всегда возможно.
При попытке обойти с помощью встраивания в HTML страницу на "неродном" домене без перемещения самого SWF файла, выполнение ExternalInterface.call будет генерировать SecurityError.

г) FlashVars: иногда, при размещении SWF в HTML страничке или при загрузке из другого SWF, передают путь до домена и\или иные данные через FlashVars (что на самом деле – плохо, т.к. это явная "дыра" для отвязки от доменов):

FlashVars можно передать в качестве параметра:

<object ...>
	<param name="FlashVars" value="var1=value1&var2=value2" />
</object>
или через имя файла:
<object ...>
	<param name="movie" value="somecoolmovie.swf? var1=value1&var2=value2" />
</object>
Дотянуться до FlashVars можно так:
— в AS3 – root.loaderInfo.parameters.var1
— в AS2 – _root.var1

Привязка к доменам без получения текущего URL или домена


Этот способ основан на кроссдоменной политике безопасности. Привязываемый SWF при запуске пытается загрузить по прямому пути "специальный" файл-пустышку с определённого хоста, на котором находится файл crossdomain.xml с разрешением загрузки ресурсов только с доверенных доменов, на одном из которых и должен находиться SWF. Соответственно, если "ворованный" SWF файл будет обращаться к "специальному" файлу с домена, не прописанного в crossdomain.xml, произойдет нарушение изолированной среды (sandbox security violation) и "специальный" файл не загрузится. Также при этом сгенерируется исключение и сработает событие SecurityErrorEvent.SECURITY_ERROR у экземпляра класса LoaderInfo (Loader.contentLoaderInfo). Это позволит определить то, что SWF находится не там, где должен бы.
К "специальному" файлу следует обращаться по прямому пути, чтобы предотвратить его подмену.

Определение локального запуска


Локальный запуск SWF можно определить с помощью получения ссылки на SWF или HTML и проверки её на наличие подстроки "file://".
Если SWF запущен вне HTML, то метод ExternalInterface.call вернёт null.
Также можно проверить тип FP, в котором работает SWF, с помощью Capabilities.playerType:
  • "ActiveX" – при проигрывании через ActiveX FP, который, например, используется в IE
  • "Desktop" – для среды выполнения Adobe AIR (кроме SWF-содержимого, загруженного HTML страницей, со свойством Capabilities.playerType имеющим значение "PlugIn")
  • "External" – при проигрывании через внешний FP или при запуске из-под Flash Professional IDE
  • "PlugIn" – при проигрывании через браузерный плагин (и для SWF-содержимого, загруженного HTML страницей в AIR приложении)
  • "StandAlone" – при проигрывании через автономный FP (при запуске локального SWF файла в браузерах, значение Capabilities.playerType != "StandAlone")
Ещё один способ – с помощью сравнения Security.sandboxType с Security.REMOTE.

Все эти приёмы имеют свои преимущества и недостатки — некоторые не требуют сервера, но требуют перекомпиляции SWF файла при изменении доменов, к которым должна осуществляться привязка, а некоторые — наоборот.
В кодах к статье (ссылка на архив есть в конце статьи), в папке "1-URL Lock", вы найдёте пример с реализацией некоторых из описанных методов и пару примеров патчей одного из методов.
Внимание! Большинство приёмов URL-Lock можно обойти, работая из-под локального сервера, такого, как Denwer или XAMPP.


— 2. Переменные, ресурсы и классы


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

Защита переменных


Переменные надо прятать и защищать от изменений. Наиболее простой способ скрытия – написание своих "обёрток" вокруг базовых классов.
Например, CryptInt, CryptString и т.д. В самих классах можно написать методы для получения и отдачи значения, шифруя или расшифровывая его, что спрячет переменную в памяти. Взгляните на реализацию классов CryptInt и CryptString в кодах к статье, в папке "2-Memory&Domain\MemoryExample".
Также, для сокрытия значений переменных, можно разбивать их на составляющие, перемешивать, сдвигать и т.д. Можно даже попробовать написать некий конвейер, который будет перемещать скрываемое значение из одних переменных в другие, освобождая использованные переменные для GC (Garbage Collector). В качестве "временных хранилищ" для таких вот перерабатываемых переменных, можно использовать ByteArray, возможно, с привлечением "Алхимии" (fastmem из Azoth, Memory из Apparat).
А чтобы переменные не смогли поменять, даже если их найдут, то можно использовать, например, сравнение значения переменной с эталоном. Причём сравнение можно производить как при изменении переменной или обращении к ней, так и через заданные промежутки времени.

Подмена ресурсов


Подменить SWF целиком, либо подгружаемые внешние файлы можно в кэше браузера. В фальшивом SWF файле, или в подменённом ресурсе могут быть произведены любые изменения графики (размеры, прозрачность, сдвиг, частота кадров и т.д.) и кода (который делает то, чего бы вам меньше всего хотелось).
Чтобы усложнить задачу недоброжелателю (гарантированно защититься от подмены нельзя), следует проверять подлинность загружаемых данных и самого себя – можно, например, хранить MD5 и сравнивать при загрузке, причем MD5 хранить где-нибудь подальше и в зашифрованном виде.
Если SWF общается с сервером – производить скрытые проверки – отсылать на сервер MD5 загружаемых ресурсов, например.

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

Переопределение классов


Скажу сразу, что гарантированно от этого защититься также не получится.
Подменять классы можно с помощью загрузки целевого SWF в ApplicationDomain загрузчика. Чтобы это сделать, можно написать загрузчик, содержащий класс с таким же именем, что и у подменяемого, и загрузить SWF в свой домен:
var appDomain:ApplicationDomain = ApplicationDomain.currentDomain;
var swfLoader:Loader = new Loader();
swfLoader.load(new URLRequest('original.swf'), new LoaderContext(false, appDomain));
Таким образом, загружаемый SWF вынужден будет "общаться" с классом из своего загрузчика. Примеры реализации Вы сможете посмотреть в кодах к статье, в папке "2-Memory&Domain\applicationDomain". Вы встретите там примеры подмены классов с помощью загрузки в дочерний и в свой домен. В "жертве" Вы сможете найти несколько вариантов проверки на загруженность (которые обходятся заменой класса, в котором они содержатся).
Можно попытаться воспрепятствовать этому, загружая основной SWF через собственный загрузчик в новый ApplicationDomain:
var appDomain:ApplicationDomain = new ApplicationDomain(null);
var swfLoader:Loader = new Loader();
swfLoader.load(new URLRequest('original.swf'), new LoaderContext(false, appDomain));
и установив прочную связь между загрузчиком и основным SWF так, чтобы их было очень трудно "разлучить". Однако, если подменят загрузчик и сумеют эту связь сохранить, то эти усилия будут напрасны.

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


— 3. Водяные знаки (watermarks)


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

Вот некоторые их типы:

  1. Хорошо заметные элементы: текст, логотип, изображение и т.д.
    Если метка "впаяна" (относится как просто к изображениям, так и к графике в SWF), то её удаление придётся осуществлять вручную, редактируя данные, которые подверглись применению метки, что может оказаться очень сложной или вовсе невыполнимой задачей.
    Если же метка лежит отдельно (либо добавляется программно), например, на самом верхнем "слое" в SWF, то шансы аккуратно её удалить гораздо выше.
  2. Скрытые элементы. Они могут быть где угодно. От таких водяных знаков зачастую невозможно избавиться, т.к. никто кроме их создателей не будет знать, где искать. Такие метки должны быть уникальными, дабы была возможность доказать в суде, что это действительно метки, и что их невозможно повторить.
Этот пункт получился весьма ущербным, однако, сказать тут больше особо нечего, просто не забывайте, что такие средства защиты тоже существуют.


— 4. "Упаковывание" SWF


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

Вы можете добавить SWF в свой flex или flash проект с помощью тэга Embed, например, так:
[Embed(source = "./dummy.swf", mimeType = "application/octet-stream")]
private var _content:Class;
Добавленный таким образом файл (это может быть любой двоичный файл или несколько файлов, если тэгов Embed будет несколько) помещается в тэг DefineBinaryData.
Далее можно загрузить "упакованный" таким образом файл с помощью Loader.loadBytes примерно так:
var someFile:ByteArray = new _content();
loader.loadBytes(someFile);
Существует масса инструментов для разбора SWF файлов и манипуляции с тэгами, благодаря которым существует возможность заменять или добавлять тэг DefineBinaryData.
Зная всё это, можно набросать свой простой "упаковщик" для автоматизации процесса. Перед оборачиванием, SWF можно как-нибудь преобразовать, например, обработав простым и быстрым симметричным xor’ом, а в загрузчике (который будет содержать "упакованный" SWF) написать код, который будет расшифровывать хранящийся в нём SWF, и уже затем его загружать. Простое "шифрование" с помощью xor нужно лишь для защиты от автоматической обработки декомпиляторами, чтобы SWF файл не находился внутри загрузчика в "чистом виде", более сложное шифрование бессмысленно.
Бывает, файлы "внедряют" и другими способами, например, в виде BitmapData, а также, создают несколько уровней вложенности.

Пример реализации простейшего упаковщика Вы сможете найти в кодах к статье, в папке "3-SWFPacker". Также там лежат два файла: orig.swf и packed.swf – можете попробовать их декомпилировать и сравнить.
Этот способ обходится, как минимум, двумя путями:
— исследование алгоритма расшифровщика в загрузчике и написание собственного расшифровщика (трудоёмко, особенно при большой вложенности загрузчиков)
— сохранение участка памяти, в котором находится уже распакованный и загруженный в FP SWF файл (просто и быстро, особенно, если не использованы средства защиты от сохранения из памяти, о которых написано ниже)



— 5. Динамическая генерация кода и редактирование SWF


Меня уже неоднократно спрашивали – возможно ли во flash в реальном времени генерировать код или создавать и редактировать другие SWF файлы? Ответ – да, это возможно!
Что касается кода, попробуйте AS3Commons ByteCode. Этот проект позволит Вам собирать и выполнять в реальном времени байткод.
Вы можете создавать классы с различными методами и свойствами с нуля, или наследуясь от других классов. Тела методов, например, могут описываются с помощью байткода линейно — methodBuilder.addOpcode(Opcode.getlocal_0), или сразу блоками:
var source:String = (<![CDATA[
    getlocal_0
    pushscope
	...
    getlocal_1
    returnvalue]]>).toString();
methodBuilder.addAsmSource(source);
Вы можете загрузить сгенерированный код в AVM с помощью abcBuilder.buildAndLoad().
Также для манипуляций с байткодом Вы можете попробовать as3abc, который, по словам автора, является частичным портом замечательного framework’а Apparat от небезызвестного Joa Ebert’а.
Если же Вы мечтаете о динамическом исполнении скрипта, а не байткода, то посмотрите на as3scriptinglib. Используя этот проект, Вы сможете в реальном времени компилировать и выполнять ActionScript 3/ECMAScript скрипты, включая JavaScript. Посмотреть на то, как это работает можно во flex демо-приложении. К сожалению, проект давно не обновляется.

Для экспериментов с самим SWF файлом и его структурой, попробуйте as3swf или swfassist.

Возможность генерации кода на лету, а также возможности редактирования и создания SWF в реальном времени, позволяют создавать различные сложные сценарии и приёмы для запутывания злоумышленника и скрытия Вашего кода. Вот, например первые 3 приёма, что пришли мне на ум:
  1. Генерировать и исполнять код\байткод на клиентской стороне по частям, приходящим от сервера, оставляя недоброжелателям в оригинальном SWF из всего кода, лишь то, что Вам не жалко им оставить (код свободных библиотек, кнопок и т.д.) + код расшифровщика и "строителя" уже финального кода.
  2. Собирать SWF по частям на клиентской стороне, аналогично трюку с кодом – можно собирать по тэгам, получая их от сервера, или, распаковывая поодиночке из зашифрованных данных, упакованных в загрузчике.
  3. Создавать самомодифицирующийся в процессе исполнения код, который, например, будет по определённым правилам или шаблонам генерировать одни и те же методы разными способами. Например, i++ можно реализовать как i+=1 (и это будет разный байткод, по крайней мере, он сейчас разный, в FP 10.1), также циклы могут быть реализованы по-разному, и т.д.
    Возможно, удастся так исхитриться, чтобы сгенерированный код изменял те методы, которые его сгенерировали. Например, чтобы в следующей итерации их выполнения сгенерировались уже другие классы и методы, тем самым значительно усложняя и запутывая код, чтобы, в случае его получения злоумышленником, последнему пришлось потратить как можно больше времени на разбор всей этой "каши".
    Предполагаю, что это довольно сложный (или даже невозможный во flash) трюк. Реально работающей реализации этой идеи на flash я пока не встречал. Если Вы встречали – то подскажите где посмотреть, буду рад изучить.

— 6. Запутывание кода (obfuscation) и скрытие данных


Сначала об обфускации. Ей могут подвергаться как исходный так и скомпилированный в байткод код. Обфускация исходного кода не так удобна и эффективна, запутывание байткода выглядит куда как привлекательней, т.к. исходный код должен удовлетворять требованиям компилятора, чего не скажешь о байткоде, в котором, к тому же, есть куда больший простор для проявления фантазии и смекалки.
Байткод обфускация может включать в себя переименование элементов в бессмысленные и недопустимые для компилятора имена, использование некоторых анти-декомпиляторских трюков, скрытие и "замусоривание" кода и др.

Переименование — это, пожалуй, единственный необратимый и 100% работающий способ хоть что-то спрятать во flash. Суть метода заключается в изменении у методов, свойств, пакетов и других элементов имён, которые сохраняются после компиляции, но не используются при выполнении кода. Автоматически восстановить оригинальные имена невозможно, придётся это делать вручную, догадываясь о них по смыслу во время исследования кода. После обфускации новые имена могут содержать недопустимые символы или зарезервированные слова, что не позволит с лёгкостью перекомпилировать и использовать код, полученный после декомпиляции.

Иногда при обфускации байткода расставляют "ловушки" для декомпиляторов, основанные на том, что декомпиляторы анализируют код комплексно, блоками, в отличие от FP, который исполняет его линейно, например:
  • Некорректный "мёртвый код" — создаётся искусственный условный переход, который всегда отрабатывает только одну ветку. Таким образом, во второй ветке мы имеем код, который никогда не выполнится, но будет обрабатываться декомпиляторами. И если там что-то непредсказуемое (а мы можем сделать там что угодно, т.к. FP до туда никогда не доберётся), то некоторые декомпиляторы могут повести себя необычно – например, аварийно завершиться.
  • Непредсказуемые байты в тех областях, которые игнорирует FP, но которые не всегда игнорируют и корректно обрабатывают декомпиляторы.
Кроме того, код можно просто "замусорить", увеличив его в объёмах и снизив его читабельность. Это может негативно сказаться на производительности.

Скрытие данных можно осуществлять разными способами, например:
  • Скрытие в SWF тэгах. Как я уже говорил, можно использовать flex-тэг Embed для встраивания любых файлов в SWF тэг DefineBinaryData, однако, можно создать и SWF тэг своего типа (FP такие тэги просто пропускает) с любыми данными, и затем извлекать их во время выполнения с помощью самораспарсивания.
    Пример есть в файлах к статье, в папке "4-CustomTag" — там находится проект на основе уже упомянутой библиотеки as3swf (изменён класс com.codeazur.as3swf.factories.SWFTagFactory и добавлен com.codeazur.as3swf.tags.TagCustom). При запуске, SWF проверит — нет ли уже в нём нового тэга, и если есть — то выведет текст из него в текстовое поле, а если тэг не найдётся, то создаст сам в своей копии новый тэг со строкой, загрузит получившийся SWF в себя же с помощью Loader.loadBytes() и ещё предложит сохранить его в новый SWF файл.
  • Пользовательские мета данные. Для внедрения своих метаданных, сначала их необходимо объявить в коде (можно перед объявлением класса, метода, переменной):
    package
    {
        [FocusMeta(name="secret", data="123")]
        public class Dummy
        {
            [Cool(key="another secret")]
            public var dummyInt:int;
            public var dummyStr:String;
        }
    }
    
    А затем добавить их названия в "Additional compiler arguments" таким образом: -keep-as3-metadata+=FocusMeta,Cool. После этого, Вы сможете обращаться к своим мета данным через XML, который можно получить с помощью describeType():
    var typeXML: XML = describeType(Dummy);
    var myMeta: XMLList = typeXML.factory.metadata.(@name == "FocusMeta");
    
Некоторые декомпиляторы и дизассемблеры позволяют с лёгкостью находить и исследовать пользовательские тэги и метаданные.

Для того, чтобы самостоятельно научиться создавать качественные помехи декомпиляторам, Вам придётся изучить то, как декомпиляторы исследуют байткод и SWF в целом. Но помните — разработчики декомпиляторов постоянно развивают свои продукты и если Ваш SWF с хитрыми приёмами попадёт к ним, то вскоре эти приёмы могут перестать работать.
Существуют утилиты для защиты SWF, применяющие только уловки против декомпиляторов или методики "упаковки", без обфускации имён, что позволяет получить чуть ли ни оригинальный исходный код, с помощью качественных публичных или собственных декомпиляторов, который компилируется без ошибок и хорошо читается. Ниже я расскажу о "правильных" инструментах, на которые следует обратить внимание.


— 7. Защита от сохранения SWF файла из памяти


Гарантированно защититься от дампа из памяти нельзя. Однако, есть несложный приём, который может усложнить процесс – можно создать множество фальшивых SWF-заголовков (именно по ним обычно ищут SWF файлы в памяти), которые помогут оригинальному SWF затеряться среди них. Чтобы Ваш SWF файл не выделялся на фоне других, можно создать массу фальшивых заголовков с размерами = размерам Вашего SWF файла.
Чтобы заголовки были видны в памяти, достаточно записать их в ByteArray. Вот пример реализации этого способа, вытянутый из загрузчика одного из протекторов:
private function spamMemory(swfBytes:ByteArray):void
{
    var allLen:uint = swfBytes.readUnsignedInt();
    var swfLen:uint = swfBytes.readUnsignedInt();
    var spamMemorySWFLen:uint = swfBytes.readUnsignedInt();
    var spamMemorySWFBytes:ByteArray = new ByteArray();
    spamMemorySWFBytes.endian = Endian.LITTLE_ENDIAN;
    swfBytes.readBytes(spamMemorySWFBytes, 0, spamMemorySWFLen);
    var allBytes:ByteArray = new ByteArray();
    while (allBytes.length < allLen) 
    {
        spamMemorySWFBytes.position = 4;
        spamMemorySWFBytes.writeUnsignedInt((((swfLen * 3) / 4) + ((Math.random() * swfLen) / 2)));
        allBytes.writeBytes(spamMemorySWFBytes);
    };
    Sprite.prototype["__spam_"] = allBytes;
}
Как Вы могли догадаться, swfBytes – это массив байт с началом "шаблонного" заголовка и "настройками" для создания "фальшивых" заголовков, а в цикле длина у "шаблонных" заголовков заполняется случайным значением в заданном диапазоне.


— 8. Инструментарий исследователя flash


Основываясь на собственном опыте, я бы порекомендовал эти инструменты (цены указаны на конец 2010 года):

Декомпиляторы:
  • Action Script Viewer (ASV) – умно анализирует код, строит timeline, видит все тэги, очень многое умеет. Стоимость лицензии на одного пользователя – $80.
  • AS3 Sorcerer (от разработчика ASV) – декомпилирует только код, для анализа кода используется движок ASV, работает быстро и очень качественно. Если Вы приобретали ASV в 2010 году, то лицензию на AS3 Sorcerer Вам предоставят бесплатно. Стоимость лицензии с бесплатными обновлениями на 3 месяца — $22.
  • Sothink SWF Decompiler – можно пользоваться на незащищённых проектах, если под рукой не оказалось ASV. Стоимость лицензии — $79.99.
Дизассемблеры (для работы с байткодом):
  • ASV и Sothink умеют показывать байткод, но этого не всегда достаточно.
  • Nemo440 – "Advanced ActionScript 3/ABC2/Flex 2/Flex 3/Flex 4/AIR disassembler". Бесплатен.
  • Yogda – "AVM2 bytecode workbench" – позволяет ассемблировать, патчить байткод, есть встроенная документация по байткоду, иногда падает при анализе защищённых SWF, однако периодически обновляется и обрастает функционалом. Бесплатен.
  • RABCDAsm – "Robust ABC (ActionScript Bytecode) [Dis-]Assembler" – замечательный консольный ассемблер\дизассемблер байткода. Бесплатен (+доступен исходный код).
Протекторы\обфускаторы:
  • secureSWF — из всей массы продуктов этой категории, что мне удалось попробовать, он единственный сумел качественно позаботиться о целостности и работоспособности всех подопытных приложений и при этом качественно выполнить свою основную задачу. Конечно же, не бывает идеальных продуктов в этой области, но secureSWF даёт очень хороший контроль над всем процессом защиты и позволяет тонко настроить различные параметры. Например, Вы можете настроить переопределяющие правила для переименования, установить уровень "замусоривания" кода и т.д. В самой дорогой редакции, Вы получаете также возможность "упаковывать" SWF или привязать её к домену. К сожалению, цена на этот продукт весьма высока: стоимость Standard редакции составляет $199.
  • irrFuscator — может Вам приглянуться за его возможности по обфускации исходного кода перед компиляцией, однако, я считаю, что такой подход значительно хуже по своему качеству, чем байткод обфускация, т.к. не позволяет использовать запрещённые компилятором символы и различные трюки с байткодом. Этот продукт также умеет работать на уровне байткода, но проекты, которые я пробовал им "накрывать" довольно часто оставались неработоспособными. Стоимость Standard редакции составляет 79 евро.
  • OBFU – пожалуй, это самый дорогой обфускатор для flash, который мне доводилось видеть (стоимость – 1500 евро). Автор обфускатора — Николя Канасье, создатель haXe. Не ясно – поддерживает ли он AS3, т.к. примеры, выложенные на сайте проекта, написаны на as2 и датированы 2005 годом. К сожалению, попробовать его в действии мне пока не довелось, поэтому если вдруг Вы его когда-то пробовали, то не поленитесь, напишите отзыв о нём в комментариях.
Также прошу обратить внимание, что Amayeta SWF Encrypt, DComSoft SWF Protector, Mochi Encryption – это не то, чем Вам стоило бы "защищать" свои проекты (на данный момент), вы потратите Ваши деньги на бесполезную защиту.
Более того, Вы можете и самостоятельно реализовать простейшую обфускацию, как, например, это сделано в примере от makc3d.

Депротекторы\декрипторы и т.д.:
  • SWF Reader – проект одного польского умельца — многофункциональный редактор, позволяет работать со структурами, тэгами и т.д. Также имеет встроенные декрипторы\депротекторы для некоторых известных продуктов (в частности, распаковщики для doSWF, secureSWF). Cтоимость лицензии – 10 евро.
  • SWF Decrypt – поможет Вам избавиться от Amayeta SWF Encrypt и DComSoft SWF Protector (Mochi Encryption также в планах разработчика). Бесплатен.
Разное:
  • SWiX – отличная утилита для редактирования тэгов SWF файла в удобном XML представлении. Бесплатна.
Если Вы знаете иные, действительно заслуживающие внимания инструменты, и готовы о них рассказать, то рассказывайте в комментариях или пишите лично, буду рад с ними познакомиться.


— эпилог


Резюмируя, можно сказать, что, умело пользуясь подручными средствами для защиты своих проектов, Вы сможете отсеять некоторую аудиторию злоумышленников. Однако, если за исследование вашего SWF файла возьмётся профессионал, то, вероятно, он не остановится перед препятствиями и доведёт дело до конца, особенно, если он мотивирован интересной задачей или достаточным денежным вознаграждением. Поэтому всегда здраво оценивайте свой вклад в защиту своих проектов – не тратьте на это слишком много времени и средств, если это того не стоит.
И ещё — не компилируйте SWF с отладочной информацией, т.к. она содержит множество полезных потенциальному взломщику данных, не говоря уже о снижении производительности.

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

Материалы для изучения и источники информации для этой статьи (беспорядочно):
  1. Презентация Claus Wahlers "Hacking SWF" с FITC Amsterdam 2010.
  2. Доклад Симонова Валентина (valyard) "Кто и как взламывает наши игры" с FlashGAMM 2010
  3. Статья Никиты Лещенко на английском о том, как по-простому защитить ресурсы и код, используя шифрование.
  4. Статья от romamik "Защита своими руками".
  5. Статья от Nautilus "Как защитить вашу игру от ребрендинга".
  6. Статья от puzzlesea "Robolander, 25 days later. Защита, борьба с китайцами".
  7. Статья от flashco "Обфускаторы: от бесплатного до €1500".
  8. Alexis' SWF Reference — описание тэгов всех версий формата, описание заголовка SWF файла и многое другое.
  9. OWASP – в некотором роде вики по вопросам безопасности, относящихся к flash (ссылки на статьи, инструменты и т.д.).
  10. Очень впечатляющая статья о модели безопасности во flash от senocular под названием "Security Domains, Application Domains, and More in ActionScript 3.0".
  11. Статьи от Adobe о безопасности во flash.
  12. Официальная статья от Adobe о безопасности во FP 10 (White Paper).
Также в данной статье и кодам к ней могут быть использованы различные материалы из неуказанных тут источников, о которых я не вспомнил.

Файлы к статье: codestage.ru/files/flash_defend_files.7z
Дмитрий Юханов @focus
карма
28,0
рейтинг 0,0
Разработчик, исследователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (33)

  • +1
    Могу до кучи добавить презентацию Валентина Симонова (habrauser Valyard ) — www.slideshare.net/flashgamm/valentin-simonov-who-cracks-our-games-and-how-that-is-done

    Он довольно большой опыт обобщил на эту тему; а презентация эта с доклада на московском Флэшгаме.
    • 0
      Сорри. Оно уже есть 2-м пунктом.
  • 0
    В декомпиляторы можно добавить еще Eltima Flash Decompiler
    • 0
      Спасибо, правда я не стал добавлять этот продукт не случайно. Есть мнение, что авторами Eltima Flash Decompiler и Amayeta SWF Encrypt являются одни и те же люди. Eltima сойдёт для быстрой правки различных элементов в незащищённых SWF, однако тут может лучше подойти Sothink SWF Quicker, о котором я, к сожалению, забыл упомняуть в статье, в связке с ASV.
      • 0
        Вау, не знал, что там такой скандал. Я далек от темы Flash, просто хотел поддержать ребят из родного города.
  • +3
    Давно на Хабре таких классных статей по данной тематике не было. Спасибо вам!
  • 0
    Более совершенный анало CryptInt можно глянуть у MochiMedia: MochiDigits

    www.mochimedia.com/support/dev_docs#MochiDigits

    спасибо за статью =)
  • –2
    Программы в нативном коде прошли все эти этапы эволюционной борьбы «защита-взлом» с десяток лет назад. Результаты неутишительные — защита/обфускация кода выполняемого на стороне клиента практически бессмысленна. Очередной виток. Ну-ну.

    Создаём инструменты для обфускации? PROFIT! Ломаем то, что наделали обфускаторы? PROFIT! И т.д. и т.п. А идея проста — защита кода на стороне клиента ненужна.
    • 0
      Тем не менее, не всегда есть возможность подключения сервера к проекту + не всегда требуется защищать ото всех, иногда достаточно оградиться от тех, кто дальше простых декомпиляторов не пойдёт.
      Ну и врядли у кого-то возникнет желание воровать обфусцированный код каких-нибудь самописных и очень занятных движков для использования и дальнейшего дописывания «под себя» в своих проектах.
  • +1
    github.com/CyberShadow/RABCDAsm
    тоже ниче, нагибал им приложение для вконтактика
  • –6
    Очень хороший и подробный обзор, огромное спасибо!
    Мог бы, плюсанул бы )
    В избранное и перечитать ещё раз по внимательней обязательно )
  • 0
    спасибо, занес в избранное
  • +1
    Вот самая надежная защита — привязка к железу. Это конечно ограничивает пользователя и тоже можно взломать, но как вариант.

    Библиотека для Flex и Air

    code.google.com/p/codegenas3/
    • +1
      Это собственно привязка не к железу, а к системным параметрам (из Capabilities — screenDPI, screenResolutionX, screenResolutionY), что не айс, ибо поменяться они могут в любой момент.

      Полноценной привязки к железу у флеша нет.
      • –1
        Я собственно ещё не смотрел, все планировал. Если так, то действительно ерунда. А написали: The project uses MD5 Encryption to generate the serial key, specific to Machine.
  • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    ждём статью «способы защиты от flash приложений»
    • 0
      Сегодня политика безопаности flash-приложений такова, что ими сложно как-то навредить.
      Раз в год находится какая-нибудь уязвимость и где-то неделю все мы под угрозой.
      Последний раз можно было обойти разрешение на использование камеры и микрофона.
      Вот помнится во времена Flash 5 под Win '98 можно было винт форматнуть :)
  • 0
    Есть еще Flasm — обфускатор.

    Flasm is a free command line assembler/disassembler of Flash ActionScript bytecode. It lets you make changes to any SWF. Flasm fully supports SWFs produced by Macromedia Flash 8 and earlier Flash versions.

    Бесплатный.
    • 0
      К сожалению, оно только с AS2 работает, и это не обфускатор, а асм\дизасм байткода…
  • 0
    Очень круто. Все известные мне способы в одной статье + несколько настоящих ниндзя-техник.
    Только почти все можно обойти, если не пользоваться обфускаторами. Я пользуюсь SWF Encrypt, доволен.
  • 0
    Я тоже им пользуюсь, но после этой статейки как то не очень он мне уже.
  • 0
    Очень интересно получается, только что обошёл url-lock в своей игре обфусцированной SWF Encrypt при помощи Yogda.
    На всё про всё, меньше 5 минут.
    Совсем плохо (((
  • +1
    Спасибо за статью! Очень все обобщенно и понятно. Единственное хотел добавить несколько вещей:

    1. Лучше писать свои утилиты по защите и не выкладывать их в общий доступ.

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

    3. Есть идея загрузчика с кучей тегов DefineBinaryData, картинок, текста, чисел. Загрузчик составляет результирующий swf из кучи картинок, тегов, строк, чисел(естественно написать надо утилиту которая генерирует такой код). Все шифровать ключами, которые тоже не хранить в открытом виде, тоже шифровать с большой вложенностью в нескольких вложенных swf. Проверять контрольные суммы тегов и swf. И самое главное — сделать так что если даже из памяти и выдерут результирующий swf то он не будет работать, т.к. надо обеспечить плотную работу с загрузчиком. Т.е. и сам загрузчик+оригинальный не будет работать на определенном домене, так и оригинальный swf не будет работать вообще.

  • 0
    То, что доктор прописал. Про генерацию байткода вообще не знал.
  • 0
    Отличнейший подбор, но ничего не поможет, игры, особенно клиент-серверные так и будут коммуниздить.
    Кстати, отчего не упомянули их в обзоре? Самые большие баталии воров и авторов, как мне кажется, происходят именно на этом поле — там тоже раздолье: уже не передают информацию в открытую через 80 порт, все шифруют/солят, добавляют в ответ и запрос неверные/ничего не значащие параметры и т.п.
    • 0
      В этой статье я старался писать больше о самом клиенте, чем о клиент-серверном взаимодействии, да и в количестве текста был ограничен, т.к. хотел всё в один пост уместить, чтобы не разбросано было, но всё равно, спасибо, что заметили про шифрование трафика — это, безусловно, также относится к средствам защиты во flash.
  • 0
    as3abc собирать не позволяет. Только парсить. Шедевральный дизассемблер/ассемблер это rabcdasm, регулярно пользуюсь, быстрый, простой, показывает и cinit и scripts. Он гораздо мощнее чем yogda и тем более nemo404. Ну и как же не упомянули abcdump с ключом -D
    • +1
      Спасибо за полезный комментарий! Добавлю таки rabcdasm в список инструментов (я не хотел изначально включать в инструментарий консольные приложения). А «abcdump с ключом -D» — это классика, которая работает, но не так удобна и интересна, как иные продукты из этой области.
      • 0
        кстати не abcdump а swfdump, опечатался.
        • +1
          Угу, но я вас понял, вы ведь про Flex SDK'шный говорили, там только один dump был)
          • 0
            ага, о нём самом.
  • 0
    А ещё к методам защиты от декомпиляции можно отнести написание в байткоде валидных с точки зрения AVM2, но невозможных с точки зрения actionScript3 конструкций, таких как например вызов constructsuper из метода. Нормально работает, декомпилируется как super() в коде метода и не компилируется никак. Ещё множество таких конструкций придумать можно.

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