Защита своими руками

Всем привет.

Итак. Нам бы не хотелось, чтоб нашу флешку распотрошили какие-нибудь школьники и запустили под своим именем, что-нибудь внутри поменяв :-) Да и вообще чтоб кто-то там ковырялся.

Какие наши варианты? Пойти и купить защиту. Если есть лишняя сотка баксов, так и делайте. Только непробиваемых защит не бывает, не забывайте.

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

Но возникли два обстоятельства: киндисофт на fgl мою игру защитил так, что она перестала запускаться, хотя ничего такого в ней нет сложноустроенного. А чуть позже я читал ради любопытства статьи на haxe.org и вот здесь haxe.org/com/libs/format/abc увидел такой код: (приводится с сокращениями)

loader = new flash.display.Loader();
loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, onLoaded);
loader.loadBytes(swfBytes.getData());
    
public static function onLoaded(e) {
    var m = loader.contentLoaderInfo.applicationDomain.getDefinition("Main");
    var inst : Dynamic = Type.createInstance(m,[]);
    trace(inst.test());
}


Я когда его читал, еще помнил про облом с киндисофтом. И подумал: ведь так можно реализовать защиту самому.

Ну и сразу шашки в руки и вперед. Базовый вариант, которым я с вами тут и делюсь, был готов часа за полтора. Вот скриншот того, что получилось:


На всякий случай: то, что у меня получилось, это очень простая защита только от школьников.

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

Общая идея:
1. Портим нашу флешку, но так чтоб можно было починить :-)
2. Эмбедим ее в новую флешку как просто бинарный файл
3. В новой флешке получаем как БайтАррэй
4. Чиним испорченное
5. Загружаем лоадером и вуаля

Теперь код. Самое главное:

//ProtectorMain.as
package 
{
        import flash.display.Loader;
        import flash.display.Stage;
        import flash.display.StageScaleMode;
        import flash.events.Event;
        import flash.utils.ByteArray;
        
        public class ProtectorMain
        {
                private var myLoader:Loader;
                private var myStage:Stage;
                
                public function ProtectorMain (st:Stage):void 
                {
                        myStage = st;
                        
                        [Embed(source = '../bin/file.bin', mimeType = "application/octet-stream")]
                        const fileClass:Class;
                        var bytes:ByteArray = new fileClass as ByteArray;

// восстанавливаем данные. Каждый как умеет :-)
                        
                        myLoader = new Loader();
                        myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
                        myLoader.loadBytes(bytes);
                }
                
                private function loaded(e:Event):void
                {
                        myLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaded);
                        myStage.addChild(myLoader.content);
                }
                
        }
}

Прелоадер:

// ProtectorPreloader.as
package 
{
        import flash.display.DisplayObject;
        import flash.display.MovieClip;
        import flash.display.Stage;
        import flash.events.Event;
        import flash.events.ProgressEvent;
        import flash.utils.getDefinitionByName;
        
        public class ProtectorPreloader extends MovieClip 
        {
                
                public function ProtectorPreloader () 
                {
                        addEventListener(Event.ENTER_FRAME, checkFrame);
                        loaderInfo.addEventListener(ProgressEvent.PROGRESS, progress);
                        // show loader
// тут каждый сам за себя
                }
                
                private function progress(e:ProgressEvent):void 
                {
                        // update loader
// и тут тоже каждый сам за себя
                }
                
                private function checkFrame(e:Event):void 
                {
                        if (currentFrame == totalFrames) 
                        {
                                removeEventListener(Event.ENTER_FRAME, checkFrame);
                                startup();
                        }
                }
                
                private function startup():void 
                {
                        // hide loader
                        stop();
                        loaderInfo.removeEventListener(ProgressEvent.PROGRESS, progress);
                        var st:Stage = stage;
                        stage.removeChild(this);
                        var mainClass:Class = getDefinitionByName("ProtectorMain") as Class;
                        new mainClass(st);
                }
                
        }
}

Ну и код на питоне:

import sys, os

def main():
    #paths
    inputPath = os.path.join(os.path.dirname(sys.argv[0]), "bin", "unprotected.swf")
    outputPath = os.path.join(os.path.dirname(inputPath), "file.bin")
    
    print '"encrypting"', inputPath, "to", outputPath

    #read file
    inFile = open(inputPath, "rb")
    data = inFile.read()
    inFile.close()

    #corrupt data
    #всё сами!

    #write files
    outFile = open(outputPath, "wb")
    outFile.write(data)
    outFile.close()
    
if __name__ == "__main__":
    main()


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

Готово. Теперь компилируем игру, потом протектор. Всё должно работать.

Расширять идею можно до бесконечности.
  • +9

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

0
давно у нас не было постов с кодом) сам хоть и пользуюсь киндисофтом, но почитать было интересно. плюсанул.
+1
В таком варианте защита отламывается минут за 10, но как прототип — хорошо.
Спасибо за пост. Плюсанул.
  • ryzed
  • ryzed
+1
Во-первых зависит от того, как именно испортить данные :-) Или можно как-то по простому восстановленную swf из памяти достать?
Можно еще трюк сделать: при компиляции релиза указывать в качестве основного класс, который ничего не делает. И вызывать из протектора совсем другой класс. Тогда собранная swf не будет запускаться.
Кроме того никто не мешает еще и обфускатором игру обработать.

А главное уже и так школьники идут мимо. Туда, где попроще.
0
1. Выдрать «испорченный» байтэррей (единственное, что пока не знаю как сделать. Я не практик :).
2. Скопипастить кусок кода, который восстанавливает данные, не разбираясь.
3. Вставить в свою swf, которая прогоняет восстановление и сохраняет рабочий файл на диск или сервер.

Если не сложно, можешь сделать тестовую защищенную флэшку? Интересно поковырять.
0
Могу. Тебе куда?
0
ed.ryzhov@gmail.com
0
Можешь любою с мочи лайв апдейтом взять, там принцип такойже
0
Кстати да. Можно свой лайв апдейт замутить. С блекджеком и шлюхами!
0
Апдейт — единственное, на что годится. Для защиты использовать — лишний АБСОЛЮТНО НЕНУЖНЫЙ гемор. Потому как от той-же обфускации и криптования важных строк, толку будет намного больше.
0
защита отламывается минут за 10
За меньше. Файл не закриптован даже. Если криптовать, то декриптор будет видно в декомпилере, восстановить его код — скопипастить из декомпилера нужный кусок. Вместо цепляния клипа к стейжу написать код для сохранения его в файл.
+1
Я написал, что защита только от школьников :-)

Криптование файла я оставил на совести читателя. Впрочем и у себя не криптовал особо.
А так да, даже если закриптовать, то декриптор будет видно. И что делать? Если флешку может проиграть флеш-плеер, то и человек сможет ее достать и проиграть.

Отличие от обфускации, что прячет не только код, но и арт :-) И обфускации не отменяет, т.е. применять по идее в комплексе. Тут была ссылка на бесплатный обфускатор, кстати.
А что за криптование важных строк? Это что? Каких строк?
0
с мочи-кодом, например, чтоб нельзя было просто поменять строку с ним на свою.
+2
Побыл немного тупым школьником.
Был неправ, признаю. Взлом занял полтора часа :)

Основная проблема заключалась в том, что коммерческие декомпиляторы (как, впрочем, и бесплатные) не умеют выдергивать «эмбеденные байтэрреи» из флэшки. Они их просто не видят.
Пришлось найти исходники простенького парсера swf и заточить под задачу.
  • ryzed
  • ryzed
0
Но, вообще, да, хорошее начало для крутой защиты.
Если приделать виртуальную машину для декриптора, то смысла ломать уже не будет, слишком дорого получится.
Будет время — надо будет заняться.
0
Непонятно почему декомпиляторы не видят пока байтэррей.
Но вручную не трудно вытащить его из флешки, загрузить в свою и восстановить, если известен алгоритм.

Хотя часть народа отсеется.
0
Для этого и нужна виртуальная машина.
Например, сделать декриптор на брейнфаке, и пускай разбираются.
0
Декриптор (алгоритм) будет же в прелоадере (например) содержаться.
Или я чего-то не понял?
+1
Декриптор (алгоритм) будет написан на эзотерическом языке программирования (http://ru.wikipedia.org/wiki/Brainfuck).
В прелоадере будет интерпретатор этого языка (он маленький, 1-2 килобайта) и сама программка на нем (++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++...).
Чтобы понять, как расшифровывается файл — надо разобрать, что делает программка.
Чтобы заставить разбираться — завязаться внутри декриптора на контрольную сумму флэшки или что-то подобное.
Как-то так.
0
Только один момент: не надо понимать как расшифровывать файл: достаточно это просто сделать, можно и не понимая :-)
А сделать это имея программу хоть бы и на брейнфаке — не вопрос.
0
Надо разбираться, в этом и юмор.
Я же написал, «завязываемся на контрольную сумму флэшки или что-то подобное».
Если выдрать декриптор и зашифрованный кусок, контрольная сумма будет другая и у нас расшифруется мусор.
0
Так. На чью контрольную сумму, внешней флешки или внутренней? Кто и на каком этапе считает эту контрольную сумму?
0
Логично, чтобы был результат — контрольную сумму внешней флэшки. Считать внешней прогой и прошивать в конечную флэшку
0
На контрольную сумму внешней флэшки.

Считает — программка на брейнфаке(к примеру).
На вход ей подается вся загруженная флэшка, полностью stage.loaderInfo.bytes, она считает ключ и декриптует (ксорит, в простейшем случае) этим ключом внутреннюю.

Тут получается «бесконечный» цикл, криптование внутренней флешки изменяет ключ внешней, но это можно обруливать.

Вообще, очень интересная тема, давно хотел заняться, все руки не доходят.
0
Вот меня этот бесконечный цикл и заставил спрашивать. Как его избежать? ИМХО никак.
0
Можно пропускать содержимое «внутренней» флешки.
Ее изменение ничего не даст (она просто распакуется неверно), а остальная часть (внешняя) — останется защищенной.
0
Только тогда — это все равно что шифровать константой. Или нет и я что-то упускаю?
0
Как только мы изменим хоть на один байт внешнюю часть (допишем сохранение расшифрованной внутренней флешки на диск), у нас поменяется ключ и расшифруется мусор.

Для того, чтобы узнать ключ, надо разобраться с алгоритмом расчета, а он у нас на «брейнфаке».

Против снятия дампов поможет постепенная расшифровка. Каждый уровень отдельно.
0
Ну то есть да, в принципе ясно. Действительно уже на шаг больше надо делать, а не просто достать код и исполнить. Хотя подсунуть на вход алгоритма старую флешку наверное не так уж трудно, вполне будет видно место, где он к ней доступ получает. Но уже чуть сложнее.
А, как я уже писал, абсолютной защиты здесь быть не может.
+1
Это да, так кейгены вроде часто делают.
Просто вырезают алгоритм и пропускают через него же.
+1
Интересная задумка…
Кстати тут что-то похожее делалось
groups.google.com/group/ruflash/browse_thread/thread/63bbed92692d037?hl=ru
+1
Да не, это уже перебор, такими темпами можно всю игру на браинфаке написать)) Ключь и алгоритм и криптограмма лежат в одном месте => криптостойкость = 0. Как вариант ныкать ключ, привязывать его генерацию к домену, контрольной сумме и т.д. Но все равно можно сделать дамп памяти и выковарить байтаррэй в первозданном виде.
+1
Комменты жгут. Сразу почувствовал себя тупым школьником :)
0
:))
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.