Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Reverse Engineering iOS apps. Lessons learned.
Search
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.5k
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
650
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.7k
Other Decks in Programming
See All in Programming
えっ!!コードを読まずに開発を!?
hananouchi
0
130
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
500
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
150
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
200
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
220
1B+ /day規模のログを管理する技術
broadleaf
0
130
Welcome to the "Parametricity" 🏙️ − Generic だけど Specific な世界 −
guvalif
1
130
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
160
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
270
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1k
任せる範囲はこう広がった / How the Scope of AI Delegation Has Expanded
nrslib
1
240
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
370
Featured
See All Featured
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
180
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
490
HDC tutorial
michielstock
2
730
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Prompt Engineering for Job Search
mfonobong
0
370
Google's AI Overviews - The New Search
badams
0
1.1k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
4 Signs Your Business is Dying
shpigford
187
22k
Optimising Largest Contentful Paint
csswizardry
37
3.8k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
180
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
240
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