
Как защитить вашу игру от ребрендинга

Перевод поста с блога ФГЛ
Все слышали много жалоб в последнее время о «ребрендинговых кражах» игр – когда недобросовестные сайты ломают вашу свеже-выпущенную игру и заменяют спонсорскую заставку, лого и другие спонсорские элементы. Эта практика в основном ограничивается несколькими странами, особенно это относится к китайским порталам, и происходит это уже в течении многих лет, но теперь это становится все более распространенным явлением.
Все это равносильно краже игры, и полностью противоречит спонсорской модели. Ну и естественно, модифицировать и распространять защищенные авторскими правами игры – вполне незаконно… вот почему такая практика менее распространена в США где спонсор легко может подать иск против другого портала за такое непорядочное поведение. А вот против порталов работающих в других странах принять подобные правовые меры бывает затруднительно.
Но разработчики игр могут активно противодействовать! На самом деле это довольно просто – защитить вашу игру от незаконного ребрендинга. Для начала нужно кое-что узнать о SWF файлах.
SWF формат позволяет легко подменять изображения
Файл формата SWF является открытым форматом, хорошо документированым Adobe. SWF файлы состоят из небольших отдельных кусочков, примерно как ZIP архив состоит из большого количества отдельных, упакованных в нем файлов. Каждое из ваших изображений в SWF файле хранится в виде отдельного блока.
Точно так же как вы можете распаковать ZIP архив, вы можете распотрошить SWF файл на отдельные кусочки, после чего можно изменить (подменить) некоторые части (например, изображения) и потом собрать все обратно в SWF. Описаный сценарий конечно сильно упрощен, на самом деле там все намного сложнее чем в ZIP формате, но идея очень похожая. Все ваши изображения, звуки, векторные обьекты и код хранятся в виде отдельных блоков внутри файла.
Но это не недостаток SWF формата – это его особенность! В Adobe сделали формат достаточно простым в использовании, чтобы его можно было прочитать или модифицировать любыми средствами. Удобство использования этого формата – вот причина большого разнообразия замечательных инструментов и языков для Flash. Но, как всегда, есть и обратная сторона… это значит, что кто-нибудь с легкостью может заменить ваши изображения на свои. Есть куча отличных инструментов которые позволяют разобрать ваш SWF файл на части а потом собрать все обратно уже с новыми изображениями.
Защитные инструменты «защищают» исходный код, но не изображения…
Вы можете быть удивлены: «А что же тогда делают все эти обфускаторы и SWF-шифровалки?». Они не шифруют изображения. Они зашифровывают код. Если мы опять используем аналогию с ZIP архивом – ваш скомпилированный AS2 или AS3 код хранится в SWF файле в отделтных блоках, полностью изолированных от изображений, звуков и всего остального. Все что программы для защиты флеш файлов могут сделать – это сильно запутать (обфусцировать) именно эти блоки с кодом, после чего взломщику становится намного сложнее разобраться в нем.
Но они не способны защитить ваши изображения… Так как изображения хранятся непосредственно в формате JPEG, нет смысла говорить об их шифровании, потому что после шифрования флеш-плеер не знал бы, как прочитать файл!
Используйте ваш код для защиты изображений
Если защитить можно только код, как тогда защитить ваши изображения? Для этого нужно добавить собственный код, который проверяет действительно ли это те изображения которые должны быть в игре. После этого вам нужно будет воспользоваться Kindisoft или другими средствами для обфускации кода, чтобы злоумышленник (например, китайский хакер) не смог удалить ваш код проверяющий изображения.
Но как наш код узнает что изображение «правильное»? Нам понадобятся контрольные суммы – небольшие числовые значения которые математически представляют большие объемы данных. Возможно, вы слышали о MD5 или SHA1… та же идея. На самом деле кроме алгоритмов MD5 и SHA1 есть большое количество других способов подсчитать контрольную сумму для чего-нибудь. Вот самый простой (и соответственно самый быстрый) способ получения контрольной суммы изображения:
function checksum_Basic(bmp:BitmapData):uint {
var bounds:Rectangle = new Rectangle(0, 0, bmp.width, bmp.height);
var bytes:ByteArray = bmp.getPixels(bounds);
var total:uint = 0;
for (var loop:uint = 0; loop < bytes.length; ++loop) total += bytes[loop];
return total;
}
Эта функция просто пробегает по всем пикселам изображения (предварительно помещенных в массив) и суммирует их RGB значения. В результате получается одно число. Если изображение изменится – изменится и это число! Теперь ваша игра просто в рантайме должна проверить все необходимые изображения на соответствие ранее полученым вами контрольным суммам. Если суммы не те, что вы ожидали, значит изображение было изменено.
Для начала напишите несколько строк временного кода для генерирования контрольных сумм изображений которые вы хотите защитить. Предположим, у вас есть экранный обьект или изображение с идентификатором «picture». Вот как вы можете вызвать вышеописанную фонкцию:
var bitmapData:BitmapData = new BitmapData(picture.width, picture.height);
bitmapData.draw(picture, new Matrix());
trace("Контрольная сумма изображения " + checksum_Basic(bitmapData));
Теперь в окне трассировки вы должны увидеть что-то на подобие: «Контрольная сумма изображения 4921452». Сгенерируйте таким способом контрольные суммы для всех изображений которые вы хотите защитить, после чего скопируйте их в код вашей игры. В игре в нужном месте вы вызываете ту же самую функцию для проверки суммы изображения:
var bitmapData:BitmapData = new BitmapData(picture.width, picture.height);
bitmapData.draw(picture, new Matrix());
if (checksum_Basic(bitmapData) != 4921452) {
// китайцы атакуют!
}
Повторите эту процедуру для каждого изображения. Убедитесь, что изображения полностью загружены, прежде чем делать проверку!
Если ваша игра обнаружит что картинки подменены – она может показать какое-нибудь предупреждающеее сообщение и прекратить работу. { … вырезанно … здесь идут нравоучения Эрика о политкорректности и прочих предосторожностях не имеющих отношения к даной теме … }
Двойной удар – использование двух контрольных сумм вместе
На первый взгляд, вышеописанный метод – это все что вам необходимо для предотвращения подмены изображений в игре. Но данная функция все же довольно слабая для защиты. Теоретически, взломщик может подобрать такую картинку, контрольная сумма которой будет совпадать с суммой вашей картинки. Это конечно достаточно серьезная задача для взломщиков, но вы никогда не можете знать насколько настойчивы они могут быть, пытаясь взломать вашу игру.
Поэтому, если вы хотите сделать действительно параноидальную защиту для своей игры – давайте добавим еще одну контрольную сумму! Мы используем хорошо известный алгоритм SHA256. Почему? Потому что это очень безопасно… и потому что очень просто! И ни какой математики не нужно с нашей стороны. Если вы используете Flex, то этот алгоритм уже в него встроен:
import mx.utils.SHA256;
function checksum_Flex(bmp:BitmapData):String {
var bounds:Rectangle = new Rectangle(0, 0, bmp.width, bmp.height);
var bytes:ByteArray = bmp.getPixels(bounds);
return SHA256.computeDigest(bytes);
}
Или если вы не пользуетесь Flex, можно скачать AS3 Core Lib, добавить в папку с вашим проектом и использовать оттуда функцию SHA256:
import com.adobe.crypto.SHA256;
function checksum_AS3CoreLib(bmp:BitmapData):String {
var bounds:Rectangle = new Rectangle(0, 0, bmp.width, bmp.height);
var bytes:ByteArray = bmp.getPixels(bounds);
return SHA256.computeDigest(bytes);
}
Эта функция работает точно так же как и вышеописанная функция checksum_Basic, лишь с тем отчичием что выдает вместо обычного числа строку примерно такого вида: «ce68f9ebff02e8019702a3b4bc8d93535ffcf6b4844f24c0cf37e93bb871b97b». Все что вам нужно – собрать такие строки для всех изображений которые необходимо защитить, после чего «зашить» их в код вашей игры, точно так же как было описано выше. Если во время выполнения картинка дает другую строку, скорее всего она была подменена.
Вы можете удивиться, зачем использовать оба варианта? Почему бы просто не использовать только строковую функцию, ведь ее сложнее сломать? Дело в том, что в SWF файле все строковые значения хранятся вместе в небольших блоках отдельно от кода. Поэтому, даже если ваш код защищен обфускатором, взломщик иногда все-равно может видеть и изменять ваши строковые выражения. (Kindisoft secureSwf имеет режим, который шифрует строки, также как и код, но другие шифраторы не всегда это делают.) Так что опытный хакер может найти в файле строки похожие на строковые контрольные суммы и заменить их на строки для своих собственных изображений, обходя таким образом ваш проверочный код. На самом деле это будет довольно утомительно для взломщика найти и заменить все контрольные суммы для всех изображений, особенно если их у вас много, – хакеру пришлось бы долго экспериментировать чтобы понять какая сумма связання с нужной картинкой… Но не стоит недооценивать терпение флеш воров (особенно китайских).
Намного сложнее отследить числовые значения, особенно если в вашей игре ипользуется много разных числовых констант. Использование одновременно и числовой и строковой контрольных сумм для защиты вашей игры делает ее взлом еще более сложным.
Но помните, что все это будет работать только в том случае если вы используете обфускатор! Если нет, тогда весь описанный способ будет пустой тратой времени, потому что вор сможет просто посмотреть ваш код, что и как у вас сделано и просто убрать все эти ваши проверочные блоки кода. Если вы до сих пор не купили обфускатор – сделайте это как можно быстрее, это очень необходимая инвестиция. { … опять вырезанно … здесь Эрик хвалит Kindisoft secureSwf и агитирует всех покупать его на ФГЛ … }
Остерегайтесь дешевых трюков перемещающих ваши изображения за пределы видимой области!
То, каким образом работает SWF формат одновременно является и добром и большим злом. Ну, если чесно, то для разработчиков, пытающихся сделать свои игры более защищенными это все же является большим злом и проклятием. Пример: даже если вы защитите ваши изображения описанным выше способом – хакеры по-прежнему могут просто удалить их! Как? Просто спрятать их (убрать с видимой области)! Если вы используете Flash IDE и размещаете изображения в фреймах мувиклипов, тогда все данные о фрейме (включая свойства изображений находящихся в нем) будут сохранены отдельно от кода. А как было показано выше, защитить можно только код… и хитрый хакер может просто поменять координаты изображения, так чтобы убрать его с экрана или уменьшить высоту и ширину до 1 пикселя, или поменять масштаб, или прозрачность, или видимость, или …
Если вы размещаете лого и заставки в фреймах – вам прийдется написать дополнительный код для проверки изображений на «правильность» координат, размеров и других важных свойств. Если картинка должна быть с координатами 0,0 и шириной, например 640х480, код в вашей игре должен проверить действительно ли это так на самом деле!
Если вы пишете игру полностью на AS3 или используете Flex Builder, Alchemy, или haXe, то даная проблема вас не касается, потому что создание и позиционирование изображений происходит непосредственно в коде (как и назначение всех других свойств). Изображения могут быть спрятаны или убраны за пределы экрана только в играх сделаных полностью во Flash IDE, где все картинки встраиаются в игру непосредственно в фреймах на таймлайне.
Дополнение.
pjbaron отметил, что есть несколько свойств, которые могут быть скорректированы: alpha должно быть 1.0, visible должно быть true, и scaleX и scaleY должны иметь значения, которые от них ожидались. Много переменных для проверки!
Дополнение №2.
Rocketman предложил другой подход: использовать контрольные суммы для отрендеренного экрана всей вашей игры, а не отдельных изображений. Это легко сделать – просто в нужный момент сделайте скриншот всей сцены и поместите его в BitmapData, сгенерируйте для него контрольную сумму и используйте как было описанно выше для отдельных изображений. Если контрольная сумма вашей сцены отличается от того что вы ожидали – значит происходит что-то незапланированное.
Дополнение №3.
Но эта идея не совсем хорошо работает, потому что если ваша игра будет смасштабирована (растянута или сжата в размерах) на некоторых порталах, контрольная сумма отрендеренной сцены может не совпасть с суммой «зашитой» в проверочном коде.
Так что такой способ не очень рекомендуется применять.
Дополнение №4.
Лучшая на данный момент рекоммендация: просто не размещать брендинг (заставки, лого) в фреймах мувиклипов в Flash IDE. Вместо этого в нужных фреймах пишите ActionScript код который создает необходимые изображения и добавляет их на сцену или в мувиклип с помощью метода addChild(). Таким образом вам удастся избежать подмены свойств а также что-то непреднамеренно испортить самому.
Любители AS2 … это все относится и к вам!
К сожалению, я не знаю, как программировать на AS2… по крайней мере, не достаточно хорошо, чтобы сделать нужные примеры кода. Но все описанные здесь идеи можно применить и для AS2.
В AS2 тоже есть BitmapData, но нет встроенного способа выгрузить все пиксели вместе в массив, так что прийдется использовать BitmapData.getPixel() для получения значений каждого пиксела отдельно. Такой метод будет намного медленне работать чем в AS3 и потому вы не сможете использовать его для всех изображений, особенно в большой игре, – это может быть слишком медленно, – просто используйте метод только для действительно важных изображений!
И так, у вас теперь есть способ как защитить вашу игру от нелегального ребрендинга! Меньше часа работы. Определенно стоит того, чтобы сохранить видимым логотип спонсора и весь остальной брендинг.
*****
P.S.
Перевод старался сделать не дословным, а более понятным и удобочитаемым. От себя добавлю что статья расчитана в основном на новичков, опытные акулы игростроя обо всем этом давно уже должны знать и активно применять на практике :) Хотя, если ваша игра хит – все вышеперечисленные методы всеравно не остановят бесстрашных и многочисленных китайских хакеров :)
- +23
- Nautilus
Комментарии (46)
А может кто-нибудь очень добрый и щедрый напишет пеккейдж для автоматической защиты определенных пикчей?)) Но опять же китайские друзья прочитав даже эту статью стали умнее вдвое. Соревнование пули и брони. Пока что пули пробивают почти все броники))
П.С.: Кто мне ответит — зачем ломать флеш игры?
Читал, что взламывают даже полностью обфусцированные игры, вернее в них подменяют арт, например.
Если же контролировать арт, а код зашифровать, то будет сложновато.
Любой китайский
школьниквредитель, даже в заобфусцированном файле сможет найти оператор if, отвечающий за проверку, потому что сразу после него срабатывает «китайцы атакуют!»,и поменять:
на:
и не важно сколько раз считается сумма и какими алгоритмами
Легко ли ее найти среди других условных конструкций?
Для параноиков (я такой) — чексумму тоже стоит по-своему проверять. Так что по типовому коду не найти потом функцию в байткоде. Сами чексуммы если они в виде строк — не хранить в виде строк. Итд. Можно и не чексуммой проверять, но тоже надежно.
По памяти — в дампе swfmill были и блоки байткода. (детально не смотрел — так как нужно было другое. потому могу врать в деталях — но мельком видел)
в том числе там были очень прикольные ищеи про вложенные файлы.
Грубо — идея в том, что берем swf — компресим и шифруем. А потом — запихиваем как ресурс в другую swf. (тем же swfmill или как бинарные данные). Внешний swf — грузит все ресурсы, а потом — расшифровывает.
Такое — мне представляется обфескаторы — никак не смогут расколоть даже в теории. (это лечится только пошаговой отладкой и копанием в исходных алгоритмах).
Если громадное желание защитить еще посильнее — тогда можно или напихать «отложенных проверок» (как scmorr выше написал). Или побить игру на отдельные блоки и по мере необходимости расшифровывать. (то есть раскидать защиту и расшифровку по разным местам — что бы защиту выковыривать было сложнее).
Ну и еще можно посмотреть в сторону ширования с односторонним ключом (rsa\idea — помоему называется). когда шировать может только автор имея открытый ключ, а хакеру прийдется переписывать большие куски кода в swf что бы вырезать расшифровку.
— есть лоадер, который грузит файл стримом, и предварительно получил ключик для декриптования
— сервак отдаёт swf файл и каждые 8192 байта шифрует по RC4 ключем
— лоадер принимет поток и каждые 8192 байта декриптует, причём чипер создаётся каждый раз новый
— если у вас на клиенте и серваке будет стандартизированный рандом, то можно
в добавок сделать так, что сервак будет отдавать не 8912 байт, а рандомное количество по сиду
— лоадер соответственно обфускирован, т.ч. прочитать что он там делает довольно трудно
Ключик можно перехватывать.
Что такое чипер?
Чем обфусцируете?
По идее можно всю флешку потом достать из памяти?
нет, сам лоадер не большой, а игры здоровые 30-50 мб.
т.к. контент грузится по 8кб и сразу этот кусок расшифровывается и ложится в ByteArray, то общая нагрузки минимальная
да, можно, просто в данной реализации он шел через слой авторизации и SSL в виде RC4(JSON), по ключу сессии
так же чтобы получить этот ключ нужно было отправить md5(сессия+loaderInfo.bytesTotal)
если не знать способа шифрования, ключ мало чем поможет
Cipher aka сайфер )
secureSWF
думаю что в любом случае можно флешку достать из памяти, только нужно чтобы руки были заточены под это
скоро напишу статью-отчет по защите.
действительно работет только обфускация, виртуализация и раскиданные проверки.
Мне кажется что лучше всего работает то — что быстрее всего сделать. И что нестандартное (1) и доставит больше всего геммороя ломателю (2).
(1) защита от декомпиляции, шифрование, внедрение одного swf в другой.
(2) размазанные проверки. В том числе не только по месту расположения, но и по времени (когда защита проявляется неявно — например тормозами)
Тем более что большая часть шагов — даст защиту от большинства попыток поломать код. А на большее — мне кажется — время не стоит и тратить. (на тот же сбыт или геймплей\идеи проектов время лучше потратить — отдача должна быть больше)
Ну — и мне кажется — что самого swfmill может хватить что бы поправить байткод и собрать обратно (как раз что бы вырезать проверки типа if(CRC() == CorrectCrc).
Но — правда — не имею опыта работы с алхимией. Смущает незнание — насколько она хорошо работает на реальных довольно больших проектах. Очень смущает статус исследовательского проекта.
Вообще — очень нравится сама идея писать C\C++ код — который можно будет отпортировать на тот же iphone или еще куда.
Haxe — как портируемый вариант тоже держу в голове, но нужно добраться и проверить — насколько оно на практике хорошо и надежно работает.
использовать можно, но не удобно…
мало ли что там затесалось в сложных условиях
А по сути уводить проверки из стартового класса в глубь кода.
Пример обфускации.
А вообще непробиваемой защиты нет, есть случаи когда затраты на взлом (деньги или время) делают его невыгодным.
Но если получится зашифрованная структура с непонятными именами, а в ней еще зашифрованные классы , то найти что-то будет проблематично
Ну и как параноик :) — все равно можно на всякий случай важные методы и переменные называть по-левому с самого начала, самому. То-есть не писать метод computeChecksum() а сразу getActionBoomFX().
В основном, как я понимаю, восстановить из обфускации можно только читаемость компилятором. Вообще, я не сильно копал декомпиляторы, но меня поражает, что я там не нашел галочки — «при деобфускации генерить свои читабельные имена». То-есть, если видишь, что было обфусцировано на нечитабельные компилятором имена с кривыми символами — жми галку и получай имена типа class001, var999.
Далее — декомпилировать зашифрованный киндисофтом по агрессивному варианту файл и идеально собрать его заново — это эпическая задача. Я думаю пересборка китайцами делается только в крайних случаях для тупо незашифрованных игр. А так — возможно они декомпилят чтобы найти нужные места, но обратно не пытаются собрать, просто дальше в байткоде правят. Думаю в большинстве случаев и декомпилировать не надо — порезал строки и арт, который в открытом виде считай и все.
Вобщем, поэтому я и ратую за нетипичные гадости, которые ни поиском по байткоду, ни в куче деобфусцированного кода так просто не найти.