Чёрные флаги и черепа на заднице
Аннотация
Пиратство ваших программ — проблема или нет? Как эффективно бороться с любителями чёрных флагов и доморощенными хакерами и одновременно оставить время на то, чтобы создать по-настоящему шикарное приложение — рассказывает один из авторов Kali Anti-Piracy, коммерческой библиотеки защиты приложений для iPhone.
Пиратство, вселенная и всё остальное
Так уж вышло, что под Мак я пишу вообще довольно давно (с 1993 года), а под iPhone — задолго до того, как появилась возможность официально разрабатывать для него приложения. И всё это время, параллельно с разработкой я сталкиваюсь с одним и тем же явлением — пиратством софта.
Отношусь ли я к пиратству отрицательно? Честно говоря, положа руку на сердце, скорее нет, чем да. В общем-то это — нормальный процесс, часть экосистемы производитель-потребитель, и отстутсвие пиратства, как правило, означает лишь низкую привлекательность платформы для потребителя и, как следствие, для разработчиков программного обеспечения. Конечно же, пиратство ударяет в первую очередь по разработчикам, но это происходит в разной мере — чуть ниже я коснусь этого момента подробнее.
Как же у нас обстоят дела с пиратством на iPhone? О, тут всё просто здорово! Практически любая выпущенная мало-мальски популярная программа становится достоянием широких масс любителей халявы в течение нескольких минут после её выпуска. Для этого есть специализированные сайты, форумы, и другая инфраструктура вроде автоматизированных установщиков прямо на iPhone.
При всём этом, количество «любителей халявы» на iPhone не настолько велико по отношению к общему количеству пользователей iPhone и iPod touch (которых уже набралось около 55 миллионов) — по разным оценкам, оно составляет около 1.5 миллионов человек, из которых «активны» (то есть постоянно занимаются установкой и распространением «ломанных» версий программ) всего около
Именно поэтому большинство крупных производителей программ для iPhone (особенно, компании, специализирующиейся на играх) — Gameloft, EA Mobile, и другие, предпочитают игнорировать вопрос пиратства — количество тех, кто их игру сворует, значительно ниже тех, кто купит её.
Что касается «мелких» разработчиков, которых в App Store большинство, их проблема пиратства затрагивает значительно больше. Особенно это касается тех приложений, которые внезапно стали относительно популярными и на которые обращаются взоры среднестатистического пирата (который сам ничего ломать не может, но с удовольствием скачает «халявную» версию и будет ей пользоваться). В таких случаях, по данным самих разработчиков, уровень пиратства зачастую достигает
Чёрные флаги, татуировки, iPhone и Россия
Что же мы имеем в России? Российский рынок iPhone составляет где-то полмиллиона устройств (плюс-минус 100 тысяч), из которых, увы, подавляющее большинство пользователей обожают «халяву». Не буду разбирать причины этого явления, так как это выходит за рамки этой статьи, замечу лишь, что это связано с широким распространением процедуры взлома защиты телефона для открытия системного диска на запись, который и делает возможным запуск «освобождённых от защиты» программ. В России это действие было необходим подавляющему большинству аппаратов, так как они требовали программной разблокировки в те времена, когда iPhone ещё не продавался официально. В дальнейшем тенденция открывать диск на запись с установкой на них Cydia/Installer (альтернативных App Store установщиков) сохранилась, и несмотря на то, что «белых» аппаратов становится больше, доля «открытых на запись» по-прежнему велика.
К чему я веду? А к тому, что если вы делаете программу, направленную на российский рынок, будьте готовы к тому, что 80% пользователей будут ей пользоваться бесплатно. Увы и ах, но это так, и эти цифры подтверждаются как нашим опытом, так и опытом тех, кто что-либо выпускал в русский App Store.
Конечно же, можно и нужно в таком случае защищать свои программы от любителей помахать чёрным флагом и вытатуировать черепушку на заднице. При этом важно соблюсти баланс — нужно сделать так, чтобы время, затраченное на защиту своей программы, было достаточным для того, чтобы её было не очень просто взломать, но одновременно не отняло значительную часть того времени, что вы могли бы потратить на создание новых функций и других полезных штук в своём программном продукте.
Ломать — не строить!
Конечно же, для того, чтобы понять, как программы ломаются, необходимо хотя бы в общих чертах представлять себе, как они защищаются.
Когда пользователь приобретает программу в App Store (бесплатные приложения тоже «приобретаются», только со стоимостью ноль долларов), ему предоставляется файл ipa (который, кстати, является всего-навсего zip-архивом), содержащий в себе:
- Cамо приобретённое приложение (.app в подпапке Payload)
- Картинку, которая показываается в iTunes вместо иконки приложения (файл iTunesArtwork)
- Мета-данные из App Store (файл iTunesMetadata.plist) — идентификатор приложения, его категория, стоимость и т.п.
- Сведения о пользователе, который приобрёл приложение и ключи для расшифровки зашифрованной части (файлы в папке SC_Info в application bundle).
При установке на устройство (через iTunes либо другими средствами) специальный демон-установщик проверяет правильность цифровой подписи приложения (оно должно быть подписано сертификатом Apple), и, если всё в порядке, устанавливает его на пользовательский раздел устройства (/var/mobile/Applications/).
Сам раздел бинарного файла, содержащий исполняемый код, шифруется собственным алгоритмом Apple, расшифровщик которого находится в ядре. Шифрование устроено таким образом, что удалив данные о том, кто приобрёл приложение, становится невозможным его расшифровать. Расшифровка происходит в момент загрузки приложения, «на лету», когда сегмент с исполняемым кодом загружается в память телефона.
Таким образом, купив приложение, не так-то просто им сразу же поделиться с друзьями — у них оно просто не будет устанавливаться и запускаться, только если не передать им вместе с ним свой login и пароль для App Store аккаунта. Да и то существует ограничение на 5 устройств на аккаунт.
Защита сама по себе достаточно хорошая — насколько я знаю, шифрование реализовано частично на аппаратном уровне, чтобы было непросто сделать программу-расшифровщик. Но как всегда есть одно «но», которое, к слову, было найдено в первый же день работы App Store одним из членов iPhone Dev Team netkas — дело в том, что расшифрованный сегмент хранится в памяти в… расшифрованном виде. Это и определило всю методологию взлома программ, использующуюся до настоящего времени.
Итак, что же нужно делать, чтобы «сломать» приложение? Во-первых, его… надо честно купить! Ведь без этого невозможно будет расшифровать зашифрованную часть приложения. Поэтому все «ломанные» программы были хотя бы однократно куплены «хакерами» (в кавычках потому, что то, что фактически делают эти люди, мало относится к классическому определению хакинга и даже кракинга).
После чего приложение запускается и к нему подсоединяется (на устройстве) отладчик gdb, который и делает дамп памяти уже расшифрованного исполняемого сегмента. Полученный расшифрованный сегмент заменяет собой зашифрованный в бинарном исполняемом файле программы, и в заголовках файла (так называемые load commands) меняется флажок cryptID с 1 на 0 в LC_ENCRYPTION_INFO, чтобы система при загрузке знала, что теперь там находятся не зашифрованные данные.
Также из bundle приложения удаляется папка SC_Info, в которой лежат данные о том, кто купил этот продукт в App Store.
Естественно, все эти действия нарушают цифровую подпись приложения, и оно перестаёт запускаться (проверки на целостность также идут на уровне ядра). В «джейлбрейкнутых» устройствах часть проверок отключена, и проверяется только правильность хэша подписи бинарного файла, но не то, кем именно он подписан (в «честной» системе запускаться могут только файлы, подписанные Apple в том или ином виде). Поэтому заново создаётся «фальшивая» подпись бинарного файла (а точнее, только хэш, который проверяется ядром) при помощи утилиты ldid, написанной saurik — автором небезызвестного приложения-установщика Cydia.
Поскольку подпись bundle теперь нарушена, в Info.plist приложения дописывается ключ SignerIdentity со значением (обычно) «Apple iPhone OS Application Signing». Это делается для обхода проверки подписи установщиком на устройстве, который и распаковывает файлы .ipa. При дописывании ключа файл Info.plist обычно конвертируется из бинарного формата в текстовой (XML) — это связано с тем, что большинство утилит, манипулирующих файлами plist, умеют работать только с текстовым его вариантом.
После всех этих манипуляций дополнительно удаляется файл iTunesMetadata.plist, как потенциально содержащий приватную информацию о том, кто купил приложение, и всё вместе упаковывается в новый ipa-файл.
Конечно же, чтобы встроенный установщик ipa-файлов «принял» файл со «сломанной» подписью, он тоже должен быть соответствующим образом модифицирован.
Весь этот процесс сейчас делается автоматически и не требует от «взломщика» никаких специальных технических навыков. Бóльшая часть из них даже не подозревает, что именно происходит в момент «взлома» купленного приложения — неудивительно, что процесс пиратства поставлен на достаточно широкую ногу, так как фактически всё, что требуется — это купить программу, запустить скрипт-ломалку и подождать полторы-две минуты — после чего можно заливать полученный взломанный файл в Интернет и получить свою капельку славы, как крутого хакера.
Спасение утопающих — дело рук…
Конечно же, разработчики тоже не дремлют, и всячески пытаются предотвращать прискорбные факты взлома своих приложений. На данный момент существует несколько подходов в защите своего приложения от пиратства, предлагаю рассмотреть их все.
Можно использовать готовые решения от третьих лиц, такие как AntiCrack и Kali Anti-Piracy. Но они, хотя и реализуют дополнительные уровни проверок и защиты, стоят денег, пусть и небольших, так что можно попробовать обойтись своими силами — это не будет столь же эффективно, но отпугнёт многих хакеров, так как приложению будет требоваться «ручное вмешательство», а таких специалистов — сильно меньше.
Итак, что же можно сделать самому?
Во-первых, всегда можно проверить на наличие ключа SignerIdentity в своем Info.plist файле. Это самая широко распространённая проверка среди разработчиков, но с появлением iPhone OS 3.0, где данный ключ строго говоря не нужен, отсутствие его не является гарантией того, что приложение не сломано.
NSString* signerIdentityKey = @"SignerIdentity";
if ([[[NSBundle mainBundle] infoDictionary] objectForKey:signerIdentityKey])
{
// нас взломали!
}
Во-вторых, можно проверять формат Info.plist — хотя то, что он в бинарном формате, опять-таки не говорит с полной уверенностью, что нас не сломали:
NSData* infoPlistData = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Info.plist"]];
NSPropertyListFormat format;
if ([NSPropertyListSerialization propertyListFromData:infoPlistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:nil])
{
if (format != NSPropertyListBinaryFormat_v1_0)
{
// нас взломали!
}
}
В третьих, можно проверять наличие файла iTunesMetadata.plist в папке по соседству с bundle вашего приложения. Для процесса разработки эту проверку лучше отключить, так как данный файл создаётся только для приложений, установленных из App Store:
NSString* metadataFilename = @"iTunesMetadata.plist"; // зашифруйте как-нибудь!
if (![[NSFileManager defaultManager] fileExistsAtPath:[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:metadataFilename]])
{
// нас взломали!
}
В четвёртых, можно проверять флаг cryptID внутри своего файла. Если он равен 0, значит, исполняемый сегмент расшифрован. Это уже не самый тривиальный код (я позволил себе его взять у landof):
#import <dlfcn.h>
#import <mach-o/dyld.h>
static BOOL is_cracked () {
const struct mach_header *header;
Dl_info dlinfo;
/* Fetch the dlinfo for main() */
header = dlinfo.dli_fbase;
if (dladdr(&main, &dlinfo) == 0) {
NSLog(@"Could not find the main() symbol (very odd)");
return YES;
}
/* Compute the image size and search for a UUID */
struct load_command *cmd = (struct load_command *) (header + 1);
for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {
/* Encryption info segment */
if (cmd->cmd == LC_ENCRYPTION_INFO) {
struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
/* Check if binary encryption is enabled */
if (crypt_cmd->cryptid < 1) {
/* Disabled, probably stolen */
return YES;
}
/* Probably not stolen? */
return NO;
}
cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
}
/* Encryption info not found */
return YES;
}
В пятых, хоть я и упоминал это в первом пункте, постарайтесь зашифровать строки, которые используется при проверке защиты (Info.plist, и т.д.). И постарайтесь не располагать проверки в методах вашего app delegate под названием isMyAppCracked — это даёт дополнительную наводку тем, кто будет ковыряться в защите руками. Помните о том, что даже если вы и включили простые проверки защиты в свой код, опытный хакер найдёт их за
Заключение
Не забывайте о том, что пираты — пиратами, а вы пишете приложение для своих будущих (и, возможно, уже существующих) пользователей. Потому не стоит тратить на защиту слишком много времени — ведь оно пригодится для более важных вещей. Если вас действительно беспокоит пиратство, купите себе одно из профессиональных решений для защиты — стоят они до 100 долларов на продукт, а экономят достаточно времени.
И ещё — невзламываемых защит не существует. Помните об этом.
Ресурсы
- AntiCrack — библиотека для защиты от пиратства (donationware, от 30€)
- Kali Anti-Piracy — другая библиотека для защиты от пиратства (от 100$)
- ReadWriteWeb — интересная статья про мотивацию пиратов (на английском)