- iOS app introspection
- Jailbreak in 2026
- Get decrypted IPA off Jailbroken device
- Strings
- Get App Store iPAs
- Quick-start run-time introspection
- Objection
- Inspect files inside ipa
- Inspect sandbox data on Jailbroken device
- Logs
- Build information
- Applesign
- Sideload iOS app
- Frida-Server
- Frida-Gadget
- Frida basics
- Frida's --eval flag
- Frida Intercepter
- Frida-Trace
- Bypass anti-Frida checks
- Cookies
- Change iOS Version
- LLVM Instrumentation
Please read if you have a Jailbreak - checkra1n, unc0ver, TrollInstallerX or dopamine - and want to get it working.
There is no magic "download and it works". Forget Cydia Impactor.
Warning
If you just download a jailbreak ipa - even if it is TrollInstallerX it won't be code-signed. It won't install.
Preparing an old iOS device for Jailbreak takes time and effort:
- Find jailbreak
- Prepare iOS Device
- Prepare XCode for older iOS versions
- Create Provisioning Profile in XCode
- Sign and deploy Jailbreak IPA
- Verify you can SSH onto iOS device
- CheckRa1n great option when you have an older iOS device.
- PaleRa1n iOS 15+. Requires older iOS device.
- Electra iOS 11. But iOS 11 was now old. Most apps dropped support.
- [Dopamine]https://github.com/opa334/Dopamine/releases/tag/2.2.2 16.x
- Unc0ver iOS 14.2 device Unc0ver seems ok.
- Trust the
Profileof the Developer. - Ensure the device
Truststhe machine it connects with. - On newer iOS version this includes turning on Developer Settings; iOS 16.0 to 16.6.1
Settings -> Privacy & Securityand then scroll down until you see Developer Mode, tap that option.
Open XCode/Window/Devices and Simulators. Let XCode observe the device. This step is preparing us to grab a valid provisioning profile.
If you are trying to use an older device with an older OS, keep reading...
Let's say you have a new XCode version with an old device ( say iOS 14 ). Why would I let XCode and the iOS device get out of sync ? Jailbreaks are often limited to older iOS versions. For example, with Unc0ver, the latest XCode doesn't support iOS 14. If you upgrade iOS, you lose your chance to use that Jailbreak.
You can update XCode Device Support to support old iOS versions, as follows:
- Grab the Device Support files here.
- Open
Applications. Right-click on Xcode, in Finder, and select“package contents”. - Navigate to
contents/Developer/platform/iPhoneOS.platform/DeviceSupport. - Unzip the downloaded file and copy the folder to above location.
- Restart XCode.
- The older iOS device should now be a possible build target.
On a fresh, new copy of XCode, you may have to set your Project to the iOS Deployment Target you want AND also install the iOS latest image, before it builds; I found this confusing and new with XCode; so on an iOS 16.1 device, I had to install the 26.x iOS files before my XCode would be build any app, let alone iOS 16.1.
We need to get a Provisioning Profile ( called the embedded.mobileprovision file).
- Open
Xcodeand selectFile/New/Project/Swiftand call it anything. You don't need tests or any CoreData pieces. Just a basic "HelloWorld" app. - Select
buildfor your connected device.
[!INFO] You don’t need to
Runthe app on the device.
- Go to
/Users/< user id >/Library/Developer/Xcode/DerivedData/
If you look inside the folder ( remember HelloWorld.app is a folder ) you will find a fresh embedded.mobileprovision. This contains the uniques IDs and an expiry date for the developer profile associated to the app.
# find your "Apple Development" ID
security find-identity -v -p codesigning
# tools to inspect device ( works on jailed and jailbroken devices )
brew install libimobiledevice
# check if you can see attached iOS device
idevice_id
0000FFFF-0011111111122222 (USB)
# sign Jailbreak ipa
applesign -p -7 \
-i ${CODESIGNID} \
-b rm.helloworld \
--clone-entitlements \
-m embedded.mobileprovision \
TrollInstallerX.ipa
-o $SIGNED_IPA
# Deploy to the device over USB
ios-deploy -b $SIGNED_IPA# macOS - install iProxy
brew install libusbmuxd
# For SSH over USB access
iproxy 2222 22 &
# most jailbreaks: Password is well known
ssh -p 2222 root@localhost
# dopamine uses the `mobile` user not root
# user sets password when invoking the jailbreak
ssh -p 2222 mobile@localhostIf you have a test or local build that wasn't encrypted by Apple during the App Store submission ignore this section; you don't need to decrypt. Decrypting the app binary is essential, if the app was downloaded from the AppStore. You cannot skip this step if you want to find good strings, debug the app or repackage the iPA.
# Get script to decrypt iPA
https://github.com/AloneMonkey/frida-ios-dump
# Attach a jailbroken iPhone and create tunnel over USB
iproxy 2222 22 &
# Ensure Frida is running on iOS device. Then run frida-ios-dump
./dump.py foo.bar.bundleid
# Check AppStore binary is now decrypted ( cryptid 0 decrypted vs cryptid 1 encrypted )
otool -l Payload/foo.app/foo | grep -i LC_ENCRYPTION -B1 -A4
Load command 12
cmd LC_ENCRYPTION_INFO_64
cryptid 0
--If you didn't decrypt the app you won't find good strings.
strings $APP_BINARY | grep -E 'session|https|pinning|secret'- Install Apple's utility Apple Configurator 2 from macOS store
- Install the target iOS app on the target device
- Then open
Apple Configurator 2and "sign in" with the same Apple account used on the target device - Sign-out and sign-in to refresh the known app list
- Right click on device and select
Add/Apps - Select the app you want to copy
At this point Apple Configurator 2 will download a copy of the app to:
~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Caches/Assets/TemporaryItems/MobileApps/
When you hit the "Skip App / Replace / Stop" modal, select nothing. Go to Finder and grab the IPA.
Tools like Frida and Objection can work with apps straight from the app store. To work on a jailed device, the app needs to be re-signed as it adds a dynamic library ( gadget ) to the app's list of frameworks.
# Install Objection
pip3 install objection
# jailed device
objection --gadget $BUNDLE_ID start
# jailbroken device
objection --name $BUNDLE_ID start
#### Objection commands ####
# sandbox location on device
env
# search for useful methods
ios hooking search methods pincode
[MySwiftApp.Services - validatePinCode:error:]
[MySwiftApp.Services - disablePinCode]
# search for useful classes
ios hooking search classes pincode
# watch method calls inside a Class
ios hooking watch class MySwiftApp.Services
# verify Objection can see Class
ios hooking search classes MySwiftApp.Services
# list Class Methods of a Class
ios hooking list class_methods MySwiftApp.Services
# read info.plist
ios plist cat Info.plist
# download file
file download Info.plist
# dump memory
memory dump all myapp_memory.dump
Will dump 111 rw- images, totalling 718.5 MiB
Dumping 512.0 MiB from base: 0x280000000 [####################################] 100%
# search memory for strings
strings myapp_memory.dump | grep -i session
# KeyChain dump
ios keychain dump --json output.json
# list bundles used by app
ios bundles list_bundles
# list classes
ios hooking list classes
# list frameworks used by app
ios bundles list_frameworks
# list modules
memory list modules
# print UI hierarchy of view controllers, labels, buttons, etc
ios ui dump# Unzip the IPA file to reveal the Payload folder
unzip myApp.ipa
# big files inside ipa file
find Payload -size +2M
# Files that were mistakingly shipped inside of App Bundle
find . -name '*.json' -or -name '*.txt'
# Check for ReactNative
find . -name main.jsbundle
# Check for Certificates
find . -name '*.crt' -or -name '*.cer' -or -name '*.der'
# Property lists inside Payload folder. Recursive search.
find Payload/ -name '*.plist'
# Provisioning Profiles
find . -name '*.mobileprovision'
# Dynamically linked frameworks
find . -name '*.framework'
# Locally linked javascript
find Payload -name '*.js'
# Search all plist files for a value
find . -name '*.plist' | xargs grep "LSApplicationQueriesSchemes"
# Search all plist files for Device Permissions or App Transport Security
find . -name '*.plist' | xargs grep "NS"
# Search all files using only grep
grep "LSApplicationQueriesSchemes" . -R
# Recursive search all files using grep inside Payload folder
grep "Requires" Payload -R
# foobar.app/Info.plist: <key>UIRequiresFullScreen</key>
# foobar.app/Info.plist: <key>LSRequiresIPhoneOS</key># Sandbox. Look here for Cookies, Cache.db, Docuements, persisted data, json files, etc
/var/mobile/Containers/Data/Application/[GUID given at install time]/
# find runtime data location
cd /private/var/mobile/Containers/Data/Application/ && grep --include=\*.plist -rnw . -e "myapp"
# -r recursive
# -n line number
# -w match whole word
# -e ignore case
# Folder of App Bundle that was installed. Executables, frameworks, fonts, CSS, html. NIB files.
/private/var/containers/Bundle/Application/[GUID given at app install]/foo.app
# App executable
/private/var/containers/Bundle/Application/[GUID given at app install]/foo.app/foo
# freshly installed IPA is at the bottom of list
cd /private/var/mobile/Containers/Data/Application/ && ls -lrt
cd [app guid]/Documents/
cd [app guid]/Library/
# Databases to pull off a device
/private/var/Keychains
TrustStore.sqlite3
keychain-2.db
pinningrules.sqlite3# Extract IPA (whether App Store encrypted or not)
scp -r -P 2222 root@localhost:/var/containers/Bundle/Application/<app GUID>/hitme.app ~/hitme.app
# Different to SSH, the uppercase P for Port with SCP. Order important.
scp -P 2222 root@localhost:/var/root/overflow.c localfilename.c
# from Jailbroken device to local machine
# Cache.db used by WKWebView and NSURLSession
scp -P 2222 mobile@localhost:/private/var/mobile/Containers/Data/Application/[guid from install time]/Library/Caches/${BUNDLE_ID}/Cache.db .
# auto generated screenshot
scp -P 2222 mobile@localhost:/private/var/mobile/Containers/Data/Application/[guid from install time]/Library/SplashBoard/Snapshots/sceneID:${BUNDLE_ID}-default/< big long guid>A\@3x.ktx .
# from local machine to remote Jailbroken device
scp -P 2222 hello.txt root@localhost:/var/root/# physical device
idevicesyslog -u <DeviceID> | myPipedProgram
# Get logs from iOS Simulator
xcrun simctl spawn booted log stream --level=debug
# Get logs from iOS Simulator by App Name
xcrun simctl spawn booted log stream --predicate 'processImagePath endswith "MyAppName"'# Check platform
lipo -info libprogressbar.a
# Check for build errors
jtool -arch arm64 -L <binary inside app bundle>
# Check minimum iOS version & restrict linker flag
jtool -arch arm64 -l <binary inside app bundle
# Check Load Commands
rabin2 -H playground
# Sections of the Binary
objdump -macho -section-headers Payload/myApp.app/myApp
# iOS app entitlements
codesign -d --entitlements :- Payload/MyApp.app
jtool -arch arm64 --ent <binary inside app bundle>
# Check binary was stripped
rabin2 -I -a arm_64 <binary inside app bundle> | grep -E 'stripped|canary'
# Check Position Independent Code set
rabin2 -I -a arm_64 <binary inside app bundle> | grep -E 'pic|bits
# Check for Bitcode enabled
# [!] this command won't work on a locally built Simulator / iPhone app. Bitcode happens after setting `Archive`
otool -l libprogressbar.a | grep __LLVM
otool -arch arm64 -l tinyDynamicFramework | grep __LLVMApplesign is a wrapper around Codesigning tools from Apple.
npm install -g applesignFirst, you want to get hold of an embedded.mobileprovision file:
- Open
Xcodeand selectFile/New/Project/Swiftand call it anything. You don't need tests or any CoreData pieces. Just a basic "HelloWorld" app. - Select
buildfor your connected device.
[!INFO] You don’t need to
Runthe app on the device.
- Right click on the
/Product/HelloWorld.app- in the left hand view pane - and selectshow in Finder.
If you look inside the folder ( remember HelloWorld.app is a folder ) you will find a fresh embedded.mobileprovision. This contains the uniques IDs and an expiry date for the developer profile associated to the app.
# Read the Provisioning Profile
# ensure your device ID is in the profile and the profile is fresh.
security cms -D -i embedded.mobileprovision
# find code signing key
security find-identity -v -p codesigning
export CODESIGNID=<GUID>
# tell Applesign to use your Provisioning Profile
applesign -7 -i ${CODESIGNID} -m embedded.mobileprovision $UNSIGNED_IPA -o $SIGNED_IPA
# Speed up repackaging
rm -v $UNSIGNED_IPA | rm -v $SIGNED_IPA | 7z a $UNSIGNED_IPA Payload
# Re-sign IPA and set new Bundle ID
# caution,changing the Bundle ID can cause ios-deploy issues
// applesign -7 -i ${CODESIGNID} -b funky-chicken.resigned -m embedded.mobileprovision $UNSIGNED_IPA -o $SIGNED_IPA
Caution
Remember to decrypt that app ! You can't resign an encrypted AppStore app.
| Title | Detail |
|---|---|
| Missing Device ID | Check Provisioning Profile (embedded.mobileprovision) included device's UUID |
| Code signing key expired | Timeframe for paid iOS Developer license is one-year versus one-week for free developer signing key. |
| Wrong Code-Signing Key | check the Code Signing Key was NOT an iPhone Distribution key |
| Mismatched bundle ID | Error 0xe8008001. When you create the Provisioning Profile it knows of the Bundle ID. So when you resign an IPA, it must match this value. |
| identity is no longer valid | Error 0xe8008018: The identity used to sign the executable is no longer valid. Make sure that the Apple Development key was selected when running security find-identity -v -p codesigning, I hit this error when I selected a Developer ID Application. I should have selected the ID associated to Apple Development credential. |
| Code Signing Keys Match | check the Code Signing Key used when creating the Provisioning Profile matched the Code Signing Key selected when repackaging and code signing. |
| XCode check | When generating an app - to get hold of embedded.mobileprovision file - remember the Code signing options are different for each Project Target and ProjectTests. |
| Delete Old Apps | check no old app is installed on the phone [ that was signed with a different key ] but has the same Bundle ID. |
| Entitlements overload | You can have a Provisioning Profile (embedded.mobileprovision) that contained more Capabilities than the app you are re-signing. |
| Clone Entitlements | When the app is complicated, with many entitlements, sometimes it is easier just to --clone-entitlements with Applesign. |
| Wrong Bundle ID | When you add specific Entitlments you need a unique Bundle ID. Check whether you need to change Bundle ID when re-signing. |
| Network settings | Settings\General\Profiles and Device Management to trust the Developer Profile and App. This won't happen if you are manually proxying or setting a local DNS server., when installing with iOS-deploy. |
| Watch Extensions | iOS-deploy spits out lots of messages if you change the bundle ID when resigning an app ( Error 0xe800009e: This app contains an app extension with an illegal bundle identifier. If you use the free developer account, changing the Bundle ID is the only to repackage another party's app. I ended up deleting the Watch.app from the Payload bundle as I wasn't interested in testing. That worked. |
If none of the above work open Console.app on macOS. Select your device and set process:mobile_installation_proxy in the Search Bar. This will give details behind the sideloaded IPA error message.
ios-deploy -b $SIGNED_IPA // defaults to send over wifi
ios-deploy -b -W $SIGNED_IPA // uses USB
ios-deploy -B | grep -i funky // list Bundle IDs#### update host machine
pip3 install --upgrade frida
# list available devices
frida-ls-devices
# list processes and bundle ID from USB connected device
frida-ps -Uai
# Force open Calender on USB attached device
frida -U -f com.apple.mobilecal
# open foobar over usb and force start. starts app running
frida -U -f com.apple.mobilecal --no-pause
# get the target app's process ID from USB connected device
frida-ps -U | grep -i myapp
# Run script and quit Frida
frida -U -f foobar --no-pause -q --eval 'console.log("Hi Frida");'Since Frida version ~12.7, it was quick and simple to Frida on a Jailed device:
# Get Frida-Gadget
<https://github.com/frida/frida/releases>
# Unzip
gunzip frida-gadget-12.xx.xx-ios-universal.dylib.gz
# Create directory for Frida-Gadget
mkdir -p ~/.cache/frida
# Move Frida-Gadget
cp frida-gadget-12.xx.xx-ios-universal.dylib ~/.cache/frida/gadget-ios.dylib
# Invoke Frida-Gadget on Clean device
frida -U -f funky-chicken.debugger-challengefrida -U "My App" // Attach Frida to app over USB
Process.id
419
Process.getCurrentThreadId()
3843
var b = "hello frida"
console.log(b)
"hello frida"
c = Memory.allocUtf8String(b)
"0x1067ec510"
Memory.readUtf8String(c)
"hello frida"
console.log(c)
0x1067ec510
console.log(c.readUtf8String(5))
hello
console.log(c.readUtf8String(11))
hello frida
ptrToC = new NativePointer(c);
"0x1067ec510"
console.log(ptrToC)
0x1067ec510
console.log(ptrToC.readCString(8))
hello fr
Memory.readUtf8String(ptrToC)
"hello frida"Objective-C's syntax includes the : and @ characters. These characters were not used in the Frida Javascript API.
// Attach to playground process ID
frida -p $(ps -ax | grep -i -m1 playground |awk '{print $1}')
ObjC.available
true
ObjC.classes.UIDevice.currentDevice().systemVersion().toString()
"11.1"
ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String()
ObjC.classes.UIWindow.keyWindow().toString()
RET: <WKNavigation: 0x106e165c0>
// shows Static Methods and Instance Methods
ObjC.classes.NSString.$ownMethods
ObjC.classes.NSString.$ivars
var myDate = ObjC.classes.NSDate.alloc().init()
console.log(myDate)
2019-04-19 19:03:46 +0000
myDate.timeIntervalSince1970()
1555700626.021566
myDate.description().toString()
"2019-04-19 19:03:46 +0000"
var a = ObjC.classes.NSUUID.alloc().init()
console.log(a)
4645BFD2-94EE-413D-9CE5-8982D41ED6AE
a.UUIDString()
{
"handle": "0x7ff3b2403b20"
}
a.UUIDString().toString()
"4645BFD2-94EE-413D-9CE5-8982D41ED6AE"var b = ObjC.classes.NSString.stringWithString_("foo");
b.isKindOfClass_(ObjC.classes.NSString)
true
b.isKindOfClass_(ObjC.classes.NSUUID)
false
b.isEqualToString_("foo")
true
b.description().toString()
"foo"
var c = ObjC.classes.NSString.stringWithFormat_('foo ' + 'bar ' + 'lives');
console.log(c)
foo bar livesvar url = ObjC.classes.NSURL.URLWithString_('www.foobar.com')
console.log(url)
www.foobar.com
url.isKindOfClass_(ObjC.classes.NSURL)
true
console.log(url.$class)
NSURLvar b = ObjC.classes.NSString.stringWithString_("foo");
var d = ObjC.classes.NSData
d = b.dataUsingEncoding_(1) // NSASCIIStringEncoding = 1, NSUTF8StringEncoding = 4,
console.log(d)
<666f6f> // This prints the Hex value "666f6f = foo"
d.$className
"NSConcreteMutableData"
var x = d.CKHexString() // get you the Byte array as a Hex string
console.log(x)
666f6f
x.$className
"NSTaggedPointerString"
var newStr = ObjC.classes.NSString.stringWithUTF8String_[d.bytes]
// demoapp is the iOS app name
myapp=$(ps x | grep -i -m1 demoapp | awk '{print $1}')
frida-trace -i "getfsent*" -p $myapp
// Connect to process with Frida script
frida --codeshare mrmacete/objc-method-observer -p 85974Process.enumerateModules()
// this will print all loaded Modules
Process.findModuleByName("libboringssl.dylib")
{
"base": "0x1861e2000",
"name": "libboringssl.dylib",
"path": "/usr/lib/libboringssl.dylib",
"size": 712704
}
Process.findModuleByAddress("0x1c1c4645c")
{
"base": "0x1c1c2a000",
"name": "libsystem_kernel.dylib",
"path": "/usr/lib/system/libsystem_kernel.dylib",
"size": 200704
}
DebugSymbol.fromAddress(Module.findExportByName(null, 'strstr'))
{
"address": "0x183cb81e8",
"fileName": "",
"lineNumber": 0,
"moduleName": "libsystem_c.dylib",
"name": "strstr"
}
Module.findExportByName(null, 'strstr')
"0x183cb81e8"
Module.getExportByName(null,'strstr')
"0x183cb81e8"
Process.findModuleByAddress("0x183cb81e8")
{
"base": "0x183cb6000",
"name": "libsystem_c.dylib",
"path": "/usr/lib/system/libsystem_c.dylib",
"size": 516096
}
a = Process.findModuleByName("Reachability")
a.enumerateExports()
....
{
"address": "0x102fab020",
"name": "ReachabilityVersionString",
"type": "variable"
},
{
"address": "0x102fab058",
"name": "ReachabilityVersionNumber",
"type": "variable"
}
....
...
..
frida -U -f funky-chicken.debugger-challenge --no-pause -q --eval 'var x={};Process.enumerateModulesSync().forEach(function(m){x[m.name] = Module.enumerateExportsSync(m.name)});' | grep -B 1 -A 1 task_threads
"address": "0x1c1c4645c",
"name": "task_threads",
"type": "function"frida -U -f funky-chicken.debugger-challenge --no-pause -q --eval 'var x={};Process.findModuleByAddress("0x1c1c4645c");'
{
"base": "0x1c1c2a000",
"name": "libsystem_kernel.dylib",
"path": "/usr/lib/system/libsystem_kernel.dylib",
"size": 200704
}[objc_playground]-> var a = ObjC.classes.NSString.stringWithString_("foo");
[objc_playground]-> a.superclass().toString()
"NSString"
[objc_playground]-> a.class().toString()
"NSTaggedPointerString"
// PASTE THIS CODE INTO THE FRIDA INTERFACE...
Interceptor.attach(ObjC.classes.NSTaggedPointerString['- isEqualToString:'].implementation, {
onEnter: function (args) {
var str = new ObjC.Object(ptr(args[2])).toString()
console.log('[+] Hooked NSTaggedPointerString[- isEqualToString:] ->' , str);
}
});
// TRIGGER YOUR INTERCEPTOR
[objc_playground_2]-> a.isEqualToString_("foo")
[+] Hooked NSTaggedPointerString[- isEqualToString:] -> foo
1 // TRUE
[objc_playground_2]-> a.isEqualToString_("bar")
[+] Hooked NSTaggedPointerString[- isEqualToString:] -> bar
0 // FALSE// frida -U -l open.js --no-pause -f com.yd.demoapp
// the below javascript code is the contents of open.js
var targetFunction = Module.findExportByName("libsystem_kernel.dylib", "open");
Interceptor.attach(targetFunction, {
onEnter: function (args) {
const path = Memory.readUtf8String(this.context.x0);
console.log("[+] " + path)
}
});try {
var targetFunctPtr = Module.findExportByName("YDAppModule", "$s9YDAppModule17ConfigC33publicKeyVerifyCertsSayypGvpfi");
if (targetFunctPtr == null) {
throw "[*] Target function not found";
}
Interceptor.attach(targetFunctPtr, {
onLeave: function(retval) {
var array = new ObjC.Object(retval);
console.log('[*]ObjC Class Type:\t' + array.$className);
return retval;
}
});
console.log("[*] publicKeyVerifyCertificates called ");
}
catch(err){
console.log("[!] Exception: " + err.message);
}# check it works
frida-trace --v
# excellent place to read about Flags
frida-trace --help
# spawn and NO trace
frida-trace -f objc_playground
# trace ObjC static Class Method
frida-trace -m "+[NSUUID UUID]" -U "Debug CrackMe"
# trace ObjC mulitple methods at once without a wildcard
frida-trace \
-m "+[NSURL URLWithString:]" \
-m "-[NSURL initWithString:]" \
-U \
-f "${BUNDLE_ID}"
# trace ObjC with wildcard
frida-trace \
-m "*[NSURL *]" \
-U \
-f "${BUNDLE_ID}"
# Trace entire Module. Bad idea!
frida-trace \
-I UIKit \
-U \
-f "${BUNDLE_ID}"
# Trace my own framework. Better idea
frida-trace \
-I FooFramework \
-U \
-f "${BUNDLE_ID}"
# trace C function on iOS
frida-trace -U -f "${BUNDLE_ID}"
frida-trace -m "*[*URLProtection* *]" -U -f com.robot.demo //
# useful methods
-m "*[NSURLSession* *didReceiveChallenge*]" # check whether https check delegate used
-m "*[*URLProtection* *] # for https challenge information
-m "*[NSURL absoluteString]"
-i "getaddrinfo"
-i "SSLSetSessionOption"
Edit the Frida-Trace auto-generated, template file.
onEnter: function (log, args, state) {
log("-[NSURLRequest initWithURL:" + args[2] + "]");
var str = new ObjC.Object(ptr(args[2])).toString()
console.log('[*] ' , str);
},frida-ps -Uai // get your bundle ID
frida --codeshare mrmacete/objc-method-observer -U -f $BUNDLE_ID
[+] At the Frida prompt...
// Method isJailbroken
observeSomething('*[* isJail*]')
// Observe String compares
observeSomething('*[* isEqualToString*]');
// A Class ( ObjC ) or Module (Symbol ). The first asterix indicates it can be eith Instance or Class method
observeSomething('*[ABC* *]');
// Watch Cookies
observeSomething('-[WKWebsiteDataStore httpCookieStore]');
observeSomething('-[WKWebAllowDenyPolicyListener *]');
// dump the URL to hit
observeSomething('-[WKWebView loadRequest:]');
// you get all HTML, js, css, etc
observeSomething('-[WKWebView load*]');
// Read the entire request
observeSomething('-[WKWebView loadHTMLString:baseURL:]')
// Check for a custom UserAgent
observeSomething('-[WKWebView *Agent]');
# Rename Frida process
bash -c "exec -a YDFooBar ./frida-server &"
# Set Frida-Server on host to a specific interface and port
frida-server -l 0.0.0.0:19999 &
# Call Frida-server from Host
frida-ps -ai -H 192.168.0.38:19999
# Trace on custom port
frida-trace -m "*[NSURLSession* *didReceiveChallenge*]" -H 192.168.0.38:19999 -f $BUNDLE_ID/private/var/mobile/Containers/Data/Application/<app guid, given at install time>/Library/Cookies/Cookies.binarycookiesscp -P 2222 root@localhost:/private/var/mobile/Containers/Data/Application/<App GUID>/Library/Cookies/Cookies.binarycookies cookies.binThe original BinaryCookieReader script out of date ( still Python 2 only ):
# install cookie file parser
pip3 install binary-cookies-parser
# read file
bcparser cookies.bin# script from https://github.com/interference-security/frida-scripts/blob/master/iOS/show_binarycookies.js
frida -U -p 1990 -l show_binarycookies.js
$) ps -ax | grep -i WebKit.Networking
29163 ?? <longPath>/.../com.apple.WebKit.Networking
$) frida --codeshare mrmacete/objc-method-observer -p 29163
[PID::29163]-> %resume
[PID::29163]-> observeSomething('*[* cookiesWithResponseHeaderFields:forURL:]');
######################################
+[NSHTTPCookie cookiesWithResponseHeaderFields:forURL:]
cookiesWithResponseHeaderFields: {
"Set-Cookie" = "EuConsent=<removed for brevity>; path=/; expires=Sat, 16 Nov 2019 14:51:01 GMT;";
} (__NSSingleEntryDictionaryI)
forURL: https://uk.yahoo.com/?p=us&guccounter=1 (NSURL)
RET: (
"<NSHTTPCookie
version:0
name:EuConsent
value:<removed for brevity>
expiresDate:'2019-11-16 14:51:01 +0000'
created:'2019-11-15 14:51:01 +0000'
sessionOnly:FALSE
domain:yahoo.com
partition:none
sameSite:none
path:/
isSecure:FALSE
path:"/" isSecure:FALSE>"
)WARNING: only change the minimum iOS version of a specific app's plist and not for the entire device. Things start to break - like calls into C libraries - when you change the device's read-only iOS version.
ssh onto device
root# cd /System/Library/CoreServices/
root# cat SystemVersion.plist
root# nano SystemVersion.plist
EDIT THE VALUE. KEEP THE OLD VALUE!https://developer.apple.com/library/archive/qa/qa1964/_index.html
otool -l -arch all my_framework | grep __llvm_prf
nm -m -arch all my_app | grep gcov