====== IOS Tweaking with Frida & Objection ====== I had planned to look at the IOS app for some time. More recently, DJI decided to implement certificate pinning to try to stop us from seeing what data they are exchanging. My first thought "How rude!". To get under the hood, Hostile reminded me (again) that I should really look at Frida. Never done it before, but that is no excuse. I started looking, and I also found https://github.com/dpnishant/appmon and https://github.com/sensepost/objection My first efforts were to get the basic frida functionality working since these apps depended on it. But this was a major fail. Frida on Python 2.x was not working for me all that well. And on 3.x, it failed to install due to some weird certificate errors that are OSX related. After struggling, I downloaded the egg file and did the install manually. That worked. While researching, I concluded that Objection looked like a good fit for what I wanted, so I installed that also. Below is the step by step of what I have done so far to get to this point. ===== 1. Install required tools ===== ==== 1.0. Install Base Tools & Dependencies==== We need several tools and dependencies in order to start. Many of these may be already installed on your mac if you've been involved in previous development work. However, its a good idea to check and install if necessary. Lets start with Xcode. === 1.0.1 Install Xcode === Go to App store, search and Install Xcode, this is a multi GB (around 5) install, so give it time. Once complete, In terminal run the following commands sudo xcode-select -s /Applications/Xcode.app/Contents/Developer === 1.0.2 Install Brew === /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" === 1.0.3 Install Python 3 === brew install python3 === 1.0.4 Install wget === brew install wget === 1.0.5 Install NPM === brew install npm ==== 1.1. Install Frida ==== Frida does not install cleanly on OSX using pip3. There is some weird ass certificate error. I've googled enough and decided to cheat. By putting the egg file in ~ it just works... cd ~ wget `wget -q "https://pypi.python.org/pypi/frida" -O- | grep macosx | tail -1 | cut -d "\"" -f 2` pip3 install frida ==== 1.2. Install objection ==== pip3 install objection -U ==== 1.3. Install applesign ==== npm install -g applesign ==== 1.4. Install insert_dylib ==== cd ~/Documents git clone https://github.com/Tyilo/insert_dylib cd insert_dylib xcodebuild cp build/Release/insert_dylib /usr/local/bin/insert_dylib ==== 1.5. Install ios-deploy ==== npm install -g ios-deploy ==== 1.6. Create a frida gadget configuration file ==== The [[https://www.frida.re/docs/gadget/|Frida Gadget]] supports configuration parameters, that will allow the gadget to start in a variety of modes. We need to add this file to the IPA which will be read by the gadget during application load. This is particularly important, since we dont want to be dependent on the USB cable which is the default behaviour. At the moment, the behaviour here is to wait for frida to connect. Later, we can have our own gadget scripts that we will add into the IPA which will work standalone, without a need to have a network connection at all. Download this file to ~/Documents { "interaction": { "type": "listen", "address": "0.0.0.0", "port": 27042, "on_load": "wait" } } Or, if you're ready to do some on-board hooking without any external comms, use this instead { "interaction": { "type": "script", "path": "Tweak.js", "on_change": "reload" }, "code_signing": "required" } and create Tweak.js in the same directory with your required hooks. ===== 2. Generate (or renew) signing credentials ===== ==== 2.1. Generate a code signing certificate ==== * https://github.com/sensepost/objection/wiki/Patching-iOS-Applications#preparations---xcode ==== 2.2. Generate a mobileprovision file ==== * https://github.com/sensepost/objection/wiki/Patching-iOS-Applications#preparations---mobileprovision ==== 2.3. Take note of your code signing value ==== You will need a hex string to use below, which is your code-signing identity. Execute this command to get your value security find-identity -p codesigning -v ===== 3. Application Acquisition ===== 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 "~/Documents/DJI GO 4.ipa" NB: This MUST be a decrypted Go4 app. these are available to [[howto:firmware#dji_go_4|download here]] ===== 4. Application Modification & Signing ===== cd ~/Documents rm -rf Payload mkdir -p "Payload/DJI GO 4.app/Frameworks" cp FridaGadget.config Tweak.js "Payload/DJI GO 4.app/Frameworks/" zip -r "DJI GO 4.ipa" Payload objection patchipa -s "DJI GO 4.ipa" --codesign-signature ===== 5. Application Deploying ===== cd ~/Documents rm -rf Payload unzip *-frida-codesigned.ipa ios-deploy --bundle Payload/*.app -W ===== 6. Launch the app ===== There are a few options for launching... Pick which is best for you ==== 6.1 Launch with lldb debugger active ==== Device must be connected via USB and unlocked. ios-deploy --bundle ~/Documents/Payload/*.app -W -d -m [[https://github.com/gm281/lldb-trace/|this]] may also be of interest to trace some of the flow via lldb ==== 6.2 Just launch with no debugger active ==== ios-deploy --bundle ~/Documents/Payload/*.app -m -L After launch is complete, you can disconnect the USB cable. Make sure you promptly launch Frida if you have it in "wait" mode, otherwise the app will fail to completely launch. ==== 6.3 Launch in Springboard ==== We can now launch in Springboard, if you correctly configured your Frida configuration file, and you are only using Swizzle hooks. ===== 7. Launch objection ===== Launch the objection user interface in a new terminal window. Objection is a front end with pre packaged Frida hooks. ==== 7.1 USB Connection ==== objection explore ==== 7.2 Network Connection ==== objection -N -h explore ===== 8. Use The Hooks ===== Too many hooks to cover here... but the first one that was of interest to me is listed below. ios sslpinning disable ===== 9. What's next ===== ==== 9.1. Explore network traffic ==== Look at traffic that we can see, noting that most of it is now not SSL pinned. What is of interest? Starting objection as described above is great - but there is one better option. objection -N -h explore --startup-command "ios sslpinning disable" This will automatically apply the SSL pinning bypass as soon as frida connects. By doing this, we appear to get access to all SSL comms without pinning, with the exception of traffic to https://statistical-report.djiservice.org ... for reasons unknown at this time. ==== 9.2. Implement enhanced SSL pinning hook ==== The default SSL pinning works out of the box with Frida/Objection - However, some DJI traffic is still not able to be read. Looking at lldb, we find: DJI GO 4[5809:2007849] CFNetwork SSLHandshake failed (-9807) This error message is [[https://opensource.apple.com/source/libsecurity_ssl/libsecurity_ssl-36800/lib/SecureTransport.h|defined here]]... errSSLXCertChainInvalid = -9807, /* invalid certificate chain */ The default objection hook code is [[https://github.com/sensepost/objection/blob/master/objection/hooks/ios/pinning/disable.js|listed here]] for reference 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://raw.githubusercontent.com/vtky/Swizzler2/master/SSLKillSwitch.js|this alternative hook]] and you should be good to go. Install with the wget command below. wget -q -O- https://raw.githubusercontent.com/vtky/Swizzler2/master/SSLKillSwitch.js > /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/objection/hooks/ios/pinning/disable.js After making this change, DJI ssl pinning is your oyster. Crack it open! ==== 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. bool __cdecl -[DJITermsNotificationController shouldShowTerms](DJITermsNotificationController *self, SEL a2) { void *v2; // x0 void *v3; // x0 void *v4; // x21 BOOL v5; // w22 objc_msgSend(&OBJC_CLASS___NSUserDefaults, "standardUserDefaults"); v2 = (void *)objc_retainAutoreleasedReturnValue(); objc_msgSend(v2, "objectForKey:", CFSTR("AppleLanguages")); v3 = (void *)objc_retainAutoreleasedReturnValue(); objc_msgSend(v3, "objectAtIndex:", 0LL); v4 = (void *)objc_retainAutoreleasedReturnValue(); NSLog(); if ( (unsigned __int64)objc_msgSend(v4, "hasPrefix:", CFSTR("zh-"), v4) & 1 ) LOBYTE(v5) = 0; else v5 = (unsigned __int64)objc_msgSend(v4, "hasPrefix:", CFSTR("ru")) ^ 1; 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) //Terms and Conditions Bypass - jezzab var hook = ObjC.classes.DJITermsNotificationController["- shouldShowTerms"]; Interceptor.attach(hook.implementation, { onLeave: function(retval) { console.log("Bypassing Terms and Conditions Dialog"); retval.replace(0); } }); Subsequently, I spoke to the author of Frida about a problem with our work so far. Specifically, we want to be able to run the DJI GO app without launching via debug. He explained that the interceptor method is fairly intrusive, dynamically modifying code which gets caught by IOS. Instead, he suggested swizzling, which is an objective c alternative method to swap two functions. Even better, it passes all IOS security appsigning constraints. He provided the following as a replacement sample for our simple hook. var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; var shouldShowTerms = DJITermsNotificationController['- shouldShowTerms']; shouldShowTerms.implementation = ObjC.implement(shouldShowTerms, function (handle, selector) { return false; }); or if we want to see the original value... var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; var shouldShowTerms = DJITermsNotificationController['- shouldShowTerms']; var shouldShowTermsImpl = shouldShowTerms.implementation; shouldShowTerms.implementation = ObjC.implement(shouldShowTerms, function (handle, selector) { var originalResult = shouldShowTermsImpl(handle, selector); console.log('Original says:', originalResult, 'we say: false'); 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); then you can access the instance variables of self through self.$ivars depending on whether you want to access method arguments or instance variables (or other things on the instance) 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 === 'optional') { ==== 9.4. First Standalone Patch ==== Good news. We accomplished this. Jezzab has packaged an earlier js file into an app, and launched in standalone mode. However, as per above notes - we had a limitation. We needed to launch with debug mode, otherwise we got code signing violations. Two things are needed to fix this... * 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 and running without debug - the intercept methods and other naughtiness will be disabled - allowing a tweaked app to run with no code signing problems. * The above change is now in Frida... To implement this, we need the following frida configuration { "interaction": { "type": "script", "path": "Tweak.js", "on_change": "reload" }, "code_signing": "required" } * 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 ==== Create more hooks and package with a frida tweak'd app to change the behaviour of the application. For example: - Turning off SSL pinning by default - Responding with "Approved" to all GEO requests - Bypassing "terms of service" screens - Login "offline" ==== 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**, which has the nice benefit of giving you access to thousands of existing modules from npm, including (plenty of options for persistence) Some useful details are [[https://gist.github.com/oleavr/cae76c895eb7d227216ed3ffe9dbbeb3|available here]] on how to use it to hack on `frida-java`, but just mentally replace that with any other module from npm. 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.1. Useful LLDB commands ==== Interrupt process and halt process interrupt Set a breakpoint on SSLHandshake b SSLHandshake View registers register read If anything in the list looks like it has a CoreFoundation or Obj-C object, type the following: po And finally, if you think there's a C string being pointed to by one of the variables, you can use this: p (char*) ==== 10.2. Useful Objection Commands ===== ios cookies get ios nsuserdefaults get ios sslpinning disable ==== 10.3. Tracing Application Flow and System Calls ==== [[https://github.com/gm281/lldb-trace/|This GitHub project]] may be useful when combined with lldb to get some more information about execution flow - but have not fully got it working yet. ==== 10.4. Resources ==== * [[https://blog.securityevaluators.com/debugging-ios-applications-a-guide-to-debug-other-developers-apps-c041311498eb|A Guide to Debug Other Developers’ Apps]] * [[https://developer.apple.com/library/content/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/Introduction.html|LLDB Quick Start Guide]] * [[https://developer.apple.com/library/content/documentation/General/Conceptual/lldb-guide/chapters/Introduction.html#//apple_ref/doc/uid/TP40016717|LLDB Debugging Guide]] ==== 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 { "code_signing": "required" } 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://docwiki.appmethod.com/appmethod/1.16/topics/en/RunIOSDeviceFailedHelpPage|this page is a good reference guide]] to help troubleshoot. ===== 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 is dependent on one component called [[https://github.com/saucelabs/isign|isign]] 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 "[[https://pypi.org/project/construct/2.5.5/|construct]]". Note that iSign fails to operate with versions of Construct after 2.5.5. Also, pip fails to install 2.5.4 and 2.5.5 automatically. You will need to manually install if you want to experiment with these versions. Construct is "Construct is a powerful declarative parser (and builder) for binary data." In other words, you define a data structure and point the parser to a stream of data, and it builds python objects containing the data from the stream. 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 /tmp/isign-Kvt0Zy/Payload/DJI GO 4.app/DJI GO 4 removing ua: /tmp/isign-Kvt0Zy Traceback (most recent call last): File "/usr/bin/isign", line 5, in pkg_resources.run_script('isign==1.6.15.1547538117.dev26-root', 'isign') File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 540, in run_script self.require(requires)[0].run_script(script_name, ns) File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 1455, in run_script execfile(script_filename, namespace, namespace) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/EGG-INFO/scripts/isign", line 252, in isign.resign(app_path, **kwargs) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/isign.py", line 81, in resign alternate_entitlements_path) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/archive.py", line 400, in resign ua.bundle.resign(deep, signer, provisioning_profile, alternate_entitlements_path) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/bundle.py", line 262, in resign super(App, self).resign(deep, signer) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/bundle.py", line 177, in resign self.sign(deep, signer) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/bundle.py", line 172, in sign executable = self.signable_class(self, self.get_executable_path(), signer) File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547538117.dev26_root-py2.7.egg/isign/signable.py", line 41, in __init__ self.m = macho.MachoFile.parse_stream(self.f) File "/usr/lib/python2.7/site-packages/construct/core.py", line 193, in parse_stream return self._parse(stream, Container()) File "/usr/lib/python2.7/site-packages/construct/core.py", line 665, in _parse subobj = sc._parse(stream, context) File "/usr/lib/python2.7/site-packages/construct/core.py", line 846, in _parse obj = self.cases.get(key, self.default)._parse(stream, context) File "/usr/lib/python2.7/site-packages/construct/core.py", line 665, in _parse subobj = sc._parse(stream, context) File "/usr/lib/python2.7/site-packages/construct/core.py", line 266, in _parse return self.subcon._parse(stream, context) File "/usr/lib/python2.7/site-packages/construct/core.py", line 440, in _parse raise ArrayError("expected %d, found %d" % (count, c), sys.exc_info()[1]) construct.core.ArrayError: ('expected 79, found 78', ArrayError('expected 6, found 3', SwitchError('no default case defined',))) So. Lets talk about program flow. The structure of the macho binary is defined in [[https://github.com/saucelabs/isign/blob/master/isign/macho.py|macho.py]]. This object is used in [[https://github.com/saucelabs/isign/blob/master/isign/signable.py|signable.py]] in line 41 self.m = macho.MachoFile.parse_stream(self.f) To help understand where the failure is happening, [[https://pypi.org/project/construct/2.5.5/|construct]] provides some [[https://construct.readthedocs.io/en/latest/debugging.html|debugging]] capabilities. If you look at line 169 of [[https://github.com/saucelabs/isign/blob/master/isign/macho.py|macho.py]], you will notice there is a probe command commented out. #Probe(), To get some debugging data as the IPA is parsed, remove the comment character above in [[https://github.com/saucelabs/isign/blob/master/isign/macho.py|macho.py]] 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, Everything else was unchanged. I then get the following [[https://dji.retroroms.info/debug.txt|debug output]] 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://github.com/saucelabs/isign/blob/master/isign/macho.py|macho.py]] to parse this file. 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://dji.retroroms.info/debug.txt|debug output]] ==== 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| |