reverse engineering ios apps
Post on 11-Sep-2014
6.143 Views
Preview:
DESCRIPTION
TRANSCRIPT
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
top related