├── .circleci └── config.yml ├── .gitignore ├── .images └── webstorm_setup_frida_autocomplete.png ├── README.md ├── c_access.js ├── c_memcmp.js ├── c_open.js ├── c_sectrustevaluate.js ├── c_substring.js ├── c_task_thread.js ├── eslint.config.mjs ├── objc_blocks.js ├── objc_cfstring.js ├── objc_deeplinks.js ├── objc_nslog.js ├── objc_nsstring_containsstring.js ├── objc_nsstring_stringwithutf8string.js ├── objc_nsurl.ts ├── objc_nsurl_and_iskindof.js ├── objc_nsurl_modify_url.js ├── objc_nsurlprotectionspace_host.js ├── objc_nsurlqueryitem.js └── package.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | node: circleci/node@5.0.2 5 | 6 | jobs: 7 | lint_and_style: 8 | executor: node/default 9 | steps: 10 | - checkout 11 | - run: npm i eslint 12 | - run: npx eslint --version 13 | - run: npx eslint . 14 | 15 | workflows: 16 | lint: 17 | jobs: 18 | - lint_and_style 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __handlers__/ 2 | .idea/ 3 | .env -------------------------------------------------------------------------------- /.images/webstorm_setup_frida_autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustymagnet3000/frida_scripts/ac50113cbfe9a9826d935b6a9fbcc97349159014/.images/webstorm_setup_frida_autocomplete.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiny Frida Scripts 2 | 3 | ## Setup 4 | 5 | With `WebStorm` - from Jetbrains - download the `@types/frida-gum` extension. This gave auto-complete. 6 | 7 | The Frida team recommend: 8 | 9 | > we highly recommend using our TypeScript bindings. 10 | 11 | A `typescript` file vs a `javascript` file is a "no brainer": 12 | 13 | - Compile time errors; speeds up debugging. 14 | - Code completion. 15 | - [Other benefits](https://learnfrida.info/basic_usage/#javascript-vs-typescript). 16 | 17 | 18 | ### Enable auto-complete 19 | ![](.images/webstorm_setup_frida_autocomplete.png) 20 | 21 | ## Tips for writing Frida Scripts 22 | 23 | ### Objective-C 24 | 25 | #### Read ObjectiveC function parameters 26 | 27 | - 0 = 'self' 28 | - 1 = The selector string 29 | - 2 = The first argument 30 | 31 | #### List Objective-C methods 32 | 33 | ```javascript 34 | ObjC.classes.NSString.$ownMethods 35 | ObjC.classes.NSURL.$ownMethods 36 | ``` 37 | 38 | #### ObjC.Object Properties 39 | 40 | ```javascript 41 | const foobar = new ObjC.Object(retval) 42 | // foobar.$className 43 | // foobar.$moduleName 44 | // foobar.$kind 45 | ``` 46 | 47 | #### API Resolver 48 | 49 | ```typescript 50 | const resolver = new ApiResolver('objc'); 51 | const matches = resolver.enumerateMatches('-[NSURL initWithString:]'); 52 | const firstPtr = matches[0].address; 53 | 54 | Interceptor.attach(firstPtr, { // code 55 | // ... 56 | // .. 57 | 58 | ``` 59 | ` 60 | #### Find C function 61 | 62 | ```javascript 63 | DebugSymbol.fromAddress(Module.findExportByName(null, 'strstr')) 64 | ``` 65 | 66 | ### Reference Counting 67 | The Frida author wrote: 68 | 69 | "Never interact with Objective-C APIs without an `autorelease-pool`."_ 70 | 71 | 72 | 73 | ##### Reading C Strings can throw errors 74 | When dealing with `C` character arrays, `Memory.readUtf8String(args[1]);` can `throw` a Javascript error. For example: 75 | 76 | `Error: can't decode byte 0xda in position 2 at /repl19.js:25`. 77 | 78 | You can use: `Memory.readCString(args[1], 20)` to avoid this. You can even limit the size of the read with an ( optional ) size value. 79 | 80 | Or you can handle the error: 81 | ``` 82 | try { 83 | this._needle = Memory.readUtf8String(args[1]); 84 | } 85 | catch(err){ 86 | nonDecoableChars++; // this._needle == Javascript's undefined type 87 | } 88 | ``` 89 | ##### `lldb` convenience arguments differ to `frida` 90 | For example: 91 | 92 | ``` 93 | -[NSString containsString:] 94 | 95 | -lldb --------------------------------- 96 | 97 | (lldb) po $arg1 98 | haystack 99 | 100 | (lldb) po (char *)$arg2 101 | "containsString:" 102 | 103 | (lldb) po $arg3 104 | needle 105 | 106 | -frida --------------------------------- 107 | Interceptor.attach(methodPointer, { 108 | onEnter: function (args) { 109 | this._needle = new ObjC.Object(args[2]); 110 | ``` 111 | 112 | ##### Persist instance variables between onEnter() and onLeave() 113 | ``` 114 | onEnter: function (args) { 115 | this._needle = new ObjC.Object(args[2]); 116 | 117 | 118 | onLeave: function (retval) { 119 | if(this._needle != '-') { 120 | // do something 121 | } 122 | ``` 123 | 124 | 125 | ##### Cheat sheets 126 | https://github.com/iddoeldor/frida-snippets -------------------------------------------------------------------------------- /c_access.js: -------------------------------------------------------------------------------- 1 | /***********************************************************************************/ 2 | // USAGE: for arm64 3 | /************************************************************************************/ 4 | 5 | var targetFunction = Module.findExportByName("libsystem_kernel.dylib", "access"); 6 | 7 | Interceptor.attach(targetFunction, { 8 | onEnter: function (args) { 9 | const path = Memory.readUtf8String(this.context.x0); 10 | console.log("[*] " + path); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /c_memcmp.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | int memcmp(const void *s1, const void *s2, size_t n); 3 | FIND EXPORT: DebugSymbol.fromAddress(Module.findExportByName(null, 'memcmp')) 4 | USAGE: frida -l c_memcmp.js -U -f appname --no-pause 5 | /************************************************************************************/ 6 | 7 | const module_name = "libsystem_platform.dylib"; 8 | const exp_name = "_platform_memcmp"; 9 | 10 | function prettyExportDetail(message) { 11 | return '[*]' + message + '\t' + exp_name + '()\tinside: ' + module_name; 12 | } 13 | 14 | if (ObjC.available) { 15 | var nonDecoableChars = 0; 16 | try { 17 | const ptrToExport = Module.findExportByName(module_name, exp_name); 18 | if (!ptrToExport) { 19 | throw new Error(prettyExportDetail('Cannot find Export:')); 20 | } 21 | } 22 | catch(err){ 23 | console.error(err.message); 24 | } 25 | finally { 26 | console.log(prettyExportDetail('Frida script running. Pointer: ')); 27 | } 28 | } 29 | else { 30 | console.warn("[!Frida not running!"); 31 | } 32 | 33 | Interceptor.attach(ptrToExport, { 34 | onEnter: function (args) { 35 | try { 36 | this._needle = Memory.readUtf8String(args[1]); 37 | } 38 | catch(err){ 39 | nonDecoableChars++; 40 | } 41 | }, 42 | 43 | onLeave: function (retValue) { 44 | 45 | if(retValue == '0x0' && this._needle !== '' && this._needle !== undefined){ 46 | console.log(JSON.stringify({ 47 | found_in_memory: this._needle, 48 | non_decoded_strs: nonDecoableChars 49 | })); 50 | } 51 | } 52 | }); 53 | 54 | 55 | /* 56 | [*]Frida script running. Pointer: _platform_memcmp() inside: libsystem_platform.dylib 57 | 58 | {"found_in_memory":".applecoloremojiui","non_decoded_strs":56} 59 | {"found_in_memory":"NSColor","non_decoded_strs":56} 60 | {"found_in_memory":"NSColor","non_decoded_strs":56} 61 | {"found_in_memory":"NSColor","non_decoded_strs":56} 62 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":56} 63 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":56} 64 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":62} 65 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":62} 66 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":66} 67 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":66} 68 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":66} 69 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":66} 70 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":66} 71 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":66} 72 | {"found_in_memory":"NSCTFontTraitsAttribute","non_decoded_strs":66} 73 | {"found_in_memory":"NSCTFontSymbolicTrait","non_decoded_strs":66} 74 | {"found_in_memory":"{0, 0, 0, 0}","non_decoded_strs":89} 75 | */ -------------------------------------------------------------------------------- /c_open.js: -------------------------------------------------------------------------------- 1 | var ptrToOpen = Module.findExportByName("libsystem_kernel.dylib", "open"); 2 | try { 3 | if (ptrToOpen == null) { 4 | throw "open() pointer not found"; 5 | } 6 | } 7 | catch(err){ 8 | console.log("[*]Exception: " + err.message); 9 | } 10 | finally { 11 | console.log("[+] open() intercept placed"); 12 | } 13 | 14 | Interceptor.attach(ptrToOpen, { 15 | onEnter: function (args) { 16 | var str = Memory.readUtf8String(args[0]); 17 | console.log("[*]\t" + str); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /c_sectrustevaluate.js: -------------------------------------------------------------------------------- 1 | const moduleName = 'Security'; 2 | const functionName = 'SecTrustEvaluate'; 3 | const SecTrustEvaluatePtr = Module.findExportByName(moduleName, functionName); 4 | 5 | try { 6 | if (SecTrustEvaluatePtr == null) { 7 | throw '[*] %s not found', moduleName, functionName; 8 | } 9 | console.log('[*] Script loaded. ' + moduleName + '.' + functionName + '\tpointer: '+ SecTrustEvaluatePtr); 10 | 11 | Interceptor.replace(SecTrustEvaluatePtr,new NativeCallback(function(trust,result) { 12 | 13 | console.log('[*]SecTrustEvaluate called'); 14 | console.log('\tDefault SecTrustResultType: ', Memory.readU32(result)); 15 | Memory.writeU32(result,1); 16 | console.log('\tnew SecTrustResultType: ', Memory.readU32(result)); 17 | return 0; // Return errSecSuccess to OSStatus call 18 | } ,'int',['pointer','pointer'])); 19 | } 20 | catch(err){ 21 | console.log('[!] Exception: ' + err.message); 22 | } -------------------------------------------------------------------------------- /c_substring.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | char *strstr(const char *haystack, const char *needle); 3 | FIND EXPORT: DebugSymbol.fromAddress(Module.findExportByName(null, 'strstr')) 4 | USAGE: frida -l c_substring.js -U -f appname --no-pause 5 | 6 | 7 | [*]Frida running. ObjC API available! 8 | [*]Pointer to strstr() inside: libsystem_c.dylib 9 | Spawned `funky-chicken.debugger-challenge`. Resuming main thread! 10 | [*]Needle:-UK 11 | [*]Needle:-TP 12 | [*]Needle:-CS 13 | [*]Needle:-YU 14 | [*]Needle:-UK 15 | [*]Needle:-TP 16 | [*]Needle:-CS 17 | [*]Needle:-YU 18 | [*]Needle:-UK 19 | ... 20 | .. 21 | . 22 | /************************************************************************************/ 23 | 24 | const module_name = "libsystem_c.dylib"; 25 | const exp_name = "strstr"; 26 | 27 | function prettyExportDetail(message) { 28 | return '[*]' + message + '\t' + exp_name + '()\tinside: ' + module_name; 29 | } 30 | 31 | if (ObjC.available) { 32 | console.log("[*]Frida running. ObjC API available!"); 33 | try { 34 | const ptrToExport = Module.findExportByName(module_name, exp_name); 35 | if (!ptrToExport) { 36 | throw new Error(prettyExportDetail('Cannot find Export:')); 37 | } 38 | console.log(prettyExportDetail('Pointer to')); 39 | 40 | Interceptor.attach(ptrToExport, { 41 | onEnter: function (args) { 42 | this._needle = Memory.readUtf8String(args[1]); 43 | console.log('[*]Needle:' + this._needle); 44 | }, 45 | 46 | onLeave: function (retValue) { 47 | if(retValue != '0x0'){ 48 | console.log(JSON.stringify({ 49 | substring_found: this._needle, 50 | return_value: retValue, 51 | function: exp_name 52 | })); 53 | } 54 | } 55 | }); 56 | } 57 | catch(err){ 58 | console.error(err.message); 59 | } 60 | } 61 | else { 62 | console.log("[!]Objective-C Runtime is not available!"); 63 | } 64 | -------------------------------------------------------------------------------- /c_task_thread.js: -------------------------------------------------------------------------------- 1 | /***********************************************************************************/ 2 | // USAGE: frida -l c_task_thread.js -U -f appname --no-pause 3 | /************************************************************************************/ 4 | 5 | const module_name = "libsystem_kernel.dylib"; 6 | const exp_name = "task_threads"; 7 | 8 | function prettyExportDetail(message) { 9 | return '[*]' + message + '\t' + exp_name + '()\tinside: ' + module_name; 10 | } 11 | 12 | if (ObjC.available) { 13 | console.log("[*]Frida running. ObjC API available!"); 14 | try { 15 | const ptrToExport = Module.findExportByName(module_name, exp_name); 16 | if (!ptrToExport) { 17 | throw new Error(prettyExportDetail('Cannot find Export:')); 18 | } 19 | console.log(prettyExportDetail('Pointer to')); 20 | 21 | Interceptor.attach(ptrToExport, { 22 | onEnter: function (args) { 23 | console.log(prettyExportDetail('onEnter() interceptor ->')); 24 | this._threadCountPointer = new NativePointer(args[2]); 25 | console.log('[*]Address of Thread Count:' + this._threadCountPointer ); 26 | }, 27 | 28 | onLeave: function (retValue) { 29 | this._patchInt = 4; 30 | console.log(JSON.stringify({ 31 | return_value: retValue, 32 | patched_return_value: this._patchInt, 33 | function: exp_name, 34 | thread_count: this._threadCountPointer.readPointer().toInt32() 35 | })); 36 | retValue.replace(this._patchInt); 37 | } 38 | }); 39 | } 40 | catch(err){ 41 | console.error(err.message); 42 | } 43 | } 44 | else { 45 | console.log("[!]Objective-C Runtime is not available!"); 46 | } 47 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | 3 | 4 | export default [ 5 | { 6 | files: ["**/*.js"], languageOptions: {sourceType: "script"} 7 | }, 8 | { 9 | languageOptions: { globals: globals.browser } 10 | }, 11 | { 12 | rules: { 13 | semi: "error", 14 | "prefer-const": "error" 15 | } 16 | } 17 | 18 | ]; 19 | -------------------------------------------------------------------------------- /objc_blocks.js: -------------------------------------------------------------------------------- 1 | /***********************************************************************************/ 2 | // USAGE: work in progress 3 | /************************************************************************************/ 4 | const blockArray = []; 5 | 6 | Interceptor.attach(ObjC.classes.YDURLSessionDel['- URLSession:didReceiveChallenge:completionHandler:'].implementation, { 7 | onEnter(args) { 8 | const bl = new ObjC(block.implementation); 9 | const blockPtr = new ObjC.Block(args[4]); 10 | console.log("[*] block ptr"); 11 | var origBlock = new ObjC.Block(blockPtr); 12 | console.log(origBlock); 13 | blockArray.push(origBlock); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /objc_cfstring.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); 3 | USAGE: frida -l objc_cfstring.js -U -f appname --no-pause 4 | ***********************************************************************************/ 5 | 6 | var ptrToCFStr = Module.findExportByName("CoreFoundation", "CFStringCreateWithCString"); 7 | 8 | try { 9 | if (ptrToCFStr == null) { 10 | throw "pointer not found"; 11 | } 12 | Interceptor.attach(ptrToCFStr, { 13 | onEnter: function (args) { 14 | this._str = Memory.readUtf8String(args[1]); 15 | console.log("[*]\t" + this._str); 16 | } 17 | }); 18 | console.log("[*]CFStringCreateWithCString() intercept placed"); 19 | } 20 | catch(err){ 21 | console.log("[*]Exception: " + err.message); 22 | } 23 | -------------------------------------------------------------------------------- /objc_deeplinks.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************/ 2 | // https://grepharder.github.io/blog/0x03_learning_about_universal_links_and_fuzzing_url_schemes_on_ios_with_frida.html 3 | /************************************************************************************/ 4 | 5 | -------------------------------------------------------------------------------- /objc_nslog.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************/ 2 | // USAGE: frida -l objc_nslog.js -U -f funky-chicken.com.app --no-pause -q 3 | // Ole's guidance: https://github.com/frida/frida/issues/279 4 | /************************************************************************************/ 5 | var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; 6 | var NSString = ObjC.classes.NSString; 7 | var pool = NSAutoreleasePool.alloc().init(); 8 | 9 | try { 10 | var NSLog = new NativeFunction(Module.findExportByName('Foundation', 'NSLog'), 'void', ['pointer', '...']); 11 | var str = NSString.stringWithFormat_('[*]foo ' + 'bar ' + 'lives'); // fails with unicode chars 12 | if (str.isKindOfClass_(ObjC.classes.NSString)){ 13 | console.log('str ' + str + '\ttype:' + str.$className); 14 | NSLog(str); 15 | } 16 | } 17 | catch(err){ 18 | console.error(err.message); 19 | } 20 | finally { 21 | pool.release(); 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /objc_nsstring_containsstring.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | -[NSString containsString:] 3 | Returns a Boolean value indicating whether the string contains a given string by performing a case-sensitive, locale-unaware search. 4 | 5 | (lldb) po $arg1 6 | haystack 7 | 8 | (lldb) po (char *)$arg2 9 | "containsString:" 10 | 11 | (lldb) po $arg3 12 | needle <-- where the "frida" string will appear 13 | 14 | USAGE: frida -l objc_nsstring_containsstring.js -U -f appname --no-pause 15 | ******************************************************************************/ 16 | var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; 17 | var methodPointer = ObjC.classes.NSString['- containsString:'].implementation; 18 | 19 | 20 | try { 21 | var pool = NSAutoreleasePool.alloc().init(); 22 | if (!methodPointer) { 23 | throw new Error('Cannot find method'); 24 | } 25 | } 26 | catch(err){ 27 | console.error(err.message); 28 | } 29 | finally { 30 | console.log('\"-[NSString containsString:]\" pointer:\t' + methodPointer); 31 | pool.release(); 32 | } 33 | 34 | Interceptor.attach(methodPointer, { 35 | onEnter: function (args) { 36 | this._needle = new ObjC.Object(args[2]); 37 | }, 38 | 39 | onLeave: function (retval) { 40 | if(this._needle != '-' && retval.equals(ptr(0x1))) { 41 | console.log(JSON.stringify({ 42 | needle: this._needle.toString(), 43 | retval: 'found' 44 | })); 45 | } 46 | } 47 | }); 48 | 49 | 50 | /* 51 | 52 | "-[NSString containsString:]" pointer: 0x184c7dd8c 53 | {"needle":"frida","retval":"found"} 54 | {"needle":"frida-gadget","retval":"found"} 55 | {"needle":"gdbus","retval":"found"} 56 | {"needle":"gum-js-loop","retval":"found"} 57 | 58 | */ -------------------------------------------------------------------------------- /objc_nsstring_stringwithutf8string.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | +[NSString stringWithUTF8String:] 3 | + (instancetype)stringWithUTF8String:(const char *)nullTerminatedCString; 4 | Returns a string created by copying the data from a given C array of UTF8-encoded bytes. 5 | TRICK: Remember arg3 is a const char * and NOT an NSString * 6 | 7 | (lldb) b "+[NSString stringWithUTF8String:]" 8 | Breakpoint 2: where = Foundation`+[NSString stringWithUTF8String:], address = 0x0000000184b77b00 9 | 10 | (lldb) po $arg1 11 | NSString 12 | 13 | (lldb) po (char *)$arg2 14 | "stringWithUTF8String:" 15 | 16 | (lldb) po (char *)$arg3 17 | needle <-- where the "frida" string to detect may appear 18 | 19 | USAGE: frida -l objc_nsstring_stringwithutf8string.js -U -f appname --no-pause 20 | ******************************************************************************/ 21 | 22 | 23 | var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; 24 | var methodPointer = ObjC.classes.NSString['+ stringWithUTF8String:'].implementation; 25 | 26 | try { 27 | var pool = NSAutoreleasePool.alloc().init(); 28 | if (!methodPointer) { 29 | throw new Error('Cannot find method'); 30 | } 31 | } 32 | catch(err){ 33 | console.error(err.message); 34 | } 35 | finally { 36 | console.log('\"+[NSString stringWithUTF8String:]\" Implementation Pointer:\t' + methodPointer); 37 | pool.release(); 38 | } 39 | 40 | Interceptor.attach(methodPointer, { 41 | onEnter: function (args) { 42 | console.log(JSON.stringify({ 43 | thread_id: this.threadId, 44 | needle: Memory.readUtf8String(args[2]), 45 | needle_addr: args[2] 46 | })); 47 | } 48 | }); 49 | 50 | 51 | /* OUTPUT 52 | 53 | "+[NSString stringWithUTF8String:]" Implementation Pointer: 0x184b77b00 54 | {"thread_id":771,"needle":"frida","needle_addr":"0x10257da60"} 55 | {"thread_id":771,"needle":"frida-server","needle_addr":"0x10257da6f"} 56 | {"thread_id":771,"needle":"FRIDA","needle_addr":"0x10257da7e"} 57 | {"thread_id":771,"needle":"frida-gadget","needle_addr":"0x10257da8d"} 58 | {"thread_id":771,"needle":"gum-js-loop","needle_addr":"0x10257da9c"} 59 | {"thread_id":771,"needle":"gdbus","needle_addr":"0x10257daab"} 60 | {"thread_id":771,"needle":"Connection interrupted","needle_addr":"0x183f17f68"} 61 | 62 | */ -------------------------------------------------------------------------------- /objc_nsurl.ts: -------------------------------------------------------------------------------- 1 | 2 | const resolver = new ApiResolver('objc'); 3 | 4 | const matches = resolver.enumerateMatches('-[NSURL initWithString:]'); 5 | const firstPtr = matches[0].address; 6 | 7 | Interceptor.attach(firstPtr, { 8 | onEnter: function(args) { 9 | const urlObj = new ObjC.Object(args[2]); 10 | const url = urlObj.toString(); 11 | console.log('URL -> ' + url); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /objc_nsurl_and_iskindof.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************/ 2 | // USAGE: frida -l objc_nsurl_and_iskindof.js -U -f funky-chicken.com.app --no-pause 3 | /************************************************************************************/ 4 | var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; 5 | var NSURL = ObjC.classes.NSURL; 6 | 7 | try { 8 | var pool = NSAutoreleasePool.alloc().init(); 9 | var nsurl = NSURL.URLWithString_('www.foobar.com'); 10 | if (nsurl.isKindOfClass_(ObjC.classes.NSURL)){ 11 | console.log('url ' + nsurl + '\ttype:' + nsurl.$className); 12 | } 13 | } 14 | catch(err){ 15 | console.error(err.message); 16 | } 17 | finally { 18 | pool.release(); 19 | } 20 | -------------------------------------------------------------------------------- /objc_nsurl_modify_url.js: -------------------------------------------------------------------------------- 1 | console.log('[*] listening For requests...'); 2 | let totalUrls = 0; 3 | const NSString = ObjC.classes.NSString; 4 | const evilServer = "https://[server hostname]/images/charizard.png"; 5 | 6 | if (ObjC.available) { 7 | 8 | try { 9 | const className = "NSURL"; 10 | const funcName = "- initWithString:"; 11 | const hook = eval('ObjC.classes.' + className + '["' + funcName + '"]'); 12 | 13 | Interceptor.attach(hook.implementation, { 14 | onEnter: function(args) { 15 | const urlObj = new ObjC.Object(args[2]); 16 | const urlStr = urlObj.toString(); 17 | 18 | if(urlStr.endsWith('.png')) { 19 | args[2] = NSString.stringWithString_(evilServer); 20 | console.log("target:", urlStr, "\nmodified:" , evilServer); 21 | } 22 | 23 | totalUrls++; 24 | console.log(`totalUrls=${totalUrls}`); 25 | } 26 | }); 27 | } catch (error) { 28 | console.log("[!] Exception: " + error.message); 29 | } 30 | } else { 31 | console.log("[!] Objective-C Runtime is not available!"); 32 | } -------------------------------------------------------------------------------- /objc_nsurlprotectionspace_host.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************/ 2 | // USAGE: frida -l objc_nsurlprotectionspace_host.js -U -f funky-chicken.com.app --no-pause 3 | /************************************************************************************/ 4 | const NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; 5 | const protectionSpace = ObjC.classes.NSURLProtectionSpace['- host']; 6 | 7 | try { 8 | var pool = NSAutoreleasePool.alloc().init(); 9 | if (!protectionSpace) { 10 | throw new Error('Cannot find Export'); 11 | } 12 | } 13 | catch(err){ 14 | console.error(err.message); 15 | } 16 | finally { 17 | pool.release(); 18 | } 19 | 20 | Interceptor.attach(protectionSpace.implementation, { 21 | 22 | onLeave: function (retval) { 23 | const hostname_objc_nsstr = new ObjC.Object(retval); 24 | 25 | console.log(JSON.stringify({ 26 | function: 'NSURLProtectionSpace onLeave()', 27 | hostname: hostname_objc_nsstr.toString(), 28 | type: typeof hostname_objc_nsstr, 29 | frida_type: hostname_objc_nsstr.$className, 30 | source_module: hostname_objc_nsstr.$moduleName, 31 | retval_addr: retval 32 | })); 33 | 34 | if(hostname_objc_nsstr.equals(retval)){ 35 | console.log('successful cast'); 36 | } 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /objc_nsurlqueryitem.js: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | ObjC.classes.NSURLQueryItem.$ownMethods 3 | // "+ queryItemWithName:value:" 4 | // "- initWithName:value:" 5 | *****************************************************************************/ 6 | 7 | console.log('[*] listening For NSURLQueryItem requests...'); 8 | 9 | const NSString = ObjC.classes.NSURLQueryItem; 10 | 11 | if (ObjC.available) { 12 | 13 | try { 14 | const className = "NSURLQueryItem"; 15 | const funcName = "- initWithName:value:"; 16 | const hook = eval('ObjC.classes.' + className + '["' + funcName + '"]'); 17 | 18 | Interceptor.attach(hook.implementation, { 19 | onEnter: function(args) { 20 | const queryParamName = new ObjC.Object(args[2]); 21 | const queryParamValue = new ObjC.Object(args[3]); 22 | const keyStr = queryParamName.toString(); 23 | const valStr = queryParamValue.toString(); 24 | console.log("Query parameter: " + keyStr + " " + valStr); 25 | } 26 | }); 27 | } catch (error) { 28 | console.log("[!] Exception: " + error.message); 29 | } 30 | } else { 31 | console.log("[!] Objective-C Runtime is not available!"); 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frida_scripts", 3 | "version": "1.0.0", 4 | "description": "iOS scripts that use Frida for introspection", 5 | "main": "c_access.js", 6 | "scripts": { 7 | "lint": "npx eslint ." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/rustymagnet3000/frida_scripts.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/rustymagnet3000/frida_scripts/issues" 17 | }, 18 | "homepage": "https://github.com/rustymagnet3000/frida_scripts#readme" 19 | } 20 | --------------------------------------------------------------------------------