В этой статье напишу возможные методы распознавания наличия взлома программы и методы борьбы с пиратством.
Внимание! Способы, описанные ниже, необходимо использовать только для ознакомления с возможными методами защиты. За их работоспособность данный сайт ответственности не несет в связи с постоянно обновляющейся технологией компиляции приложений, а также самой iOS. Материал должен служить источником вдохновения на создания новых защитных систем.
Итак, хватит формальностей, начнем:
1) Проверка идентификатора. Самое простое:
#if !TARGET_IPHONE_SIMULATOR int root = getgid(); if (root <= 10) { //Крякнута } #endif
Здесь ведется проверка того, что идентификатор процесса приложения не находится среди идентификаторов системных процессов(меньше или равно 10). Если находится, то программа точно взломана.
Метод очень прост в реализации, портативен и не требует дополнительных библиотек. Однако не все крякнутые приложения используют системный идентификатор процесса. Кроме того, код легко обнаруживается знающими хакерами.
2) Проверка файлов. Следующий метод, предложен одним форумчанином крупного зарубежного форума девелоперов:
#define kInfoSize 500 //Place your NSLog Plist Size into the above Define statment NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; NSString *path = [NSString stringWithFormat:@"%@/Info.plist", bundlePath ]; NSDictionary *fileInfo = [[NSBundle mainBundle] infoDictionary]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:path traverseLink:YES]; if (fileAttributes != nil) { NSNumber *fileSize; if (fileSize = [fileAttributes objectForKey:NSFileSize]) { NSLog(@"File Size: %qi\n", [fileSize unsignedLongLongValue]); //Best to see the File Size and change it accordingly first NSString *cSID = [[NSString alloc] initWithFormat:@"%@%@%@%@%@",@"Si",@"gne",@"rIde",@"ntity",@""]; BOOL checkedforPir = false; if ([fileInfo objectForKey:cSID] == nil || [fileInfo objectForKey:cSID] != nil) { if ([fileSize unsignedLongLongValue] == kInfoSize) { checkedforPir = true; } } if (!checkedforPir) { //Крякнута } [cSID release]; } }
Метод проверяет размер наличие «Signerldentity» в Info.plist. Если данная строка имеется в файле, то бьем тревогу. Для большей маскировки от хакера рекомендуется разбивать строку как можно больше. Рекомендую делать побитовое смещение искомых строк.
3) Проверка файлов. Более компактное и быстрое исполнение 2-го метода:
NSBundle *bundle = [NSBundle mainBundle]; NSDictionary *info = [bundle infoDictionary]; if ([info objectForKey: @"SignerIdentity"] != nil) { //Крякнута }
Не требует дополнительных библиотек, портативен. Рекомендуется опять же разбивать строку, т.к. открытую строку легко найти даже HEX-редактором. Рекомендуется прятать строку таким образом:
#define INIT_STRING @"SignerIdentity" NSString *aString = INIT_STRING;
Это скрывает «SignerIdentity» из шестнадцатеричного редактора, что делает взлом намного сложнее.
4) Проверка файлов. Некая новинка:
NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:(@"%@/_CodeSignature", bundlePath)]; if (!fileExists) { //Крякнута NSLog(@"Крякнута"); } BOOL fileExists2 = [[NSFileManager defaultManager] fileExistsAtPath:(@"%@/CodeResources", bundlePath)]; if (!fileExists2) { //Крякнута NSLog(@"Крякнута2"); } BOOL fileExists3 = [[NSFileManager defaultManager] fileExistsAtPath:(@"%@/ResourceRules.plist", bundlePath)]; if (!fileExists3) { //Крякнута NSLog(@"Крякнута3"); }
Метод банально проверяет существование «_CodeSignature», «CodeResources», и «ResourceRules.plist». Во время взлома приложения эти файлы не всегда учитываются и удаляются. Не самый лучший метод, однако не просто взламывается. Также рекомендую делать побитовое смещение искомых строк.
5) Проверка даты. Следующий метод — вершина технологий:
NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; NSString *path = [NSString stringWithFormat:@"%@/Info.plist", bundlePath]; NSString *path2 = [NSString stringWithFormat:@"%@/AppName", bundlePath]; NSDate *infoModifiedDate = [[[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES] fileModificationDate]; NSDate *infoModifiedDate2 = [[[NSFileManager defaultManager] fileAttributesAtPath:path2 traverseLink:YES] fileModificationDate]; NSDate *pkgInfoModifiedDate = [[[NSFileManager defaultManager] fileAttributesAtPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"PkgInfo"] traverseLink:YES] fileModificationDate]; if ([infoModifiedDate timeIntervalSinceReferenceDate] > [pkgInfoModifiedDate timeIntervalSinceReferenceDate]) { //Крякнута } if ([infoModifiedDate2 timeIntervalSinceReferenceDate] > [pkgInfoModifiedDate timeIntervalSinceReferenceDate]) { //Крякнута }
Очень уникальный метод. Проверяет дату модификации Info.plist, бинарника, а также PkgInfo. Изначально дата модификации этих 3-х файлов одинакова. После взлома дата модификации файлов Info.plist и бинарника — меняется. Если дата Info.plist и бинарника позднее PkgInfo, то программа взломана или были попытки.
6) Анти-отладчик. Далее очень мощный метод:
// The iPhone SDK doesn't have , but it does have ptrace, and it // works just fine. typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data); #if !defined(PT_DENY_ATTACH) #define PT_DENY_ATTACH 31 #endif // !defined(PT_DENY_ATTACH) void ZNDebugIntegrity() { // If all assertions are enabled, we're in a legitimate debug build. #if TARGET_IPHONE_SIMULATOR || defined(DEBUG) || (!defined(NS_BLOCK_ASSERTIONS) && !defined(NDEBUG)) return; #endif // Lame obfuscation of the string "ptrace". char* ptrace_root = "socket"; char ptrace_name[] = {0xfd, 0x05, 0x0f, 0xf6, 0xfe, 0xf1, 0x00}; for (size_t i = 0; i < sizeof(ptrace_name); i++) { ptrace_name[i] += ptrace_root[i]; } void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); ptrace_ptr_t ptrace_ptr = dlsym(handle, ptrace_name); ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0); dlclose(handle); }
Метод намного сложнее всех предыдущих. Вкратце, мы проверяем используется ли наша программа отладчиком или нет. Если да, то останавливаем отладчик. При взломе необходимо подключить отладчик к приложению (Кракулоусы — не исключение). Метод успешно останавливает процесс отладки и завершает его.
7) Шифрование. Метод проверки на SignerIdentity, но с применением шифрования:
NSLog(@"Substitution Cipher:"); char symCipher[] = { '(', 'H', 'Z', '[', '9', '{', '+', 'k', ',', 'o', 'g', 'U', ':', 'D', 'L', '#', 'S', ')', '!', 'F', '^', 'T', 'u', 'd', 'a', '-', 'A', 'f', 'z', ';', 'b', '\'', 'v', 'm', 'B', '0', 'J', 'c', 'W', 't', '*', '|', 'O', '\\', '7', 'E', '@', 'x', '"', 'X', 'V', 'r', 'n', 'Q', 'y', '>', ']', '$', '%', '_', '/', 'P', 'R', 'K', '}', '?', 'I', '8', 'Y', '=', 'N', '3', '.', 's', '<', 'l', '4', 'w', 'j', 'G', '`', '2', 'i', 'C', '6', 'q', 'M', 'p', '1', '5', '&', 'e', 'h' }; char cfile[256]; [[[NSString alloc] initWithString:@"SignerIdentity"] getCString:cfile maxLength:sizeof(cfile) encoding:NSUTF8StringEncoding]; NSLog(@"%s",cfile); for (int i=0; i<strlen(cfile); i++) cfile[i] = symCipher[cfile[i]-0x21]; NSLog(@"%s",cfile); for (int i=0;i<strlen(cfile);i++) { for (int j=0; j<sizeof(symCipher); j++) { if (cfile[i] == symCipher[j]) { cfile[i] = j+0x21; break; } } } NSLog(@"%s",cfile);
На первый взгляд этот код может показаться сложным, но это не так. Мы используем шифр замены, основной форме криптографии, для изменения строки «SignerIdentity». Так как вы можете видеть, метод шифрует строку «SignerIdentity» в зашифрованную строку , а затем расшифровываем ее обратно в «SignerIdentity»
Расшифровываем шифрованную строку и записываем ее в signIdentity:
char symCipher[] = { '(', 'H', 'Z', '[', '9', '{', '+', 'k', ',', 'o', 'g', 'U', ':', 'D', 'L', '#', 'S', ')', '!', 'F', '^', 'T', 'u', 'd', 'a', '-', 'A', 'f', 'z', ';', 'b', '\'', 'v', 'm', 'B', '0', 'J', 'c', 'W', 't', '*', '|', 'O', '\\', '7', 'E', '@', 'x', '"', 'X', 'V', 'r', 'n', 'Q', 'y', '>', ']', '$', '%', '_', '/', 'P', 'R', 'K', '}', '?', 'I', '8', 'Y', '=', 'N', '3', '.', 's', '<', 'l', '4', 'w', 'j', 'G', '`', '2', 'i', 'C', '6', 'q', 'M', 'p', '1', '5', '&', 'e', 'h' }; char csignid[] = "V.NwY2*8YwC.C1"; for (int i=0; i<strlen(csignid); i++) { for (int j=0; j<sizeof(symCipher); j++) { if (csignid[i] == symCipher[j]) { csignid[i] = j+0x21; break; } } } NSString *signIdentity = [[NSString alloc] initWithCString:csignid encoding:NSUTF8StringEncoding];
Аллилуйя! Мы скрытно получили строку signIdentity со значением «SignerIdentity». Обнаружить это отладчиком для хакера — практически невозможно. После данного кода можно спокойно использовать стандартную проверку на наличие «SignerIdentity» с использованием нашей тайно полученной строки signIdentity.
8) Проверка на JailBreak №1. Самый анархичный метод:
BOOL yes; //DETECT JAILBREAK if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"/%@%@%@%@%@%@%@", @"App", @"lic",@"ati", @"ons/", @"Cyd", @"ia.", @"app"]] || [[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"/%@%@%@%@%@%@", @"pr", @"iva",@"te/v", @"ar/l", @"ib/a", @"pt/"] isDirectory:&yes] || [[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"/%@%@%@%@%@%@", @"us", @"r/l",@"ibe", @"xe", @"c/cy", @"dia"] isDirectory:&yes]) { //Jailbreak обнаружен }
По сути не является методом определения взлома. Проверяет наличие папок, без которых крякнутую версию просто не поставить. Метод подходит только очень отчаявшимся разработчиком. которые в штыки воспринимают джейлбрейк, и категорически против него. Рекомендуется хорошо прятать искомые пути (шифрование или разбивание).
9) Проверка на JailBreak №2. Продолжение легенды:
NSError *error; //Строка для записи NSString *str = @"Проверка записи. Если вы это сможете прочитать, то у вас джейлбрейк!"; //Пробуем записать файл в системный раздел [str writeToFile:@"/private/test_jail.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error]; //Если записалось без ошибок if (error==nil) { //Jailbreak обнаружен }
Метод проверяет возможность записи файла в системную директорию, которая без джейлбрейка невозможна.
Наиболее скрытые (для отладчика) методы завершения процесса:
//1 close(0); //2 [[UIApplication sharedApplication] terminate]; //3 [[UIApplication sharedApplication] terminateWithSuccess]; //4 UIWebView *a = [UIWebView alloc]; UIWindow *b = [UIWindow alloc]; UIView *c = [UIView alloc]; UILabel *d = [UILabel alloc]; UITextField *e = [UITextField alloc]; UIImageView *f = [UIImageView alloc]; UIImage *g = [UIImage alloc]; UISwitch *h = [UISwitch alloc]; UISegmentedControl *i = [UISegmentedControl alloc]; UITabBar *j = [UITabBar alloc]; [a alloc]; [b alloc]; [c alloc]; [d alloc]; [e alloc]; [f alloc]; [g alloc]; [h alloc]; [i alloc]; [j alloc]; //5 system("killall SpringBoard"); //6 notify_post("com.apple.language.changed");
подобные действия отладчиком сложно заметить, а тем более понять, что именно они провоцируют аварийное завершение.
Пользуйтесь и ни в чем себе не отказывайте!
Немає коментарів:
Дописати коментар