Slide 1

Slide 1 text

Reverse Engineering iOS apps UIKonf 2014 Max Bazaliy

Slide 2

Slide 2 text

@CocoaHeadsUA iSecurityKit @mbazaliy github.com/mbazaliy

Slide 3

Slide 3 text

Security audit Competitor analysis Solution advantages Why?

Slide 4

Slide 4 text

It’s fun!

Slide 5

Slide 5 text

Analysis

Slide 6

Slide 6 text

Traffic sniffing Module call tracing I/O activity System Code Disasm\ Decompiling Debugging Resource reversing

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Compressed pngcrush appcrush.rb artwork extractor Image f iles

Slide 10

Slide 10 text

NIBs Storyboards nib dec nib_patch Interface files

Slide 11

Slide 11 text

*.mom momdec CoreData

Slide 12

Slide 12 text

Binary

Slide 13

Slide 13 text

otool class-dump MachOView Hopper cycript Reveal Tools

Slide 14

Slide 14 text

Mach-O binary

Slide 15

Slide 15 text

32 bit (ARMv6,ARMv7) 0xFEEDFACE 64 bit (ARM64) 0xFEEDFACF Universal binaries (FAT) 0xCAFEBABE Mach-O header

Slide 16

Slide 16 text

__TEXT -> code and read only data __objc sections-> data used by runtime

Slide 17

Slide 17 text

__message_refs __cls_refs __symbols __module_info __class __meta_class __instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth

Slide 18

Slide 18 text

__message_refs __cls_refs __symbols __module_info __class __meta_class __instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth

Slide 19

Slide 19 text

class-dump-z

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Binary is encrypted

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

evasi0n.com

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Rasticrac Clutch dumpdecrypted

Slide 27

Slide 27 text

Binary analysis Debugger attach ASLR bypass Binary dump Patch cryptid Clutch Rasticrac

Slide 28

Slide 28 text

Binary analysis

Slide 29

Slide 29 text

Disassembler Debugger Decompiler Hopper IDA Disassembler Debugger + objc_helper + Hex-Rays

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

id objc_msgSend(id self, SEL op, ...) 80% of calls

Slide 32

Slide 32 text

application: didFinishLaunchingWithOptions: Hopper Disassembler

Slide 33

Slide 33 text

Control flow graph Hopper Disassembler

Slide 34

Slide 34 text

Decompilation Hopper Disassembler

Slide 35

Slide 35 text

! Method names Strings Constants

Slide 36

Slide 36 text

Dump headers Modify ivars Instantiate objects Invoking methods Swizzling methods cycript

Slide 37

Slide 37 text

cy# UIApp @"" 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 @"" cy# UIApp.keyWindow.subviews[0].nextResponder.topViewController. viewControllers[0] @"" cy# JailbreakDetectionVC.messages['isJailbroken'] = function () { return NO }; {} cy# [[[UIView alloc] init] autorelease] @">"

Slide 38

Slide 38 text

Runtime inspection Modify layer Dynamically loaded Reveal

Slide 39

Slide 39 text

Foursquare.app

Slide 40

Slide 40 text

idb iNalyzer Snoop-it Introspy iRET Special tools

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Best practices Compile with PIE No credentials in plists Disable NSLog Use NSFileProtection

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

No Objective-C Integrity checks SSL pinning Obfuscation What next ?

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

Public key Certificate SSL pinning

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Use functions Strip symbols Use #define inline ((always_inline)) Method obfuscation

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

XORs Decoding tables Don’t use one key Strings obfuscation

Slide 52

Slide 52 text

! ! #define PTRACE_STRING @"

Slide 53

Slide 53 text

Deny attach Constructor tricks Change values Anti debugger tricks

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

SYSCALL

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

+ (PurchaseManager *)sharedManager {! ! if (isDebugged())! return nil;! ! static PurchaseManager *sharedPurchaseManager = nil;! static dispatch_once_t onceToken;! " dispatch_once(&onceToken, ^{! sharedPurchaseManager = [[self alloc] init];! });! "! return sharedPurchaseManager ;! }!

Slide 59

Slide 59 text

Is encrypted Is patched SC_Info iTunesMetadata overdrive tricks Integrity checks

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Terminate app Run in demo mode Change behavior What next?

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Path check URL check File access Root check Process check Jailbreak detection

Slide 71

Slide 71 text

! NSError *error;! NSString *jailTest = @"Jailbreak time!";! [jailTest writeToFile:@"/private/ test_jail.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];! if(error==nil) {! …! }!

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

iMAS Encrypted Core Data Security checks Passcode check Memory security

Slide 77

Slide 77 text

LLVM Obfuscator Instructions substitution Control Flow flattening Bogus Control Flow Functions merging

Slide 78

Slide 78 text

LLVM Obfuscator Instructions substitution Control Flow flattening Bogus Control Flow Functions merging

Slide 79

Slide 79 text

Cracking time = Protection time

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

@mbazaliy