34

Make debugserver great again (on iOS 15/16)

Make debugserver great again on iOS15/16 Jailbroken Device.

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 device
  • DeveloperDiskImage.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:

  1. 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.

  1. 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.)