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
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
660
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
Foundry Localでエージェント開発
seosoft
0
190
AIエージェント時代のコードレビューを設計する
nogu66
6
2.7k
【DroidKaigi 2026】「アクセシビリティを利用するとき、 アクセシビリティもまたこちらを利用している」 〜マルウェアによる攻撃と防衛について〜
halunoyo
0
460
[GoCon2026] When Goroutines Are Not Enough: Runtime Locality in High-Throughput Go
takehaya
6
2.1k
Building an Out-of-Order CPU
latte72
1
770
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
6
1.8k
高専キャリア LT 発表内容
crysta1221
6
5.7k
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
110
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
1
540
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
230
アクセシビリティから考える情報設計
high_g_engineer
0
370
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
140
Featured
See All Featured
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
240
Mind Mapping
helmedeiros
1
360
Color Theory Basics | Prateek | Gurzu
gurzu
0
460
WCS-LA-2024
lcolladotor
0
830
From π to Pie charts
rasagy
0
360
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
720
Code Review Best Practice
trishagee
74
20k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
480
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
530
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
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