This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
howto:iosfrida [2017/10/17 03:40] czokie [9.2. Implement enhanced SSL pinning hook] |
howto:iosfrida [2019/01/16 02:37] (current) czokie |
||
---|---|---|---|
Line 62: | Line 62: | ||
Download this file to ~/Documents | Download this file to ~/Documents | ||
- | < | + | < |
{ | { | ||
" | " | ||
Line 73: | Line 73: | ||
</ | </ | ||
+ | Or, if you're ready to do some on-board hooking without any external comms, use this instead | ||
+ | <code java FridaGadget.config> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | and create Tweak.js in the same directory with your required hooks. | ||
===== 2. Generate (or renew) signing credentials ===== | ===== 2. Generate (or renew) signing credentials ===== | ||
Line 89: | Line 102: | ||
It goes without saying you will need an IPA file to play with. Below, it is assumed you have downloaded your IPA file and saved it to " | It goes without saying you will need an IPA file to play with. Below, it is assumed you have downloaded your IPA file and saved it to " | ||
- | NB: This MUST be a decrypted Go4 app. these are available to download here [[howto:iosmod|IOSMOD]] | + | NB: This MUST be a decrypted Go4 app. these are available to [[howto:firmware# |
===== 4. Application Modification & Signing ===== | ===== 4. Application Modification & Signing ===== | ||
Line 95: | Line 108: | ||
rm -rf Payload | rm -rf Payload | ||
mkdir -p " | mkdir -p " | ||
- | cp FridaGadget.config " | + | cp FridaGadget.config |
zip -r "DJI GO 4.ipa" Payload | zip -r "DJI GO 4.ipa" Payload | ||
objection patchipa -s "DJI GO 4.ipa" --codesign-signature <your signature> | objection patchipa -s "DJI GO 4.ipa" --codesign-signature <your signature> | ||
Line 121: | Line 134: | ||
After launch is complete, you can disconnect the USB cable. Make sure you promptly launch Frida if you have it in " | After launch is complete, you can disconnect the USB cable. Make sure you promptly launch Frida if you have it in " | ||
==== 6.3 Launch in Springboard ==== | ==== 6.3 Launch in Springboard ==== | ||
- | Ideally, we want to be able to launch | + | We can now launch |
===== 7. Launch objection ===== | ===== 7. Launch objection ===== | ||
Line 157: | Line 170: | ||
The default objection hook code is [[https:// | The default objection hook code is [[https:// | ||
- | We found that this was working for SOME requests, but not all. Particularly it appears on IOS10. Instead, you can replace the default hook with [[https:// | + | We found that this was working for SOME requests, but not all. Particularly it appears on IOS10. Instead, you can replace the default hook with [[https:// |
wget -q -O- https:// | wget -q -O- https:// | ||
Line 165: | Line 178: | ||
==== 9.3. Build our first hook ==== | ==== 9.3. Build our first hook ==== | ||
Frida is new to me - I would like to build a hook that tricks the DJI go app into thinking the terms and conditions have already been accepted - even for a new install. | Frida is new to me - I would like to build a hook that tricks the DJI go app into thinking the terms and conditions have already been accepted - even for a new install. | ||
+ | |||
+ | <code c> | ||
+ | bool __cdecl -[DJITermsNotificationController shouldShowTerms](DJITermsNotificationController *self, SEL a2) | ||
+ | { | ||
+ | void *v2; // x0 | ||
+ | void *v3; // x0 | ||
+ | void *v4; // x21 | ||
+ | BOOL v5; // w22 | ||
+ | |||
+ | objc_msgSend(& | ||
+ | v2 = (void *)objc_retainAutoreleasedReturnValue(); | ||
+ | objc_msgSend(v2, | ||
+ | v3 = (void *)objc_retainAutoreleasedReturnValue(); | ||
+ | objc_msgSend(v3, | ||
+ | v4 = (void *)objc_retainAutoreleasedReturnValue(); | ||
+ | NSLog(); | ||
+ | if ( (unsigned __int64)objc_msgSend(v4, | ||
+ | LOBYTE(v5) = 0; | ||
+ | else | ||
+ | v5 = (unsigned __int64)objc_msgSend(v4, | ||
+ | objc_release(); | ||
+ | objc_release(); | ||
+ | objc_release(); | ||
+ | return v5; | ||
+ | } | ||
+ | // 10276FD28: using guessed type __CFString cfstr_Applelanguages; | ||
+ | // 10279F268: using guessed type __CFString cfstr_Ru_0; | ||
+ | // 1027BEB88: using guessed type __CFString cfstr_Zh_0; | ||
+ | // 102D88FF0: using guessed type __objc2_ivar stru_102D88FF0; | ||
+ | </ | ||
+ | |||
+ | So... What does the frida patch look like... (Thanks jezzab) | ||
+ | |||
+ | <code javascript djihook.js> | ||
+ | //Terms and Conditions Bypass - jezzab | ||
+ | var hook = ObjC.classes.DJITermsNotificationController[" | ||
+ | Interceptor.attach(hook.implementation, | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | }); | ||
+ | </ | ||
+ | |||
+ | Subsequently, | ||
+ | |||
+ | He provided the following as a replacement sample for our simple hook. | ||
+ | <code javascript djihook2.js> | ||
+ | var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; | ||
+ | |||
+ | var shouldShowTerms = DJITermsNotificationController[' | ||
+ | shouldShowTerms.implementation = ObjC.implement(shouldShowTerms, | ||
+ | return false; | ||
+ | });</ | ||
+ | |||
+ | or if we want to see the original value... | ||
+ | |||
+ | <code javascript djihook3.js> | ||
+ | var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; | ||
+ | |||
+ | var shouldShowTerms = DJITermsNotificationController[' | ||
+ | var shouldShowTermsImpl = shouldShowTerms.implementation; | ||
+ | shouldShowTerms.implementation = ObjC.implement(shouldShowTerms, | ||
+ | var originalResult = shouldShowTermsImpl(handle, | ||
+ | console.log(' | ||
+ | return false; | ||
+ | }); | ||
+ | </ | ||
+ | |||
+ | Some other comments... What about the original data that comes into this function? What can we do with it? | ||
+ | |||
+ | oleavr: method arguments follow self and sel (the first two implicit arguments). you can use var self = new ObjC.Object(handle); | ||
+ | |||
+ | Lastly - If we need any intrusive hooks in our code - we can add the below if we need to have intrusive hooks for debugging. | ||
+ | if (Process.codeSigningPolicy === ' | ||
==== 9.4. First Standalone Patch ==== | ==== 9.4. First Standalone Patch ==== | ||
- | Install | + | Good news. We accomplished |
+ | * We need to use swizzling instead of intercepting in our hooks | ||
+ | * The author of Frida has agreed to do a patch to the gadget, so that if a phone is not jailbroken | ||
+ | * The above change is now in Frida... To implement this, we need the following frida configuration | ||
+ | <code java> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | * One more thing... The on_change config item. This is another mod that oleavr made to Frida - In IOS, we can upload a .js file to user space via iTunes or ifunbox etc. When frida loads up, it will look first in the documents location for a JS file. If found, it will use it. If not found, it will look for one in the app from build time. The advantage of this is that we can update the JS without a complete app rebuild. | ||
==== 9.4. Add more Standalone Patches ==== | ==== 9.4. Add more Standalone Patches ==== | ||
Create more hooks and package with a frida tweak' | Create more hooks and package with a frida tweak' | ||
Line 176: | Line 278: | ||
- Login " | - Login " | ||
+ | |||
+ | ==== 9.5. Persistant Configuration ==== | ||
+ | We need a way to store some persistent configuration that can be read by all of these hooks. At first glance, there is SQLite support, which will do the storage, but we really don't want to execute SQL code each time we want to test if a particular widget is enabled or disabled. Taking that forward, the next logical place to go would be to use **frida-compile**, | ||
+ | |||
+ | Some useful details are [[https:// | ||
+ | |||
+ | There are some Frida-specific modules as well - like frida-uikit for UI automation on iOS | ||
+ | |||
+ | ==== 9.6. UI Settings ==== | ||
+ | Having build a persistent configuration object, we now need to think about how we can build our own UI that will allow our custom settings to be changed. | ||
===== 10. General Notes ===== | ===== 10. General Notes ===== | ||
==== 10.1. Useful LLDB commands ==== | ==== 10.1. Useful LLDB commands ==== | ||
Line 208: | Line 320: | ||
* [[https:// | * [[https:// | ||
+ | ==== 10.5. Building Frida ==== | ||
+ | |||
+ | We've worked with the author of Frida for a few things... Below is a simple set of instructions to rebuild the Frida Gadget for IOS, to take advantage of some new tweaks. | ||
+ | |||
+ | * Install Xcode with command-line tools installed (Should already be done above) | ||
+ | * Install Node.js (from nodes.org). | ||
+ | * Do a git clone of the Frida repo | ||
+ | * Set an environment variable IOS_CERTID to be the ID of a valid code-signing certificate | ||
+ | * make gadget-ios | ||
+ | |||
+ | |||
+ | What does that bring us? We have a new config option | ||
+ | |||
+ | { " | ||
+ | |||
+ | This option will allow us to have a frida modified IPA that will run on our device, launched by springboard with no special debug startup sequences required. | ||
+ | |||
+ | ===== 11. Error Codes ===== | ||
+ | If you get an error during install, [[http:// | ||
+ | |||
+ | ===== 12. Credits ===== | ||
+ | oleavr from Frida has been awesome through this process. Some of our needs were not readily possible in the existing Frida code. He has made changes throughout the process to the Frida-gadget which have been enormously helpful. In addition, he has provided guidance and coaching as we came up to speed with using Frida... so thanks oleavr. | ||
+ | |||
+ | ===== 13. Footnote ===== | ||
+ | In parallel to the Objection method of patching, work has been ongoing into an alternate method that can be scripted "in the cloud" | ||
+ | |||
+ | This work had stalled, but it was confirmed to be working. However, it was discovered recently that IPA files from 4.3.2 and later fail to be signed using this tool. Below is an analysis of the current state of problem solving. | ||
+ | |||
+ | Firstly... a quick review of the components. isign is a python app. It uses a component called " | ||
+ | |||
+ | Construct is " | ||
+ | |||
+ | When using isign with IPA 4.3.2 or later, it parses all of the framework binaries, and then moves to the main DJI GO 4 macho binary and we get a construct error. A copy of the error message is listed below: | ||
+ | |||
+ | < | ||
+ | working on / | ||
+ | removing ua: / | ||
+ | Traceback (most recent call last): | ||
+ | File "/ | ||
+ | pkg_resources.run_script(' | ||
+ | File "/ | ||
+ | self.require(requires)[0].run_script(script_name, | ||
+ | File "/ | ||
+ | execfile(script_filename, | ||
+ | File "/ | ||
+ | isign.resign(app_path, | ||
+ | File "/ | ||
+ | alternate_entitlements_path) | ||
+ | File "/ | ||
+ | ua.bundle.resign(deep, | ||
+ | File "/ | ||
+ | super(App, self).resign(deep, | ||
+ | File "/ | ||
+ | self.sign(deep, | ||
+ | File "/ | ||
+ | executable = self.signable_class(self, | ||
+ | File "/ | ||
+ | self.m = macho.MachoFile.parse_stream(self.f) | ||
+ | File "/ | ||
+ | return self._parse(stream, | ||
+ | File "/ | ||
+ | subobj = sc._parse(stream, | ||
+ | File "/ | ||
+ | obj = self.cases.get(key, | ||
+ | File "/ | ||
+ | subobj = sc._parse(stream, | ||
+ | File "/ | ||
+ | return self.subcon._parse(stream, | ||
+ | File "/ | ||
+ | raise ArrayError(" | ||
+ | construct.core.ArrayError: | ||
+ | </ | ||
+ | |||
+ | So. Lets talk about program flow. The structure of the macho binary is defined in [[https:// | ||
+ | |||
+ | self.m = macho.MachoFile.parse_stream(self.f) | ||
+ | |||
+ | To help understand where the failure is happening, [[https:// | ||
+ | |||
+ | If you look at line 169 of [[https:// | ||
+ | |||
+ | #Probe(), | ||
+ | |||
+ | To get some debugging data as the IPA is parsed, remove the comment character above in [[https:// | ||
+ | |||
+ | However, we get a new error when debugging is enabled. From what I can tell, the debugging stream is reset on changing input files. isign parses multiple files, and only fails on the last file. The debugger does not work well on opening the next file. To get debug data, I modified the test IPA file to remove all of the framework directories, | ||
+ | |||
+ | So. Whats next? To fix this problem will require comparing by hand the parsing of the 4.3.10 macho binary (specifically the DJI GO 4 file inside the IPA) against this debug data... and finding out what changes are required to [[https:// | ||
+ | |||
+ | Below is a table of the modules found within DJI GO 4 version 4.3.10 - which was created by hand analysis of the actual IPA file. This shows 79 load commands, yet Construct finds only 78. Which one is missing? Time to compare the table below with the [[https:// | ||
+ | ==== Manual analysis of DJI GO 4 v4.3.10 ==== | ||
+ | |Count|Hex|Command|Name| | ||
+ | |1|19|LC_SEGMENT_64|_PAGEZERO| | ||
+ | |2|19|LC_SEGMENT_64|_TEXT| | ||
+ | |3|19|LC_SEGMENT_64|_DATA| | ||
+ | |4|19|LC_SEGMENT_64|_LINKEDIT| | ||
+ | |5|80000022|LC_DYLD_INFO_ONLY| | | ||
+ | |6|02|LC_SYMTAB| | | ||
+ | |7|0B|LC_DYSYMTAB| | | ||
+ | |8|08|LC_LOAD_DYLINKER| | | ||
+ | |9|1B|LC_UUID| | | ||
+ | |10|25|LC_VERSION_MIN_IPHONEOS| | | ||
+ | |11|2A|LC_SOURCE_VERSION| | | ||
+ | |12|80000028|LC_MAIN| | | ||
+ | |13|2C|LC_ENCRYPYION_INFO_64| | | ||
+ | |14|0C|LC_LOAD_DYLIB|libc++.1.dylib| | ||
+ | |15|0C|LC_LOAD_DYLIB|libsqlite3.dylib| | ||
+ | |16|0C|LC_LOAD_DYLIB|libxml2.2.dylib| | ||
+ | |17|0C|LC_LOAD_DYLIB|libz.1.dylib| | ||
+ | |18|0C|LC_LOAD_DYLIB|AVFoundation| | ||
+ | |19|0C|LC_LOAD_DYLIB|Accelerate| | ||
+ | |20|0C|LC_LOAD_DYLIB|AddressBook| | ||
+ | |21|0C|LC_LOAD_DYLIB|AssetsLibrary| | ||
+ | |22|0C|LC_LOAD_DYLIB|CFNetwork| | ||
+ | |23|0C|LC_LOAD_DYLIB|CoreData| | ||
+ | |24|0C|LC_LOAD_DYLIB|CoreFoundation| | ||
+ | |25|80000018|LC_LOAD_WEAK_DYLIB|CoreGraphics| | ||
+ | |26|0C|LC_LOAD_DYLIB|CoreImage| | ||
+ | |27|80000018|LC_LOAD_WEAK_DYLIB|CoreLocation| | ||
+ | |28|0C|LC_LOAD_DYLIB|CoreMedia| | ||
+ | |29|0C|LC_LOAD_DYLIB|CoreMotion| | ||
+ | |30|0C|LC_LOAD_DYLIB|CoreTelephony| | ||
+ | |31|0C|LC_LOAD_DYLIB|CoreText| | ||
+ | |32|0C|LC_LOAD_DYLIB|CoreVideo| | ||
+ | |33|80000018|LC_LOAD_WEAK_DYLIB|Foundation| | ||
+ | |34|0C|LC_LOAD_DYLIB|ImageIO| | ||
+ | |35|0C|LC_LOAD_DYLIB|MessageUI| | ||
+ | |36|0C|LC_LOAD_DYLIB|MobileCoreServices| | ||
+ | |37|0C|LC_LOAD_DYLIB|OpenGLES| | ||
+ | |38|80000018|LC_LOAD_WEAK_DYLIB|QuartzCore| | ||
+ | |39|0C|LC_LOAD_DYLIB|SafariServices| | ||
+ | |40|80000018|LC_LOAD_WEAK_DYLIB|Security| | ||
+ | |41|0C|LC_LOAD_DYLIB|StoreKit| | ||
+ | |42|0C|LC_LOAD_DYLIB|SystemConfiguration| | ||
+ | |43|80000018|LC_LOAD_WEAK_DYLIB|UIKit| | ||
+ | |44|80000018|LC_LOAD_WEAK_DYLIB|Accounts| | ||
+ | |45|80000018|LC_LOAD_WEAK_DYLIB|AudioToolbox| | ||
+ | |46|80000018|LC_LOAD_WEAK_DYLIB|Social| | ||
+ | |47|80000018|LC_LOAD_WEAK_DYLIB|WatchConnect| | ||
+ | |48|0C|LC_LOAD_DYLIB|AWSS3| | ||
+ | |49|0C|LC_LOAD_DYLIB|AWSCore| | ||
+ | |50|0C|LC_LOAD_DYLIB|Masonry| | ||
+ | |51|0C|LC_LOAD_DYLIB|pop| | ||
+ | |52|0C|LC_LOAD_DYLIB|CoreBluetooth| | ||
+ | |53|0C|LC_LOAD_DYLIB|DJIFlySimulator| | ||
+ | |54|0C|LC_LOAD_DYLIB|libresolv.9.dylib| | ||
+ | |55|80000018|LC_LOAD_WEAK_DYLIB|NetworkExtension| | ||
+ | |56|0C|LC_LOAD_DYLIB|libicucore.A.dylib| | ||
+ | |57|0C|LC_LOAD_DYLIB|GLKit| | ||
+ | |58|0C|LC_LOAD_DYLIB|SceneKit| | ||
+ | |59|0C|LC_LOAD_DYLIB|VideoToolbox| | ||
+ | |60|0C|LC_LOAD_DYLIB|JavaScriptCore| | ||
+ | |61|80000018|LC_LOAD_WEAK_DYLIB|ReplayKit| | ||
+ | |62|0C|LC_LOAD_DYLIB|Photos| | ||
+ | |63|0C|LC_LOAD_DYLIB|DJPanoramaKit| | ||
+ | |64|0C|LC_LOAD_DYLIB|BokehFramework| | ||
+ | |65|0C|LC_LOAD_DYLIB|MediaPlayer| | ||
+ | |66|0C|LC_LOAD_DYLIB|DJHttpProtocol| | ||
+ | |67|0C|LC_LOAD_DYLIB|libiconv.2.dylib| | ||
+ | |68|0C|LC_LOAD_DYLIB|libbz2.1.0.dylib| | ||
+ | |69|0C|LC_LOAD_DYLIB|libobjc.A.dylib| | ||
+ | |70|0C|LC_LOAD_DYLIB|libSystem.B.dylib| | ||
+ | |71|0C|LC_LOAD_DYLIB|AVKit| | ||
+ | |72|0C|LC_LOAD_DYLIB|ExternalAccessory| | ||
+ | |73|0C|LC_LOAD_DYLIB|GameController| | ||
+ | |74|0C|LC_LOAD_DYLIB|MapKit| | ||
+ | |75|0C|LC_LOAD_DYLIB|WebKit| | ||
+ | |76|1C|LC_RPATH| | | ||
+ | |77|26|LC_FUNCTION_STARTS| | | ||
+ | |78|29|LC_DATA_IN_CODE| | | ||
+ | |79|1D|LC_CODE_SIGNATURE| | |