Reverse Engineering iOS apps

Download Reverse Engineering iOS apps

Post on 11-Sep-2014

6.095 views

Category:

Technology

9 download

Embed Size (px)

DESCRIPTION

UAMobile 2013 talk

TRANSCRIPT

<ul><li><p>Reverse Engineering </p><p>iOS apps </p></li><li><p> Mobile lead at RnR XP practices follower CocoaHeads UA founder </p><p>Max Bazaliy </p></li><li><p> Security audit Competitor analysis Solution advantages FUN ! </p><p>Why? </p></li><li><p>Analysis </p></li><li><p> Traffic sniffing Module call tracing I/O activity </p><p>External </p></li><li><p> SSL proxying Repeat\Edit request Breakpoints Bandwidth throttle </p><p>Charles </p></li><li><p> Disassembling Decompiling Debugging Resource reversing </p><p>Internal </p></li><li><p>Binary file Image files Interface files Property list files CoreData model files </p></li><li><p> Compressed =&gt; pngcrush =&gt; appcrush.rb =&gt; artwork extractor </p><p>Image files </p></li><li><p> NIBs Storyboards =&gt; nib dec =&gt; nib_patch </p><p>Interface files </p></li><li><p> mom =&gt; momdec CoreData Models </p></li><li><p>Binary </p></li><li><p> otool \ otx class-dump MachOView Hopper \ IDA Cycript </p><p>Tools </p></li><li><p>Mach-O binary </p><p>Section 1 data </p><p>Section 2 data </p><p>Section 3 data </p><p>Section 4 data </p><p>Section 5 data </p><p>Section n data S</p><p>egm</p><p>ent 1</p><p> S</p><p>egm</p><p>ent 2</p><p>Segment command 1 Segment command 2 </p></li><li><p> 0xFEEDFACE 0xFEEDFACF 0xCAFEBABE </p><p>Mach-O header </p></li><li><p>__TEXT -&gt; code and read only data </p><p>__objc sections-&gt; data used by runtime </p></li><li><p>__message_refs __cls_refs __symbols __module_info __class __meta_class </p><p>__instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth </p></li><li><p>__message_refs __cls_refs __symbols __module_info __class __meta_class </p><p>__instance_vars __inst_meth __cls_meth __cat_cls_meth __protocol_ext __cat_inst_meth </p></li><li><p>@interface RRSubscription : NSObject!{! NSString *_subscriptionID;!</p><p>!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; !</p></li><li><p>FairPlay </p><p>AES MD5 </p></li><li><p>otool -arch all Vl MyApp | grep -A5 LC_ENCRYP!</p></li><li><p>&gt; address (cryptoff + cryptsize) size (base address + cryptoff + cryptsize)!</p><p>&gt; gdb dump memory decrypted.bin 0x3000 0xD23000 !</p><p>&gt; Address space layout randomization!</p><p>&gt; 0x1000 -&gt; 0x4f000!</p><p>&gt; decrypted.bin -&gt; binary!</p></li><li><p>Rasticrac </p><p>Clutch </p><p>Crackulous </p></li><li><p>Binary analysis </p></li><li><p>Disassembler Debugger Decompiler </p><p>Hopper </p><p>IDA Disassembler Debugger + objc_helper </p></li><li><p>Disassembler Debugger Decompiler </p><p>Hopper </p><p>IDA Disassembler Debugger + objc_helper + decompiler </p></li><li><p>Hopper </p></li><li><p>id objc_msgSend(id self, SEL op, ...) </p></li><li><p>application_didFinishLaunchingWithOptions </p></li><li><p>Control flow graph </p></li><li><p>asm -&gt; pseudocode </p></li><li><p>! Method names Strings Constants 3rd party </p></li><li><p> Works at runtime Modify ivars Instantiate objects </p><p> Invoking methods Swizzling methods </p><p>Cycript </p></li><li><p>But </p></li><li><p> No Objective-C Integrity checks SSL pinning Obfuscation </p><p>What next ? </p></li><li><p> Public key Certificate </p><p>SSL pinning </p></li><li><p>- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {!</p><p>!NSData *remoteCertificateData = </p><p>CFBridgingRelease(SecCertificateCopyData(certificate));!NSString *cerPath = [[NSBundle mainBundle] </p><p>pathForResource:@"MyLocalCertificate" ofType:@"cer"];!NSData *localCertData = [NSData dataWithContentsOfFile:cerPath];!</p><p>if ([remoteCertificateData isEqualToData:localCertData]) {!</p><p>[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];!</p><p>} else {![[challenge sender] </p><p>cancelAuthenticationChallenge:challenge];!}!</p></li><li><p>#define _AFNETWORKING_PIN_SSL_CERTIFICATES_ 1 </p><p>!AFHTTPClient.h!@property (nonatomic, assign) </p><p>AFURLConnectionOperationSSLPinningMode sslPinningMode; </p><p>{ AFSSLPinningModePublicKey, AFSSLPinningModeCertificate } </p><p>AFURLConnectionOperation.h </p><p>When `defaultSSLPinningMode` is defined on `AFHTTPClient` and the Security framework is linked, connections will be validated on all matching certificates with a `.cer` extension in the bundle root.!</p></li><li><p> Use functions Strip symbols Use #define inline </p><p> ((always_inline)) </p><p>Method obfuscation </p></li><li><p>#define isEncrypted() bxtlrz()!static inline BOOL bxtlrz() {!!}!</p></li><li><p> XORs Encoding keys Encoding table New key for app </p><p> Use hash </p><p>Strings obfuscation </p></li><li>#define PTRACE_STRING_ENCODED @"</li><li><p>#define verifyDecodedString(encoded, hashE, hashD, success) </p><p>fweybz(encoded, hashE, hashD, success) </p><p>static inline NSString * fweybz(NSString *encoded, NSString *hashE, </p><p> NSString *hashD, BOOL *success) { </p><p> NSString *decoded = decodedString(encoded); </p><p> if (success != NULL) { </p><p> *success = (decoded &amp;&amp; [hashFromString(encoded) isEqualToString:hashEncoded] &amp;&amp; [hashFromString(decoded) isEqualToString:hashDecoded]) ? YES : NO; return decoded; </p><p>} </p></li><li><p> Deny attach Constructor -&gt; nil Change values </p><p>Anti debugger </p><p>tricks </p></li><li><p>#define denyDebugger() tmzpw()!static __inline__ void tmzpw() {!if (getuid() != 0) {!</p><p>!NSString *ptraceString = .. !!void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);!</p><p> ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(handle, ptraceString);!</p><p> ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);! dlclose(handle);!}! else! *(volatile int *)NULL = 0xDEADBEEF;!}!</p></li><li><p>ASSEMBLER </p></li><li><p>mov r0, #31!mov r1, #0!mov r2, #0!mov r3, #0!mov ip, #26!svc #0x80!</p></li><li><p>int main(int argc, char *argv[])!{! @autoreleasepool {!</p><p> denyDebugger();! return UIApplicationMain(argc, argv, nil, nil));! }!}!</p></li><li><p>+ (PurchaseManager *)sharedManager {!if (isDebugged())!</p><p>!return nil;!static PurchaseManager *sharedPurchaseManager = nil; !static dispatch_once_t onceToken;! !dispatch_once(&amp;onceToken, ^{ !</p><p>! ! !sharedPurchaseManager = [[self alloc] init];! });!</p><p>!return sharedPurchaseManager ; !}!</p></li><li><p> Is encrypted SC_Info dir iTunesMetadata </p><p> .dylib files </p><p>Integrity checks </p></li><li><p>const struct mach_header *header = (struct mach_header *)dlinfo.dli_fbase; </p><p> struct load_command *cmd = (struct load_command *) (header + 1); </p><p> for (uint32_t i = 0; cmd != NULL &amp;&amp; i &lt; header-&gt;ncmds; i++) { </p><p> if (cmd-&gt;cmd == LC_ENCRYPTION_INFO) { </p><p> struct encryption_info_command *crypt_cmd = (struct encryption_info_command *)cmd; </p><p> if (crypt_cmd-&gt;cryptid &lt; 1) </p><p> return NO; </p><p> else </p><p> return YES; </p><p> } </p><p> else </p><p> cmd = (struct load_command *)((uint8_t *) cmd + cmd-&gt;cmdsize); </p><p> } </p><p> return NO; </p></li><li><p> BOOL isDirectory = NO; NSString *directoryPath = [[[NSBundle mainBundle] </p><p>bundlePath] stringByAppendingPathComponent:@SC_Info/]; </p><p> BOOL directoryExists = [[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&amp;isDirectory]; </p><p> BOOL contentSeemsValid = ([[[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:NULL] count] == 2); </p></li><li><p> !NSDictionary *iTunesMetadata = [NSDictionary !dictionaryWithContentsOfFile:[rootDirectoryPath !stringByAppendingPathComponent:@ iTunesMetadata.plist]];!</p><p>!NSString *appleID = iTunesMetadata[appleID];! NSDictionary *accountInfo = </p><p>iTunesMetadata[downloadInfoKey][accountInfo];!!BOOL isValidAppleID = (appleID.length &gt; 0 &amp;&amp; ![appleID rangeOfString:appleIDMailAddress !options:NSCaseInsensitiveSearch].location == !NSNotFound);!</p><p> BOOL isValidDownloadInfo = (accountInfo.count &gt; 0);!</p></li><li><p> BOOL dyLibFound = NO; NSArray *directoryFiles = [[NSFileManager </p><p>defaultManager] contentsOfDirectoryAtPath:[[NSBundle mainBundle] bundlePath] error:NULL]; </p><p> for (NSString *filename in directoryFiles) { if ([[filename pathExtension] </p><p>caseInsensitiveCompare:@dylib] == NSOrderedSame) { </p><p> dyLibFound = YES; break; } }!</p></li><li><p> Terminate app Run in demo mode Change behavior </p><p>What next? </p></li><li><p> Path check File access Root check Process name System files </p><p>Jailbreak detection </p></li><li><p>!NSError *error; !NSString *jailTest = @Jailbreak time!";![jailTest writeToFile:@"/private/test_jail.txt" </p><p>atomically:YES encoding:NSUTF8StringEncoding error:&amp;error];!</p><p>if(error==nil) {!!}!!</p></li><li><p> if (getuid() == 0) {!! }!!! if (system(0)) {!...! }!</p></li><li><p> 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!</p><p>! ! ! ! ! ! ! !@"/usr/libexec/sftp-server",! ! ! !@"/Applications/blackra1n.app",! ! ! !@"/private/var/stash"];!! NSString *rooted;! for (NSString *string in jailbrokenPath)! if ([[NSFileManager defaultManager] fileExistsAtPath:string]) {!!}!!</p></li><li><p>!! for (NSDictionary * dict in processes) {!!NSString *process = [dict objectForKey:@"ProcessName"];!!! !if ([process isEqualToString:CYDIA]) {!!! ! ! !...!!! ! ! !}!</p><p>}!!</p></li><li><p> struct stat sb;! stat("/etc/fstab", &amp;sb);! long long size = sb.st_size;! if (size == 80) {!!! ! ! !return NOTJAIL;!</p><p> } else!!! ! ! !return JAIL;!</p><p> }!</p></li><li><p>Cracking time = </p><p>Protection time </p></li><li><p>@mbazaliy </p></li></ul>