├── README.md ├── classdumper.py ├── fridaHookSkeleton.js └── tlsPinningBypass.js /README.md: -------------------------------------------------------------------------------- 1 | # friOS - iOS Frida Scripts 2 | 3 | This repository contains frida scripts for iOS: 4 | * classdumper.py - dumps binary specific classes and methods 5 | * fridaHookSkeleton.js - allows one to hook multiple methods using the same hook file 6 | * tlsPinningBypass.js - bypasses TLS pinning on iOS 10 and bellow 7 | -------------------------------------------------------------------------------- /classdumper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Frida script to dump the classes specific to the app binary 3 | 4 | # (c) 2017 INTEGRITY S.A. 5 | # By: Herman Duarte 6 | 7 | """ 8 | 9 | import frida 10 | from time import sleep 11 | import sys 12 | 13 | script_test = ''' 14 | 'use strict'; 15 | 16 | rpc.exports = { 17 | classes: function () { 18 | if (ObjC.available) { return ObjC.classes; } 19 | }, 20 | getClassOwnMethods: function (className) { 21 | return ObjC.classes[className].$ownMethods; 22 | } 23 | }; 24 | ''' 25 | 26 | #appToLaunch = 'com.example.ios.app' 27 | appToLaunch = None 28 | 29 | if appToLaunch == None: 30 | if len(sys.argv) > 1: 31 | appToLaunch = sys.argv[1] 32 | print(appToLaunch) 33 | else: 34 | print("usage: python3 " + sys.argv[0] + " ") 35 | print(" python3 " + sys.argv[0] + " com.example.ios.app") 36 | sys.exit (0) 37 | 38 | device = session = pid = None 39 | 40 | try: 41 | device = frida.get_usb_device() 42 | pid = device.spawn([appToLaunch]) 43 | sleep(1) 44 | 45 | session = device.attach(pid) 46 | device.resume(pid) 47 | 48 | # selecting only the app binary to inspect 49 | appModule = session.enumerate_modules().pop(0) 50 | appModuleRanges = appModule.enumerate_ranges('---') 51 | 52 | script = session.create_script(script_test) 53 | script.load() 54 | ObjC = script.exports 55 | 56 | # getting all classes that are loaded 57 | classes = ObjC.classes() 58 | own_classes = {} 59 | count = 0 60 | 61 | print('[*] All classes of module: ' + appModule.name) 62 | for classname in classes: 63 | classptr = classes[classname]['handle'] 64 | 65 | for range in appModuleRanges: 66 | classptr_int = int(classptr, 16) 67 | 68 | if classptr_int >= range.base_address and classptr_int <= (range.base_address + range.size): 69 | print(' [*] ' + classname) 70 | methods = ObjC.get_class_own_methods(classname) 71 | for method in methods: 72 | print(' [*] ' + method) 73 | 74 | count += 1 75 | 76 | sleep(1) 77 | print('Total classes found: ' + str(len(classes))) 78 | print(appModule.name + ' specific classes found: ' + str(count)) 79 | 80 | script.unload() 81 | device.kill(pid) 82 | 83 | except: 84 | print("Device is not connected, check your USB connection") 85 | raise 86 | 87 | 88 | """ 89 | ToDo: 90 | - find the params types and print the methods full signature 91 | - 92 | """ -------------------------------------------------------------------------------- /fridaHookSkeleton.js: -------------------------------------------------------------------------------- 1 | /* 2 | * iOS Frida Hooking Skeleton Script 3 | * 4 | * (c) 2017 INTEGRITY S.A. 5 | * By: Herman Duarte 6 | */ 7 | 8 | // lets check if the env is available 9 | if (ObjC.available) 10 | { 11 | // class or classes that we want to hook methods 12 | var classes = ['']; 13 | // methods we want to hook, at least from one of the classes above 14 | var methodsWhiteList = ['']; 15 | 16 | for (var className in ObjC.classes) 17 | { 18 | if (ObjC.classes.hasOwnProperty(className)) 19 | { 20 | if (classes.indexOf(className) > -1) 21 | { 22 | console.log('[*] Start: Hooking into "' + className + '" methods'); 23 | 24 | var methods = ObjC.classes[className].$ownMethods; 25 | for (var i = 0; i < methods.length; i++) 26 | { 27 | // if the method is in the whitelist then we can intercepted it 28 | if (methodsWhiteList.indexOf(methods[i]) > -1) 29 | { 30 | try 31 | { 32 | var _className = "" + className; 33 | var _methodName = "" + methods[i]; 34 | 35 | var method = ObjC.classes[_className][_methodName]; 36 | 37 | console.log('Hooking: ' + _methodName); 38 | 39 | Interceptor.attach(method.implementation, { 40 | onEnter: function (args) { 41 | 42 | this._className = ObjC.Object(args[0]).toString(); 43 | this._methodName = ObjC.selectorAsString(args[1]); 44 | 45 | console.log("\n[*] Detected call to: " + this._className + " -> " + this._methodName); 46 | 47 | if (this._methodName == '') 48 | { 49 | console.log(" [*] param1: " + (new ObjC.Object(args[2])).toString()); 50 | console.log(" [*] param2': " + (new ObjC.Object(args[3])).toString()); 51 | 52 | //console.log('\tBacktrace:\n\t' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t')); 53 | } 54 | }, 55 | onLeave: function (retval) { 56 | if (this._methodName == '' ) 57 | { 58 | var returnvalue = new ObjC.Object(retval); 59 | console.log(" [*] Return value: " + returnvalue.toString()); 60 | } 61 | } 62 | }); 63 | console.log(' [*] Hooked: ' + _methodName); 64 | } 65 | catch(error) 66 | { 67 | console.log('Hooking Falied'); 68 | } 69 | } 70 | } 71 | console.log('[*] Completed: Hooking into "' + className + '" methods'); 72 | } 73 | } 74 | } 75 | 76 | } 77 | else 78 | { 79 | console.log('Objective-C Runtime is not available!'); 80 | } 81 | 82 | /* 83 | * ToDo: 84 | * - obtain the methods param automatically 85 | * - create onenter and onleave skeletons to print the params 86 | */ 87 | -------------------------------------------------------------------------------- /tlsPinningBypass.js: -------------------------------------------------------------------------------- 1 | /* 2 | * iOS Frida TLS pinning bypass 3 | * 4 | * (c) 2017 INTEGRITY S.A. 5 | * By: Herman Duarte 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var kSSLSessionOptionBreakOnServerAuth = 0; 11 | var errSecSuccess = 0; 12 | var noErr = 0; 13 | var errSSLServerAuthCompleted = -9841; 14 | 15 | 16 | function disablePinning() 17 | { 18 | console.log('Disabling Pinning'); 19 | internalDisablePinningIOS10(); 20 | internalDisablePinningIOS9(); 21 | console.log('... done'); 22 | } 23 | 24 | function enablePinning() 25 | { 26 | var tls_helper_create_peer_trust = Module.findExportByName('libcoretls_cfhelpers.dylib', 'tls_helper_create_peer_trust'); 27 | if (tls_helper_create_peer_trust != null) 28 | { 29 | console.log('tls_helper_create_peer_trust PTR: ' + tls_helper_create_peer_trust); 30 | Interceptor.revert(tls_helper_create_peer_trust); 31 | console.log('tls_helper_create_peer_trust restored'); 32 | } 33 | 34 | var SSLSetSessionOption = Module.findExportByName('Security', 'SSLSetSessionOption'); 35 | if (SSLSetSessionOption != null) 36 | { 37 | console.log('SSLSetSessionOption PTR: ' + SSLSetSessionOption); 38 | Interceptor.revert(SSLSetSessionOption); 39 | console.log('SSLSetSessionOption restored'); 40 | } 41 | 42 | var SSLCreateContext = Module.findExportByName('Security', 'SSLCreateContext'); 43 | if (SSLCreateContext != null) 44 | { 45 | console.log('SSLCreateContext PTR: ' + SSLCreateContext); 46 | Interceptor.revert(SSLCreateContext); 47 | console.log('SSLCreateContext restored'); 48 | } 49 | 50 | var SSLHandshake = Module.findExportByName('Security', 'SSLHandshake'); 51 | if (SSLHandshake != null) 52 | { 53 | console.log('SSLHandshake PTR: ' + SSLHandshake); 54 | Interceptor.revert(SSLHandshake); 55 | console.log('SSLHandshake restored'); 56 | } 57 | 58 | } 59 | 60 | 61 | function internalDisablePinningIOS10() 62 | { 63 | // Frida hooks for iOS 10 64 | 65 | // OSStatus tls_helper_create_peer_trust(tls_handshake_t hdsk, bool server, SecTrustRef *trustRef); 66 | var tls_helper_create_peer_trust = Module.findExportByName('libcoretls_cfhelpers.dylib', 'tls_helper_create_peer_trust'); 67 | 68 | //console.log('tls_helper_create_peer_trust PTR: ' + tls_helper_create_peer_trust); 69 | 70 | if (tls_helper_create_peer_trust != null) 71 | { 72 | console.log('Found tls_helper_create_peer_trust'); 73 | Interceptor.replace(tls_helper_create_peer_trust, new NativeCallback(function (hdsk, server, trustRef) 74 | { 75 | return errSecSuccess; 76 | }, 'pointer', ['pointer', 'int', 'pointer'])); 77 | } 78 | } 79 | 80 | function internalDisablePinningIOS9() 81 | { 82 | // Frida hooks for iOS 9 and below 83 | 84 | // https://developer.apple.com/reference/security/1399173-sslsetsessionoption?language=objc 85 | // OSStatus SSLSetSessionOption(SSLContextRef context, SSLSessionOption option, Boolean value) 86 | var SSLSetSessionOptionPtr = Module.findExportByName('Security', 'SSLSetSessionOption'); 87 | var SSLSetSessionOption = new NativeFunction(SSLSetSessionOptionPtr, 'pointer', ['pointer', 'int', 'int']); 88 | 89 | if (SSLSetSessionOptionPtr != null) 90 | { 91 | console.log('Found SSLSetSessionOption'); 92 | Interceptor.replace(SSLSetSessionOptionPtr, new NativeCallback(function (context, option, value) 93 | { 94 | console.log('Inside SSLSetSessionOption'); 95 | if (option == kSSLSessionOptionBreakOnServerAuth) 96 | { 97 | console.log('option == kSSLSessionOptionBreakOnServerAuth'); 98 | return noErr; 99 | } 100 | return SSLSetSessionOption(context, option, value); 101 | }, 'pointer', ['pointer', 'int', 'int'])); 102 | } 103 | 104 | // https://developer.apple.com/reference/security/1393063-sslcreatecontext?language=objc 105 | // SSLContextRef SSLCreateContext(CFAllocatorRef alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType) 106 | var SSLCreateContextPtr = Module.findExportByName('Security', 'SSLCreateContext'); 107 | var SSLCreateContext = new NativeFunction(SSLCreateContextPtr, 'pointer', ['pointer', 'int', 'int']); 108 | 109 | if (SSLCreateContextPtr != null) 110 | { 111 | console.log('Found SSLCreateContext'); 112 | Interceptor.replace(SSLCreateContextPtr, new NativeCallback(function (alloc, protocolSide, connectionType) 113 | { 114 | console.log('Inside SSLCreateContext'); 115 | var sslContext = SSLCreateContext(alloc, protocolSide, connectionType); 116 | // Immediately set the kSSLSessionOptionBreakOnServerAuth option in order to disable cert validation 117 | SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, 1); 118 | return sslContext; 119 | 120 | }, 'pointer', ['pointer', 'int', 'int'])); 121 | } 122 | 123 | // https://developer.apple.com/reference/security/1400161-sslhandshake?language=objc 124 | // OSStatus SSLHandshake(SSLContextRef context) 125 | var SSLHandshakePtr = Module.findExportByName('Security', 'SSLHandshake'); 126 | var SSLHandshake = new NativeFunction(SSLHandshakePtr, 'int', ['pointer']); 127 | 128 | if (SSLHandshakePtr != null) 129 | { 130 | console.log('Found SSLHandshake'); 131 | Interceptor.replace(SSLHandshakePtr, new NativeCallback(function (context) 132 | { 133 | console.log('Inside SSLHandshake'); 134 | var result = SSLHandshake(context); 135 | // Hijack the flow when breaking on server authentication 136 | if (result == errSSLServerAuthCompleted) 137 | { 138 | console.log('result == errSSLServerAuthCompleted'); 139 | // Do not check the cert and call SSLHandshake() again 140 | return SSLHandshake(context); 141 | } 142 | return result; 143 | }, 'int', ['pointer'])); 144 | } 145 | } 146 | --------------------------------------------------------------------------------