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
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
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
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
150
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
380
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
2
260
継続モナドとリアクティブプログラミング
yukikurage
3
700
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
670
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
250
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
190
仕様駆動開発へのトライを機に チームに適合する手法を模索し続けている話
freee
PRO
0
460
Welcome to the "Parametricity" 🏙️ − Generic だけど Specific な世界 −
guvalif
PRO
1
210
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
310
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
140
Go 1.27 における memory allocation の高速化
andpad
0
120
Featured
See All Featured
Into the Great Unknown - MozCon
thekraken
41
2.7k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
480
Paper Plane
katiecoart
PRO
2
52k
Marketing to machines
jonoalderson
1
5.7k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
Deep Space Network (abreviated)
tonyrice
0
250
Utilizing Notion as your number one productivity tool
mfonobong
4
520
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
400
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.9k
Technical Leadership for Architectural Decision Making
baasie
3
470
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
450
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