A (long) evening with Mobile_Obliterator
and a look into iOS entitlements
Jonathan Levin, http://www.newosxbook.com/ - 12/31/13
Part of what I do is reverse engineering. Most of the iOS-related information in the book (at least, those I was allowed to publish) was gleaned by reverse engineering by disassembling binaries, and occasionally debugging them. This is also why I ended up writing JTool, which started as a simple Mach-O header dump utility, but took a life on its own and ended up with its own disassembler.
When I noticed
/usr/libexec/mobile_obliterator, I couldn't help but be intrigued. It's a small executable (60-70k) with a big name. It's the binary responsible for Apple's "remote device wipe" feature. Not an overly sophisticated one (as I'll demostrate here) but it makes for a great demonstration of a virtually undocumented feature of iOS (which I admit I had to only scratch the surface of in the book) - Entitlements. With nothing else to do on a 13 hour flight in economy, this article is long overdue. Oh, and - happy new year :-)
Why should you care? (Target Audience)If you want to know how entitlements work at the low-level, this should prove to be a good read. If you want to see examples of using JTool (some are in the forum), there are some here. You can follow along if you have the iOS filesystem image. It's encrypted, but you don't have to read the book to get around that..
The mobile_obliterator has been rewritten in iOS7 to use XPC. We'll get to that, too.
iOS devices have a wipe feature. This is a feature of last resort if your i-Device falls into the wrong hands. In those cases, you don't want personal data, and possibly confidential data, exposed. "Wiping" involves nuking all the user's data in
/var/mobile - which is intentionally a separate partition. The responsible entity for starting mobile_obliterator is SpringBoard - in many ways the core binary in iOS, responsible for the familiar GUI, and more. In practice, however, it is launchd which starts mobile_obliterator, the same way it starts all other processes in iOS. This can be seen in mobile_obliterator's property list file:
From the plist we see that the binary (
/usr/libexec/mobile_obliterator) is associated with a Mach service -
iOS has a private framework - MobileObliteration.framework. While this framework, like most others, is in the shared library cache, a symbolicated version of it exists in the iOS SDK. Disassembling it reveals a fairly simple library:
Thus, we have the function of
perform_command, and three exported commands: Mobile_Obliterate, and its reboot and Synchronous variants. The library is easy to disassemble, and can be further decompiled. What follows is JTool's decompilation, with further annotation:
The MobileObliteration.framework, therefore, is in effect a simple XPC client of mobile_obliterator, using MOXPC calls (from the
MobileSystemServices private framework). Incidentally, XPC itself isn't much more than wrappers over the venerable Mach bootstrap server functionality (still maintained by
launchd). The various exported functions fall through to
perform_command, with the only subtle difference being the setting of additional keys in the CFDictionary passed to perform_command (Specifically,
Mobile_Obliterate_Reboot sets the
ObliterationRebootType key to
Mobile_Obliterate_Synchronous sets the
SyncType key to
On the other side of launchd, mobile_obliterator wakes up, and initializes the XPC support. After opening /dev/console, it logs a startup message, and calls
MOXPCTransportOpen to claim the
com.apple.mobile.obliteration channel (as is rightfully his, by the launchdaemon plist), and calls MOXPCTransportSetMessageHandler to install a callback function for incoming messages, before going into a
dispatch_main message loop. When messages come in, mobile_obliterator inspects the CFDictionary for the above mentioned keys, to determine if a reboot or a synchronous obliteration was requested. Once it figures out what the client requested, it can proceed to obliterate the device
But... not so fast! While the mobile obliterator aims to please, it must exercise some discretion. Specifically, it can't just allow any client to connect. It therefore requires clients to have a special entitlement to allow its services to be invoked. Entitlements form the crux of the iOS permission model. Very similar to Android's Dalvik-level permissions, they implement a declarative security model wherein a given application can specify to iOS it requires special capabilities. Servers can then request iOS to verify a client has the required entitlements before granting it access to the service.
A client specifies its entitlements in its binary - they are embedded in the code signature, as an entitlement blob (
kSecCodeMagicEntitlement = 0xfade7171). Doing so allows Apple to ensure they are not misused: Apple is the sole provider of code signatures in iOS, so apps can't simply request entitlements without Apple's App Review process flagging them. Once a binary is code signed by Apple, even its own developer can no longer modify it in any way. You can use jtool to view both the code signature and the entitlements - the latter is such common usage I added a separate --ent option for it). Using jtool on Springboard reveals plenty of entitlements:
During the Mach-O object loading, the kernel loads the code signature (LC_CODE_SIGNATURE load command) using a dedicated function (
bsd/kern/mach_loader.c). The function loads the code signature and stores it in the unified buffer cache, as a blob. Because it is in kernel memory, the code signature (once verified) can be fully trusted. In other words, the binary does not have a way to access its own entitlements (at least not from user mode)
Servers can validate that a client holds the entitlement by using the public
Security.Framework. Turning once more to
mobile_obliterator, we see (full disassembly given; also possible to skim through annotations):
The obliteration process itself isn't terribly complicated (though it's possible to walk through it with jtool as well). On a side note, iOS 6's obliterator (pre-XPC) had an easter egg in outputting ""And you will know my name is the Lord when I lay my vengeance upon thee."
Security.Framework SecTask APIs (visible in the OS X SDK Headers (
SecTask.h), but not in those of iOS) obtain the entitlements by calling on Apple's proprietary system calls in XNU dealing with code signing -
csops (#169) and
csops_audittoken (#170). These system calls are undocumented, but their implementation in the XNU open sources (bsd/kern/kern_proc.c) is straightforward to read. The relevant code is shown below:
The heart of the function is
bsd/kern/ubc_subr.c). This function locates the entitlements blob in the bigger code signing super blob , and returns it to the caller. The blob itself is just the embedded plist. More information on the code signing blob itself can be found in the open source portion of Securitya (specifically,
./libsecurity_codesigning/lib/cscdefs.c and .h, so I won't get into it here.
This article, while detailing more than the book has on entitlements, is still only scraping the surface. I've so far counted well over 100 entitlements, from miscellaneous providers ranging from SpringBoard to the various servers in /usr/libexec. Apple's own apps (in /Applications) make heavy use of entitlements (you might want to try
jtool --ent on some. For third party developers, the only entitlements Apple allows are those in XCode, which are limited to iCloud access and a few other paltry features. I'm working on a list of entitlements, providers and consumers. This will take more time, though
iOS also has a daemon -
tccd (part of the
TCC.framework) which relies on entitlements (and provides an example of a case where the entitlement value can be an array, as in
com.apple.private.tcc.allow. TCC (also present in OS X) is responsible for the UIAlert which pops up every now and then for "application XXX would like to access your photos/contacts/etc". Time (and reader interest) permitting, I'll cover this in a future article.
CS_ENTITLEMENTS_BLOB, as part of the code signature
SecTask APIs, in turn, call on csops (syscall #169) or csops_audittoken (#170)