Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reverse Engineering iOS apps. Lessons learned.

Reverse Engineering iOS apps. Lessons learned.

UIKonf, Berlin @ 2014

Max Bazaliy

May 15, 2014
Tweet

More Decks by Max Bazaliy

Other Decks in Programming

Transcript

  1. @interface  RRSubscription  :  NSObject   {        

     NSString  *_subscriptionID;        unsigned  int  _period;          float  _price;          NSDate  *_creationDate;   }     +  (id)arrayOfSubscriptionsWithJSONArray:(id)arg1;   +  (id)subscriptionWithDictionary:(id)arg1;     @property(readonly,  nonatomic)  NSDate  *creationDate;   @property(readonly,  nonatomic)  float  price;   @property(readonly,  nonatomic)  unsigned  int  period;  
  2. > address (cryptoff + cryptsize) size (base address + cryptoff

    + cryptsize)! > gdb dump memory decrypted.bin 0x3000 0xD23000 ! > Address space layout randomization! > 0x1000 -> 0x5000! > decrypted.bin -> binary! > patch header!
  3. cy# UIApp @"<UIApplication: 0x14632f70>" cy# function tryPrintIvars(a){ var x={}; for(i

    in *a){ try{ x[i] = (*a)[i]; } catch(e){} } return x;} cy# UIApp.keyWindow.subviews[0].nextResponder.topViewController @"<UINavigationController: 0x14596530>" cy# UIApp.keyWindow.subviews[0].nextResponder.topViewController. viewControllers[0] @"<JailbreakDetectionVC: 0x15a5ad10>" cy# JailbreakDetectionVC.messages['isJailbroken'] = function () { return NO }; {} cy# [[[UIView alloc] init] autorelease] @"<UIView: 0x14d71bb0; frame = (0 0; 0 0); layer = <CALayer: 0x14d702b0>>"
  4. - (void)URLSession:(NSURLSession *)session didReceiveChallenge: (NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition, NSURLCredential

    *))completionHandler{! ""…! ""NSData *localCertificateData = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"MyCert” ofType: @"crt"]];! "CFDataRef remoteCertificateData = SecCertificateCopyData(remoteVersionOfServerCertificate);! ""BOOL certificatesAreTheSame =! " [localCertificateData isEqualToData: remoteCertificateData];! ""NSURLCredential* cred = [NSURLCredential credentialForTrust: serverTrust];! ""if (certificatesAreTheSame) {! ""completionHandler(NSURLSessionAuthChallengeUseCredential,cred); " "}! ""else { " " completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,nil);! ""}!
  5. - (AFSecurityPolicy*) googleSecurityPolicy {! NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"google"

    ofType:@"cer"];! NSData *certData = [NSData dataWithContentsOfFile:cerPath];! AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];! [securityPolicy setAllowInvalidCertificates:NO];! [securityPolicy setPinnedCertificates:@[certData]];! [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];! return securityPolicy; }! ! - (void)googleRequest {! AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; ! [manager setSecurityPolicy:[self googleSecurityPolicy]];! [manager GET:@"www.google.com" parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary* responseObject) { ! } failure:^(AFHTTPRequestOperation *operation, NSError *error) {! }];! }!
  6. ! ! #define PTRACE_STRING @"<mlbD3Z1”! NSString *scInfoString = decodeString(PTRACE_STRING);! !

    ! NSData *encryptedData =! [RNEncryptor encryptData:data " " " " withSettings:kRNCryptorAES256Settings " " " " " password:@"passw0rd” " " " " " " error:&error];! ! NSData *decryptedData =! [RNDecryptor decryptData:data " " " " withSettings:kRNCryptorAES256Settings " " " " " password:@"passw0rd” " " " " " " error:&error];! !
  7. static int checkGDB() __attribute__((always_inline))! {! size_t size = sizeof(struct kinfo_proc);!

    struct kinfo_proc info;! memset(&info, 0, sizeof(struct kinfo_proc));! ! int ret, name[4];! name[0] = CTL_KERN;! name[1] = KERN_PROC;! name[2] = KERN_PROC_PID;! name[3] = getpid();! ! if ((ret = (sysctl(name, 4, &info, &size, NULL, 0))))! return ret;! return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;! }!
  8. #import <dlfcn.h>! #import <sys/types.h>! ! #define PT_DENY_ATTACH 31! ! typedef

    int (*ptrace_ptr_t)! (int _request, pid_t _pid, caddr_t _addr, int _data);! ! void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);! ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(handle, [ptraceString UTF8String]);! ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);! dlclose(handle);! !
  9. + (PurchaseManager *)sharedManager {! ! if (isDebugged())! return nil;! !

    static PurchaseManager *sharedPurchaseManager = nil;! static dispatch_once_t onceToken;! " dispatch_once(&onceToken, ^{! sharedPurchaseManager = [[self alloc] init];! });! "! return sharedPurchaseManager ;! }!
  10. const struct mach_header *header =! (struct mach_header *)dlinfo.dli_fbase;! struct load_command

    *cmd = (struct load_command *) (header + 1);! for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {! if (cmd->cmd == LC_ENCRYPTION_INFO) {! struct encryption_info_command *crypt_cmd =! "" " " (struct encryption_info_command *)cmd;! if (crypt_cmd->cryptid < 1)! return NO;! else! return YES;! }!
  11. const char * originalSignature = "5f9b18edc3666be3de79134a40deea5b";! const struct mach_header *

    header;! Dl_info dlinfo;! ! uint32_t * textSectionAddr = (uint32_t *)section->addr;! uint32_t textSectionSize = section->size;! uint32_t * vmaddr = &segment->vmaddr;! ! char * textSectionPtr = (char *)((int)header + (int)textSectionAddr - " " " " " " " " " " " " " " " "(int)vmaddr);! ! unsigned char digest[CC_MD5_DIGEST_LENGTH];! char signature[2 * CC_MD5_DIGEST_LENGTH];! CC_MD5(textSectionPtr, textSectionSize, digest);! ! for (int i = 0; i < sizeof(digest); i++)! "" "sprintf(signature + (2 * i), "%02x", digest[i]);! return strcmp(originalSignature, signature) == 0;!
  12. BOOL isDirectory = NO;! ! NSString *directoryPath = [[[NSBundle mainBundle]

    bundlePath] stringByAppendingPathComponent:@"SC_Info/"];! ! BOOL directoryExists = [[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDirectory];! ! BOOL contentSeemsValid = ([[[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:NULL] count] == 2);
  13. NSString *scInfoString = @"SC_Info/";! NSString *appleIDString = @"appleId";! NSString *appleIDMailAddress

    = @"[email protected]";! NSString *metadataString = @"iTunesMetadata.plist";! NSString *downloadInfoKeyString = @"com.apple.iTunesStore.downloadInfo";! NSString *accountInfoString = @"accountInfo";! ! NSDictionary *iTunesMetadata = [NSDictionary dictionaryWithContentsOfFile:[rootDirectoryPath stringByAppendingPathComponent:metadataString]];! NSString *appleID = [iTunesMetadata objectForKey:appleIDString];! NSDictionary *accountInfo = [[iTunesMetadata objectForKey:downloadInfoKeyString] objectForKey:accountInfoString];! BOOL isValidAppleID = (appleID.length > 0 && [appleID rangeOfString:appleIDMailAddress options:NSCaseInsensitiveSearch].location == NSNotFound);! BOOL isValidDownloadInfo = (accountInfo.count > 0);}!
  14. BOOL dyLibFound = NO;! NSArray *directoryFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:

    [[NSBundle mainBundle] bundlePath] error:NULL];! ! for (NSString *filename in directoryFiles) {! if ([[filename pathExtension] caseInsensitiveCompare:@"dylib"] == NSOrderedSame) {! dyLibFound = YES;! break;! }! }!
  15. Class hooksClass = objc_getClass("hooks");! Class descriptorsClass = objc_getClass("descriptors");! SEL allocWithZoneSelector

    = sel_registerName("allocWithZone:");! ! if (hooksClass != NULL) {! "Method method = ! " class_getClassMethod(hooksClass, allocWithZoneSelector);! " method_setImplementation(method, (IMP)nilImplementation);! }! ! if (descriptorsClass != NULL) {! Method method = ! class_getClassMethod(descriptorsClass, allocWithZoneSelector);! method_setImplementation(method, (IMP)nilImplementation);! }!
  16. ! NSError *error;! NSString *jailTest = @"Jailbreak time!";! [jailTest writeToFile:@"/private/

    test_jail.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];! if(error==nil) {! …! }!
  17. int result = fork();! "if (!result)! exit(0); ! if (result

    >= 0)! return isJail;! return noJail;! ! ! if (system(0)) ! ...! }!
  18. NSArray *jailbrokenPaths = @[@"/Applications/Cydia.app",! @"/Applications/RockApp.app",! @"/Applications/Icy.app",! @"/usr/sbin/sshd",! @"/usr/bin/sshd",! @"/private/var/lib/apt",! @"/private/var/lib/cydia",!

    @"/usr/libexec/sftp-server”,! @"/private/var/stash"];! ! for (NSString *string in jailbrokenPaths)! if ([[NSFileManager defaultManager] " " " " "fileExistsAtPath:string]) {! …! }!
  19. ! ! NSArray *processes = [self runningProcesses];! ! for (NSDictionary

    * dict in processes) {! NSString *process = dict[@"ProcessName"];! if ([process isEqualToString:@"MobileCydia"]) " " "{! ...! }! !