Background
After more than a year of difficulty, iOS 15/16 jailbreak finally has two POCs, although still based on the checkm8 vulnerability and various new patches, but having something is better than nothing. However, for post-jailbreak(referred to as JB) booth, toolchains and debugservers need to be patched again. So on a chilly afternoon before the new year, I bring you a guide on how to make debugserver great again. As for why not use the one built-in to Xcode? It must be because you are only debugging your own code, or have not experienced the extreme pressure that the debug bundle is totally fine, while the App Store version is crashing on a single click. (of course, debugging competing apps is even more challenging...)
Toolchain
- USB-A cable, not USB-C (mention below)
https://github.com/palera1n/palera1n
,and A10/A11 deviceDeveloperDiskImage.dmg
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/$Version/DeveloperDiskImage.dmg
$Version depends on your env.
Step 1. Finish Jailbreaking
Follow https://github.com/palera1n/palera1n
all necessary steps.
Why do we have to use a USB-A cable instead of a USB-C cable? At first, I used a USB-C cable and found that the device would automatically restart after about 1 minute after entering DFU mode, making it impossible to connect during the boot ramDisk stage. After switching to a USB-A cable, the issue was resolved, and there were similar feedback on the Internet.
Step 2. Prepare ENV
We need to elaborate on this.
Before iOS 12, debugserver's capabilities could be obtained directly by installing debugserver from a one-click installation of Cydia source. However, it's not possible anymore, for two reasons:
- The old debugserver was based on clang-10, so many dependencies are no longer correct
Reason: tried: '/usr/lib/llvm-10/lib/libclang-cpp.dylib' (no such file), '/usr/local/lib/libclang-cpp.dylib' (no such file), '/usr/lib/libclang-cpp.dylib' (no such file)
The smart ones may ask, can't I just install the llvm+clang-10 toolchain from the source? Unfortunately, as of New Year's Eve in 2023, all of the clang-10 sources have not been ported over yet. If you want to do it yourself, you will need to recompile a lot of dependencies, which is time-consuming and laborious, and is not recommended.
Can we just put the llvm-14 related dylibs into a specified directory? In theory, it is possible, but using different versions of llvm+clang may encounter various strange runtime issues later on, so it is also not recommended.
- The old debugserver lacks necessary permission declarations, and in any case, it needs to be re-signed before it can run. Since it needs to be re-signed, why not just use the new debugserver directly?
Fix dependency
So, after you obtain a DeveloperDiskImage from your Xcode, copy out the debugserver and scp it to your iPhone. If you try to execute it directly, you will be prompted with the following message.
dyld[420]: Library not loaded: /System/Library/PrivateFrameworks/DVTInstrumentsFoundation.framework/DVTInstrumentsFoundation
Referenced from: /private/var/root/debugserver
Reason: tried: '/System/Library/PrivateFrameworks/DVTInstrumentsFoundation.framework/DVTInstrumentsFoundation' (no such file), '/System/Library/Frameworks/DVTInstrumentsFoundation.framework/DVTInstrumentsFoundation' (no such file)
running otool -L debugserver
we do see a new dependency DVTInstrumentsFoundation
.
This problem is easy to solve, just find a way to get one. You can get a copy of it from the Xcode folder. Also, due to the introduction of Apple Silicon, these frameworks are now fat binaries that include the arm64 architecture. You can directly scp them to the device and place them in the specified path. However, when you try to run debugserver again, you may encounter the following error:
Found xxx/DVTInstrumentsFoundation.framework/DVTInstrumentsFoundation, but is (ios-sim), need (ios)
WTH? Apple has already anticipated this step? They even distinguishes between ios-sim
and ios
. I was thinking about how to get a copy of iOS from somewhere else, but after second thought, why not search on the phone?
And indeed, I found it: /Developer/Library/PrivateFrameworks/DVTInstrumentsFoundation.framework
However, since the path was different, I was wondering if it was a bug from Apple. So, naturally, I tried to create a symlink:
(if you see complains like APFS is readonly, make sure you run mount -o rw /
)
Make sure you have the correct symlink:
lrwxr-xr-x 1 root wheel 71 Jan 20 10:22 /System/Library/PrivateFrameworks/DVTInstrumentsFoundation.framework -> /Developer/Library/PrivateFrameworks/DVTInstrumentsFoundation.framework/
And try again:
iPhone:~ root# ./debugserver
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-1316.2.4.16
for arm64.
Usage:
debugserver host:port [program-name program-arg1 program-arg2 ...]
debugserver /path/file [program-name program-arg1 program-arg2 ...]
debugserver host:port --attach=<pid>
debugserver /path/file --attach=<pid>
debugserver host:port --attach=<process_name>
debugserver /path/file --attach=<process_name>
Voilà!
Step 3. Resign the entitlements
The entitlements of the debugserver in DeveloperDiskImage are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>com.apple.backboardd.launchapplications</key>
<true/>
<key>com.apple.backboardd.debugapplications</key>
<true/>
<key>com.apple.frontboard.launchapplications</key>
<true/>
<key>com.apple.frontboard.debugapplications</key>
<true/>
<key>seatbelt-profiles</key>
<array>
<string>debugserver</string>
</array>
<key>com.apple.private.logging.diagnostic</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.private.memorystatus</key>
<true/>
<key>com.apple.private.cs.debugger</key>
<true/>
</dict>
</plist>
If we try to use this one to attach to the process, we will see:
error 10:36:28.917336+0800 kernel Sandbox: debugserver(573) deny(1) process-fork
default 10:36:28.917583+0800 debugserver 1 +0.000000 sec [023d/0103]: error: ::posix_spawnp ( pid => 0, path = '/Applications/AppStore.app/AppStore', file_actions = 0x16f55d8e0, attr = 0x16f55d8f8, argv = 0x10120a930, envp = 0x10120ab80 ) err = Operation not permitted (0x00000001)
...
error 10:37:12.577979+0800 kernel Sandbox: debugserver(579) deny(1) network-bind*:1234
default 10:37:12.578212+0800 debugserver 1 +0.000000 sec [0243/0103]: ::listen or ::bind failed err = 0x00000000
This is because it's a trap. It has seatbelt-profiles
and is lacking platform-application
And some juice as always:
<key>com.apple.system-task-ports</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>proc_info-allow</key>
<true/>
These entitlements allow our debugserver to obtain "root" level ability to attach any process we need. Otherwise, we can only attach processes with debug level similar to Xcode, and cannot attach other processes.
Finally, resign the debugserver with below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>platform-application</key>
<true/>
<key>com.apple.springboard.launchapplications</key>
<true/>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>com.apple.backboardd.launchapplications</key>
<true/>
<key>com.apple.backboardd.debugapplications</key>
<true/>
<key>com.apple.frontboard.launchapplications</key>
<true/>
<key>com.apple.frontboard.debugapplications</key>
<true/>
<key>com.apple.private.logging.diagnostic</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.private.memorystatus</key>
<true/>
<key>com.apple.private.cs.debugger</key>
<true/>
<key>com.apple.system-task-ports</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>proc_info-allow</key>
<true/>
</dict>
</plist>
And then:
debugserver '172.17.31.37:1234' -a /Applications/AppStore.app/AppStore
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-1316.2.4.16
for arm64.
Attaching to process 414...
Listening to port 1234 for a connection from 172.17.31.37...
Waiting for debugger instructions for process 414.
On lldb side:
process connect connect://172.17.25.100:1234
Process 414 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00000001ba520b10 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x1ba520b10 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x1ba520b14 <+0>: mov x16, #-0x20
0x1ba520b18 <+4>: svc #0x80
0x1ba520b1c <+8>: ret
Target 0: (AppStore) stopped.
Further reading
For people that are curious, read below
The Apple Sandbox by Dionysus Blazakis
Hack in the (sand)Box (The Apple Sandbox - five years later) by Jonathan Levin (This is a true master. He wrote a large number of undocumented stuff about Apple's OS.)