Introduction
AppleMobileFileIntegrity(AMFI) is a core part of macOS and iOS security. And is by extension the bane of many a jailbreakers existence. It ensures the integrity of the code running on the OS and is the brains behind XNU's code signature verification.
AMFI consists of a KEXT(AppleMobileFileIntegrity.kext) and a usermode daemon(amfid). The KEXT by definition operates in kernel mode. Recall that AAPL fully controls ALL system binaries on iOS. This allowed them to compile a list of the individual hashes of these binaries and stash them in the kernel - more specifically in a area referred to as AMFI's Trust Cache. As a side note these pseudo signatures are referred to as ad-hoc. Validation is therefore a simple lookup of the hash in the Trust Cache. For third party apps however, things are a bit different. As in such instances AMFI calls up to it's partner in crime amfid the user-mode daemon for validation.
This series of posts will look at both AppleMobileFileIntegrity.kext and amfid. Let's begin by discussing the Mandatory Access Control Framework(MACF) a key component of APPL's ecosystem.
Background (MACF)
"The Mandatory Access Control Framework (MACF) is the substrate on top of which all of Apple's security, both MacOS and iOS, is implemented. By implementing a rich set of callouts for every user-controllable aspect of kernel functionality - system calls and Mach traps alike - it allows interested kernel components to enforce any set of rules - a policy - desired." - (Jonathan Levin, *OS Internals Volume III, p.45)
Of note is the fact that this a framework and the logic is implemented in kernel extensions(KEXTs) which must first register their intention with the framework for any number of operations the framework intercepts. MACF will then call out to these extensions aka policies and allow them to do their thing. The general flow is as follows, let's say some process wants to perform a system call or mach trap:
- The corresponding function in the kernel is called(sysent/mac_trap_table -> syscall/trap #)
- This function reaches out to the MACF and basically asks "did any policy modules request to hook this functionality?"
- MACF then says "hold my beer, and calls out to the interested policies"
- The interested policy then says "ok dude you want to perform this operation...you're good to go....or nope not today"
- Now this is where it gets interesting, sometimes the policy module has a partner in crime operating in user mode and therefore has to make an up call. Enter in amfid.
Enumerating policy clients on macOS |
Now because this blog series is about AMFI, the KEXT we are interested in is of course AppleMobileFileIntegrity.kext. If you are wondering what a KEXT is have no fear I got you. XNU by itself cannot provide all required functionality and so you need kernel extensions for things like drivers and plugins. In windows parlance a KEXT would be the equivalent of a driver (.sys). More importantly, KEXTs are also just Mach-O Bundles which it means it can be sliced and diced using a myriad of available tools.
The KEXT directory contains an Info.plist file that just has some metadata. Of note is the CFBundleExecutable key which as the name implies contains the name of the binary that will be executed.
Info.plist snippet |
KEXT layout |
Viewing KEXTs with KextViewr |
So while getting the above information - policy clients - is straight forward on macOS, because the KEXTs are prelinked in iOS we have to extract them directly from the kernel. Before we do that we need to obtain said kernel. There are number of options available and of those I usually use one of the following:
- kdump from the iOS Kernel Utilites for pre iOS 10.x kernels. Keep in mind if you use this method certain parts of the kernel will be jettisoned (so there goes the symbol table.......).
- Extract from respective Firmware (starting with iOS 10 the kernel is no longer encrypted...thank the kernel gods)
Enumerating policy clients on iOS |
Enumerating callouts
Ok so at this this point we know that AppleMobileFileIntegrity.kext is a policy KEXT. But what is exactly is a policy and what does it look like from a reverse engineering perspective. Simply put, a policy is just a set rules that gets applied on full or partial kernel operation callouts. The interested KEXT first defines a mac_policy_conf structure and then registers it with MACF through a call to mac_policy_register. The mac_policy_conf structure is of paramount importance and can be found in /security/mac_policy.h.
mac_policy_conf structure |
mac_policy_ops structure snippet |
Listing SEGMENTS on iOS 9.3.3 |
Dumping __DATA.__const on iOS 9.3.3 |
mac_policy_ops function pointers in iOS 9.3.3 |
As a casualty of the war against jailbreakers this doesn't work on iOS 10.x. Instead the initialization is done in code. So alas, the above steps will not work and a different approach is needed. Extracting the AppleMobileFileIntegrity.kext from an iOS 10 3.x kernel, opening it up in IDA and searching for references to mac_policy_register leads to:
AMFI KEXT iOS 10.x |
mac_policy_register |
A call to ARMs
On ARM architectures, function arguments and return values are passed/stored in registers X0-X7. So in this case register X0 should hold the mac_policy_conf struct pointer. Let's remind ourselves of what this struct looks like:
On ARM architectures, function arguments and return values are passed/stored in registers X0-X7. So in this case register X0 should hold the mac_policy_conf struct pointer. Let's remind ourselves of what this struct looks like:
mac_policy_struct again |
ARMed (pun intended) with this knowledge, the previous disassembly should start making sense and can be annotated as follows:
# mpc - mac_policy_conf
----------------------------------------------------------------------------------------------------
# mpc->mpc_name="AMFI"
__text:FFFFFFF00644F9EC STR X9, [X8,#(qword_FFFFFFF006E68650 - 0xFFFFFFF006E68530)]
__text:FFFFFFF00644F9F0 ADRP X9, #aAmfi_0@PAGE ; "AMFI"
__text:FFFFFFF00644F9F4 ADD X9, X9, #aAmfi_0@PAGEOFF ; "AMFI"
__text:FFFFFFF00644F9F8 FMOV D0, X9
# mpc=mpc_fullname="Apple Mobile File Integrity"
__text:FFFFFFF00644F9FC ADRP X9, #aAppleMobileFil@PAGE ; "Apple Mobile File Integrity"
__text:FFFFFFF00644FA00 ADD X9, X9, #aAppleMobileFil@PAGEOFF ; "Apple Mobile File Integrity"
__text:FFFFFFF00644FA04 INS V0.D[1], X9
# Set mpc->name and mpc->fullname
__text:FFFFFFF00644FA08 ADRP X0, #xmmword_FFFFFFF006E68FA8@PAGE
__text:FFFFFFF00644FA0C ADD X0, X0, #xmmword_FFFFFFF006E68FA8@PAGEOFF
__text:FFFFFFF00644FA10 STR Q0, [X0]
# Set mpc->mpc_labelname=""
__text:FFFFFFF00644FA14 ADRP X9, #off_FFFFFFF006E695D8@PAGE
__text:FFFFFFF00644FA18 ADD X9, X9, #off_FFFFFFF006E695D8@PAGEOFF
__text:FFFFFFF00644FA1C STR X9, [X0,#(qword_FFFFFFF006E68FB8 - 0xFFFFFFF006E68FA8)]
# Set mpc->mpc_labelname_count=1
__text:FFFFFFF00644FA20 MOV W9, #1
__text:FFFFFFF00644FA24 STR W9, [X0,#(dword_FFFFFFF006E68FC0 - 0xFFFFFFF006E68FA8)]
# X8 has mac_policy_ops
__text:FFFFFFF00644FA28 STR X8, [X0,#(qword_FFFFFFF006E68FC8 - 0xFFFFFFF006E68FA8)]
# Set mpc->mpc_loadtime_flags=0 and other fields
__text:FFFFFFF00644FA2C STR WZR, [X0,#(dword_FFFFFFF006E68FD0 - 0xFFFFFFF006E68FA8)]
__text:FFFFFFF00644FA30 ADRP X8, #dword_FFFFFFF00760B2E0@PAGE
__text:FFFFFFF00644FA34 ADD X8, X8, #dword_FFFFFFF00760B2E0@PAGEOFF
__text:FFFFFFF00644FA38 STR X8, [X0,#(qword_FFFFFFF006E68FD8 - 0xFFFFFFF006E68FA8)]
__text:FFFFFFF00644FA3C STR WZR, [X0,#(dword_FFFFFFF006E68FE0 - 0xFFFFFFF006E68FA8)]
# call mac_policy_register - sub_FFFFFFF0064514D8
__text:FFFFFFF00644FA40 ADRP X1, #unk_FFFFFFF006E6852C@PAGE
__text:FFFFFFF00644FA44 ADD X1, X1, #unk_FFFFFFF006E6852C@PAGEOFF
__text:FFFFFFF00644FA48 MOV X2, #0
__text:FFFFFFF00644FA4C BL sub_FFFFFFF0064514D8
__text:FFFFFFF00644FA50 CBZ W0, loc_FFFFFFF00644FA78
And so in a nutshell this is how mpc_ops is populated. Where the struct is now stored in register X8. Let's look at an example of an assignment.
mpo_proc_check_get_task
The hook kicks in during the acquisition of task port rights. And you should keep in mind that if you obtain a task's port you own the task. So the hook is an important one. Opening the KEXT in IDA and searching for get-task-allow reveals the following:
And after navigating through the results we eventually end up at the highlighted sub_FFFFFFF006450D50 function. The hook first checks if the target task has the get-task-allow entitlement at 0xFFFFFFF006450D7C:
If that fails, the hook then checks for the task_for_pid_allow entitlement at 0xFFFFFFF006450D9C. This is the "god-mode" entitlement as it allows the entitled task to access any other task via the task_for_pid Mach trap. Side-note: If you have ever performed debugging of iOS apps using debugserver then you would be familiar with this entitlement as you first have to sign the binary and grant the binary the entitlement.
Likewise if you are doing any work on the kernel(pid 0) - like dumping a running kernel - you would call
Ok so now that we understand hook, how does it get assigned? Well if we search for cross references in IDA we land in a section just above the snippet we annotated earlier on:
And if we press k key in IDA at offset 0xFFFFFFF00644F9C4 we see *(X8 + arg_4F0) = X9 where #arg_4F0 as defined by IDA is hex 0x500 which translates to offset 160 in the mac_policy_ops struct which is mpo_proc_check_get_task.
Recall that register X8 holds the mac_policy_ops struct. And looking at the disassembly you see several other similar assignments.
Wrap Up
We have come to the end of part 1 in the series. To recap:
mpo_proc_check_get_task
The hook kicks in during the acquisition of task port rights. And you should keep in mind that if you obtain a task's port you own the task. So the hook is an important one. Opening the KEXT in IDA and searching for get-task-allow reveals the following:
get-task-allow search results |
get-task-allow check |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
<key>run-unsigned-code</key>
<true/>
</dict>
</plist>
Likewise if you are doing any work on the kernel(pid 0) - like dumping a running kernel - you would call
ret = task_for_pid(mach_task_self(), 0, &kernel_task); // from ios-kern-utils kdump libkern.c
assuming of course that the jailbreak supports it. Going back to the hook and picking up where we left off if the calling task doesn't have the task_for_pid_allow entitlement then we see a call to sub_FFFFFFF006451014 which does a final check for unrestricted debugging:final check for unrestricted debugging |
mpo_proc_check_get_task hook initialization |
__text:FFFFFFF00644F9B8 0A 09 04 F9 STR X10, [X8,#(qword_FFFFFFF006E68D40 - 0xFFFFFFF006E68530)]
__text:FFFFFFF00644F9BC 09 00 00 B0 ADRP X9, #sub_FFFFFFF006450D50@PAGE
__text:FFFFFFF00644F9C0 29 41 35 91 ADD X9, X9, #sub_FFFFFFF006450D50@PAGEOFF
__text:FFFFFFF00644F9C4 09 81 02 F9 STR X9, [X8,#arg_4F0]
Recall that register X8 holds the mac_policy_ops struct. And looking at the disassembly you see several other similar assignments.
Wrap Up
We have come to the end of part 1 in the series. To recap:
- We introduced MACF
- Learned a little about KEXTs and how they relate to MACF
- Got introduced to Policies in the context of MACF
- Learned to identify policies on both macOS and iOS
- Saw that AppleMobileFileIntegrity.KEXT is a policy module
- Delved a little into policy registration to extract and identify callback functions
In the next post we will look at little more into AppleMobileFileIntegrity.KEXT and how it communicates with it's user-mode partner in crime amfid. I hope you found this helpful. Also if you find errors please point them out in the comments. Like I said I am learning as I go along.
Happy hacking errrrm......reversing.....mi amigos !!
Happy hacking errrrm......reversing.....mi amigos !!
Resources:
- Monitoring Process Creation via the Kernel (Part I) - for a practical example of writing KEXT's
- MacOS and iOS Internals, Volume III: Security & Insecurity
- jtool
- joker
- Kextvwr
- *OS Internal forum
- Apple Sources
Reversing Apple Mobile File Integrity (AMFI) involves analyzing and understanding the mechanisms used by Apple's operating systems. Godaddy Coupon Code It enforce code signing and file integrity.
ReplyDeleteReversing AppleMobileFileIntegrity was a challenging task, but the online community's support made it possible. In my moment of triumph, I decided to treat myself. Visiting the local Organic Store, I found solace in their range of natural products, making my achievement even sweeter. It's incredible how even in tech challenges, one can find balance through mindful choices at the organic store.
ReplyDeleteDelve into the intricacies of reversing AppleMobileFileIntegrity (AMFI) in this insightful Part 1. Uncover the complexities of iOS security and learn essential techniques. While navigating the digital realm, explore natural remedies like oil pulling and activated charcoal to effortlessly rRemove Black Stains From Teeth Naturally, ensuring optimal oral health.
ReplyDelete