Make Debugging Great Again (on Liber* Jailbroken devices)

A quick tutorial on how to get debugserver working without crashing any programs

Jonathan Levin, (@Morpheus______), - 02/28/2018


Apple's hardening of iOS 11.x is formidable, but no match if you have kernel read/write primitives. The QiLin toolkit gets around most of them by directly patching the various protections in kernel memory.

Getting debugging to work again is a bit tricky, owing to intercepting process startup. There are two different approaches to this problem. One, which I think is unwise at best, is hooking launchd with all sorts of injected code. The other, is a set of CLIs, which - a bit inconvenient - are certainly far better in terms of maintaining system stability and zero disruption.


The first challenge is getting debugserver over the hurdle of task_conversion_eval. I described this problem in MOXiI Volume I and III (particularly, in the QiLin writeup). Even with task_for_pid-allow, you still need to be marked as a platform binary to be able to get the target task port of some other process, if its binary, too, is platform. Note, this is platform binary, and not platform-application (which you need to begin with in order to execute past the sandbox's platform profile).

I tweeted the following image about this:

As you can see, before platformizing, debugserver is "unable to start the exception thread". This is because it needs the victim task's exception ports, which it can't get for lack of being a platform binary. Along comes platformize, which is a super simple binary to compile whose source and binary you can get on the QiLin page. What platformize does is call QiLin's spawnAndPlatformize, which - once in possession of the kernel task port - can access kernel memory and patch the necessary bit (TF_PLATFORM in the task's tf_flags, at offset 0x3a8 if I recall correctly), and that's that. It spawns debugserver suspended, patches, and lets it continue.

NOTE THAT debugserver needs to be resigned with the following entitlements:
  • platform-application: so it can run
  • task_for_pid-allow: so it can get the task port
  • so it can get Apple binary task ports too
It is also a good idea to remove the seatbelt-profiles from debugserver. For the impatient, that means the last entitlements of your debugserver should look like this:

Bypassing Code signing..

Doing the above will get you debugging and the task port, but you'll still see a peculiar behavior - your target will die as soon as you let it run with a break point, even if the breakpoint itself was not hit. Observe the following:

root@Phontifex-3 (~) # lldb -p 9295                                               
(lldb) process attach --pid 9295
Process 9295 stopped
* thread #1: tid = 0x2f0658, 0x00000001826b8170 CoreFoundation`__CFStrHashEightBit2, queue = '', stop reason = signal SIGSTOP
    frame #0: 0x00000001826b8170 CoreFoundation`__CFStrHashEightBit2
->  0x1826b8170 <+0>:  movz   w8, #0x406, lsl #16
    0x1826b8174 <+4>:  movk   w8, #0x401
    0x1826b8178 <+8>:  movz   w9, #0x103, lsl #16
    0x1826b817c <+12>: movk   w9, #0x301

Executable module set to "/var/containers/Bundle/Application/3F21FB14-547F-4CA0-AE28-CD89A0471619/".
Architecture set to: arm64-apple-ios.
(lldb) b xpc_connection_send_message_with_reply_sync
Breakpoint 1: where = libxpc.dylib`xpc_connection_send_message_with_reply_sync, address = 0x0000000182363e2c
(lldb) c
Process 9295 resuming
Process 9295 stopped
* thread #1: tid = 0x2f0658, 0x0000000182361ee0 libxpc.dylib`_xpc_connection_reply_callout + 60, queue = '', stop reason = EXC_BAD_ACCESS (code=50, address=0x182361ee0)
    frame #0: 0x0000000182361ee0 libxpc.dylib`_xpc_connection_reply_callout + 60
->  0x182361ee0 <+60>: add    w0, w21, #1               ; =1 
    0x182361ee4 <+64>: mov    x1, x19
    0x182361ee8 <+68>: bl     0x182367a60               ; _xpc_ktrace_pid1
    0x182361eec <+72>: ldp    x29, x30, [sp, #32]

(lldb) c
Process 9295 resuming
Process 9295 exited with status = 0 (0x00000000) Terminated due to code signing error

You might want to take a minute to reflect on this behavior . Note that EXC_BAD_ACCESS was generated at 0x182361ee0, whereas the breakpoint was at 0x182363e2c. So.. why? (answer below)

run-unsigned-code was supposed to take care of this, and you can also fix this in other ways, but the simplest method is to flip GET_TASK_ALLOW (0x4) in the code signing flags. So a third QiLin tool , csflags now does this on any pid of your choice:

root@Phontifex-3 (~) # ps -ef | grep News$                                          
  501  9320     1   0 11:21AM ??         0:04.50 /var/containers/Bundle/Application/3F21FB14-547F-4CA0-AE28-CD89A0471619/
root@Phontifex-3 (~) # /jb/csflags 9320 0x4                                         
2018-05-09 11:22:12.587 csflags[9335:3085328] STATUS: Got 64-bit kernel. Great
2018-05-09 11:22:12.594 csflags[9335:3085328] STATUS: Loaded The QiLin Toolkit for Darwin 17.2.0 Darwin Kernel Version 17.2.0: Fri Sep 29 18:14:50 PDT 2017; root:xnu-4570.20.62~4/RELEASE_ARM64_T8010 iPhone9,1 - Phontifex-3

-- Current CS Flags of process (@0xffffffe0068ca738): 0x0
-- process CS Flags @0xffffffe0068ca738 set to  0x4 (RC: 0)
My blob is @0xffffffe003b49200
BLOB CS FLAGS: 0x3000024
BLOB CS FLAGS NOW: 0x3000024
RC: 0
root@Phontifex-3 (~) # lldb -p 9320                                            
(lldb) process attach --pid 9320
b xpc_connection_send_message_with_replProcess 9320 stopped
* thread #1: tid = 0x2f12a9, 0x00000001821f4bc4 libsystem_kernel.dylib`mach_msg_trap + 8, queue = '', stop reason = signal SIGSTOP
    frame #0: 0x00000001821f4bc4 libsystem_kernel.dylib`mach_msg_trap + 8
->  0x1821f4bc4 <+8>: ret    

    0x1821f4bc8 <+0>: movn   x16, #0x1f
    0x1821f4bcc <+4>: svc    #0x80
    0x1821f4bd0 <+8>: ret    

Executable module set to "/var/containers/Bundle/Application/3F21FB14-547F-4CA0-AE28-CD89A0471619/".
Architecture set to: arm64-apple-ios.
(lldb) b xpc_connection_send_message_with_reply
Breakpoint 1: where = libxpc.dylib`xpc_connection_send_message_with_reply, address = 0x0000000182360b34
(lldb) b xpc_connection_send_message_with_reply_sync
Breakpoint 2: where = libxpc.dylib`xpc_connection_send_message_with_reply_sync, address = 0x0000000182363e2c
(lldb) c
Process 9320 resuming
Process 9320 stopped
* thread #1: tid = 0x2f12a9, 0x0000000182360b34 libxpc.dylib`xpc_connection_send_message_with_reply, queue = '', stop reason = breakpoint 1.1
    frame #0: 0x0000000182360b34 libxpc.dylib`xpc_connection_send_message_with_reply
->  0x182360b34 <+0>:  stp    x26, x25, [sp, #-80]!
    0x182360b38 <+4>:  stp    x24, x23, [sp, #16]
    0x182360b3c <+8>:  stp    x22, x21, [sp, #32]
    0x182360b40 <+12>: stp    x20, x19, [sp, #48]
(lldb) po $x0
 { name =, listener = false, pid = 9088, euid = 501, egid = 501, asid = 0 }>
(lldb) c
Process 9320 resuming

And you have debugging again :-)

Another option: fake sign with get-task-allow.


  • Will this work on Electra? Don't know. Ask coolstar. I did the Liber* line of jailbreaks.
  • Is there going to be an update of Liber* for 11.2 or later iOS/TvOS versions? Not a public one. The ingrates and their flames I got over how bad a jailbreak is without Cydia made me hand over the baton to others.
  • Can QiLin theoretically be used to create an 11.2-3-4 jailbreak? Absolutely. Just plug in either the kernel_task exploit, or register read/writeKernelMem primitives with it, and it works out of box.
  • What about LiberWatchee? It's fine, thank you :-) But no, it won't be released.
  • When is Volume II coming out? After Darwin 18 does. Not too late to make requests on the website forum.
  • Advertisement

    After a wonderful training in NYC last week, there's another MOXiI training set for July 9th-13th - this time on the west coast. Also, if you're coming to BlackHat - we have the followup to MOXiI - applied *OS Security/Insecurity - in a special two day session. I'll show a lot more QiLin there.

    * - Answer - the breakpoint invalidated the entire page (0x182360000-0x182363fff, 16K on ARM64) because it overwrites the code.