Transcript
Page 1: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Reverse Engineering iOS apps

Max Bazaliy tech hangout

Page 2: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

@CocoaHeadsUA iSecurityKit

@mbazaliy github.com/mbazaliy

Page 3: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Security audit Competitor analysis Solution advantages

Why?

Page 4: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

It’s fun!

Page 5: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Analysis

Page 6: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Traffic sniffing Module call tracing I/O activity

System

Code Disasm\ Decompiling Debugging Resource reversing

Page 7: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 8: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Binary file Image files Interface files Property list files CoreData model files

App files

Page 9: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Compressed pngcrush

appcrush.rb artwork extractor

Image f iles

Page 10: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

NIBs Storyboards nib dec

nib_patch

Interface files

Page 11: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

*.mom momdec CoreData

Page 12: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Binary

Page 13: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

otool class-dump MachOView Hopper cycript Reveal

Tools

Page 14: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Mach-O binary

Page 15: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

32 bit (ARMv6,ARMv7)

0xFEEDFACE

64 bit (ARM64)

0xFEEDFACF

Universal binaries (FAT)

0xCAFEBABE

Mach-O header

Page 16: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

__TEXT -> code and read only data

__objc sections-> data used by runtime

Page 17: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

__message_refs __cls_refs __symbols __module_info __class __meta_class

__instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth

Page 18: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

__message_refs __cls_refs __symbols __module_info __class __meta_class

__instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth

Page 19: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

class-dump

Page 20: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

@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;  

Page 21: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Binary is encrypted

Page 22: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

otool -arch all –Vl MyApp | grep -A5 LC_ENCRYP!

Page 23: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

evasi0n.com

Page 24: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 25: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

> 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!

Page 26: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 27: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Rasticrac

Clutch

dumpdecrypted

Page 28: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Binary analysis Debugger attach ASLR bypass Binary dump Patch cryptid

Clutch Rasticrac

Page 29: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Binary analysis

Page 30: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Disassembler Debugger Decompiler

Hopper

IDA Disassembler Debugger + objc_helper + Hex-Rays

Page 31: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 32: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

id objc_msgSend(id self, SEL op, ...)

80% of calls

Page 33: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

application: didFinishLaunchingWithOptions:

Hopper Disassembler

Page 34: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Control flow graph

Hopper Disassembler

Page 35: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Decompilation

Hopper Disassembler

Page 36: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

! Method names Strings Constants

Page 37: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Dump headers Modify ivars Instantiate objects

Invoking methods Swizzling methods

cycript

Page 38: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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>>"

Page 39: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Runtime inspection Modify layer Dynamically loaded

Reveal

Page 40: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Foursquare.app

Page 41: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

idb iNalyzer Snoop-it Introspy iRET

Special tools

Page 42: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 43: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Best practices

Compile with PIE No credentials in plists Disable NSLog Use NSFileProtection

Page 44: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Best practices

Sensitive - keychain View snapshots Cache.db URL Schemes Secure coding guide

Page 45: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

No Objective-C Integrity checks SSL pinning Obfuscation

What next ?

Page 46: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 47: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Public key Certificate

SSL pinning

Page 48: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

- (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); !

""} !

Page 49: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

- (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) { !

}]; !

}!

Page 50: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Use functions Strip symbols Use #define inline

((always_inline))

Method obfuscation

Page 51: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

#define isJailbroken() gbrlp() !static inline int () gbrlp{ !… !} !

Page 52: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

XORs Decoding tables Don’t use one key

Strings obfuscation

Page 53: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

!

!

!

!

!

#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]; !

!!

!

!

Page 54: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Deny attach Constructor tricks Change values

Anti debugger

tricks

Page 55: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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; !

} !

Page 56: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

#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); !

!

Page 57: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

SYSCALL

Page 58: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

syscall(26, 31, 0, 0, 0);!

ptrace

PT_DENY_ATTACH

Page 59: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

+ (PurchaseManager *)sharedManager { !

!

if (isDebugged()) !

return nil; !

!

static PurchaseManager *sharedPurchaseManager = nil; !

static dispatch_once_t onceToken; !

" dispatch_once(&onceToken, ^{ !

sharedPurchaseManager = [[self alloc] init]; !

}); !

"!

return sharedPurchaseManager ; !

}!

Page 60: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Is encrypted Is patched SC_Info iTunesMetadata

overdrive tricks

Integrity checks

Page 61: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 62: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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; !

} !

Page 63: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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; !

Page 64: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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);

Page 65: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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);}!

Page 66: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 67: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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; ! } ! }!

Page 68: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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); !

} !

Page 69: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Terminate app Run in demo mode Change behavior

What next?

Page 70: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 71: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Path check URL check File access Root check Process check

Jailbreak detection

Page 72: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

NSError *error; ! NSString *jailTest = @"Jailbreak time!"; ! ! BOOL success = [jailTest writeToFile: !"@"/private/test_jail.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error]; !

! if (success) { ! …! }!

Page 73: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

int result = fork(); !"if (!result) ! exit(0); ! if (result >= 0) ! return isJail; ! return noJail; !

! ! if (system(0)) ! ...! }!

Page 74: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

NSURL *FakeURL = [NSURL URLWithString: !@"cydia://package/com.fake.package"]; !! if ([[UIApplication sharedApplication] canOpenURL:FakeURL]) !

return isJail; ! else! return noJail;

Page 75: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

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]) { !

…! }!

Page 76: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

!!

NSArray *processes = [self runningProcesses]; !

! for (NSDictionary * dict in processes) { !

NSString *process = dict[@"ProcessName"]; !

if ([process isEqualToString:@"MobileCydia"]) " " "{ !

...!

} !!

Page 77: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

iMAS

Encrypted Core Data Security checks

Passcode check

Memory security

Page 78: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

LLVM Obfuscator

Instructions substitution Control Flow flattening Bogus Control Flow Functions merging

Page 79: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

LLVM Obfuscator

Instructions substitution Control Flow flattening Bogus Control Flow Functions merging

Page 80: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

Cracking time =

Protection time

Page 81: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19
Page 82: Reverse Engineering iOS Apps (by Max Bazaliy) - Mobile Tech Hangout - 2014.07.19

@mbazaliy