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