Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Reverse Engineering iOS apps. Lessons learned.
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Max Bazaliy
May 15, 2014
Programming
2.9k
21
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Reverse Engineering iOS apps. Lessons learned.
UIKonf, Berlin @ 2014
Max Bazaliy
May 15, 2014
More Decks by Max Bazaliy
See All by Max Bazaliy
Jailbreaking Apple Watch
mbazaliy
3
4.6k
Jailbreaking Apple Watch
mbazaliy
3
27k
Fried Apples: Jailbreak DIY
mbazaliy
1
3.1k
Pegasus Internals
mbazaliy
4
3.9k
Mobile Espionage in the Wild: Pegasus and Nation-State Level Attacks
mbazaliy
1
670
FRAPL - Next Generation Reverse Engineering Framework
mbazaliy
3
520
A Journey Through Exploit Mitigation Techniques on iOS
mbazaliy
2
2.3k
Securing iOS applications
mbazaliy
8
1.8k
Other Decks in Programming
See All in Programming
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
110
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
250
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
200
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
350
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
530
Workers Cache を知る
syumai
0
350
golang-bob.pdf
iwatsukayura
0
120
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
690
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.5k
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
310
Compiling a real-world CLI tool to native with Static Hermes
tmikov2023
0
100
難しいけど、読めた。- OSSの入口に立った話。
sts11142
0
150
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
310
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.9k
Tell your own story through comics
letsgokoyo
1
1.1k
Done Done
chrislema
187
17k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Are puppies a ranking factor?
jonoalderson
2
4k
Faster Mobile Websites
deanohume
310
32k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.1k
The Curse of the Amulet
leimatthew05
3
15k
Transcript
Reverse Engineering iOS apps UIKonf 2014 Max Bazaliy
@CocoaHeadsUA iSecurityKit @mbazaliy github.com/mbazaliy
Security audit Competitor analysis Solution advantages Why?
It’s fun!
Analysis
Traffic sniffing Module call tracing I/O activity System Code Disasm\
Decompiling Debugging Resource reversing
None
Binary file Image files Interface files Property list files CoreData
model files App files
Compressed pngcrush appcrush.rb artwork extractor Image f iles
NIBs Storyboards nib dec nib_patch Interface files
*.mom momdec CoreData
Binary
otool class-dump MachOView Hopper cycript Reveal Tools
Mach-O binary
32 bit (ARMv6,ARMv7) 0xFEEDFACE 64 bit (ARM64) 0xFEEDFACF Universal binaries
(FAT) 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
class-dump-z
@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;
Binary is encrypted
otool -arch all –Vl MyApp | grep -A5 LC_ENCRYP!
evasi0n.com
> 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!
None
Rasticrac Clutch dumpdecrypted
Binary analysis Debugger attach ASLR bypass Binary dump Patch cryptid
Clutch Rasticrac
Binary analysis
Disassembler Debugger Decompiler Hopper IDA Disassembler Debugger + objc_helper +
Hex-Rays
None
id objc_msgSend(id self, SEL op, ...) 80% of calls
application: didFinishLaunchingWithOptions: Hopper Disassembler
Control flow graph Hopper Disassembler
Decompilation Hopper Disassembler
! Method names Strings Constants
Dump headers Modify ivars Instantiate objects Invoking methods Swizzling methods
cycript
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>>"
Runtime inspection Modify layer Dynamically loaded Reveal
Foursquare.app
idb iNalyzer Snoop-it Introspy iRET Special tools
None
Best practices Compile with PIE No credentials in plists Disable
NSLog Use NSFileProtection
Best practices Sensitive - keychain View snapshots Cache.db URL Schemes
Secure coding guide
No Objective-C Integrity checks SSL pinning Obfuscation What next ?
None
Public key Certificate SSL pinning
- (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);! ""}!
- (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) {! }];! }!
Use functions Strip symbols Use #define inline ((always_inline)) Method obfuscation
#define isJailbroken() gbrlp()! static inline int () gbrlp{! …! }!
XORs Decoding tables Don’t use one key Strings obfuscation
! ! #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];! !
Deny attach Constructor tricks Change values Anti debugger tricks
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;! }!
#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);! !
SYSCALL
syscall(26, 31, 0, 0, 0); ! ptrace PT_DENY_ATTACH
+ (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 Is patched SC_Info iTunesMetadata overdrive tricks Integrity checks
None
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;! }!
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;!
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);
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);}!
None
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;! }! }!
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);! }!
Terminate app Run in demo mode Change behavior What next?
None
Path check URL check File access Root check Process check
Jailbreak detection
! NSError *error;! NSString *jailTest = @"Jailbreak time!";! [jailTest writeToFile:@"/private/
test_jail.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];! if(error==nil) {! …! }!
int result = fork();! "if (!result)! exit(0); ! if (result
>= 0)! return isJail;! return noJail;! ! ! if (system(0)) ! ...! }!
NSURL *FakeURL = [NSURL URLWithString:! @"cydia://package/com.fake.package"];! ! if ([[UIApplication sharedApplication]
canOpenURL:FakeURL])! return isJail;! else! return noJail;
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]) {! …! }!
! ! NSArray *processes = [self runningProcesses];! ! for (NSDictionary
* dict in processes) {! NSString *process = dict[@"ProcessName"];! if ([process isEqualToString:@"MobileCydia"]) " " "{! ...! }! !
iMAS Encrypted Core Data Security checks Passcode check Memory security
LLVM Obfuscator Instructions substitution Control Flow flattening Bogus Control Flow
Functions merging
LLVM Obfuscator Instructions substitution Control Flow flattening Bogus Control Flow
Functions merging
Cracking time = Protection time
None
@mbazaliy