Spitting Image:
MacOS & *OS Software Images
No software is perfect, and as Lubarsky's Law of Cybernetic Entomology states, "there's always one more bug". Bugs are proportional to the size of the code base, and in a code base as vast as that of an operating system, they are numerous and oftentimes severe. This is especially true when the bugs aren't just functional bugs, but security vulnerabilities. Software updates - once restricted just to major OS versions - have therefore become commonplace. MacOS and the *OS variants support sophisticated software update mechanisms, which can automatically locate, download and deploy software updates. The mechanisms are somewhat different between the OS variants.
MacOS and *OS software images are also of immense value to enthusiasts, researchers, and jailbreakers. Whether it's to look for ways to "tweak" the system, find upcoming features, uncover files of importance, or reverse binary diffs in updates in order to find vulnerabilities, the ability to download the filesystem image and extract files greatly simplifies the process and lessens the need to have a running instance of it on a device. That *OS software images are of importance was also noted by Apple, who chose to encrypt them with the device-class specific GID keys for most of iOS's lifetime. It was only around iOS 10 that this practice was ceased, whether by choice or mistake. Since then, all but the iBoot components are unencrypted.
This chapter (originally intended for Volume II, then ending up fitting better as an Appendix for Volume I) discusses software images in detail. Looking first at the MacOS realm, we peek behind the system installation process, and that of software updates. Then, *OS software images are explored, with the various files corresponding to the SoC's components' software. A special discussion of *OS's Over The Air (OTA) images follows, along with a demonstration of unpacking selected files from them. Finally, the aspects of "personalization" through APTickets, which lock an installation to a particular device given Apple's blessing, are discussed. Although started with *OS variants, personalization has spread to BridgeOS enabled devices, and will become standard for new Macs.
MacOS Software Images
Apple has moved the MacOS software images to the App Store, and new releases of MacOS can now be downloaded as any other app. The "app", in this case, is called
To enable the
Capability | |
---|---|
.private.EmbeddedOSInstallService | Update eOS image |
.private.diskmanagement.set-boot-device | Bless boot device |
.private.securityd.stash | Access |
.rootless.install | Bypass all restricted file protections |
.rootless.install.heritable | Bequeath restricted file protection bypass to children |
.rootless.restricted-nvram-variables | Full access to write NVRAM variables |
The Installer's
DMG | Name | Contents |
---|---|---|
Diagnostics Image | EFI diagnostics utility with drivers, in | |
System Image | Recovery file system + installer application | |
Payload | Packages to install |
The
Creating a standalone update disk image
A standalone update disk image can prove highly useful when installing MacOS onto a machine with no network access, or no prior MacOS image - for example, a virtual machine. With little work, the Resources
is a small binary, --volume
argument. An additional argument, --nointeraction
, can be specified to disable prompts and automate the process when scripting.
MacOS Software Updates
Installing a new version of MacOS is commonly an annual experience, but quite a few updates are published by Apple throughout the year. The user can manually check for software updates by using apple → About this Mac → Software Updates, which brings up
Software Update.app
The software updating responsibilities of MacOS are performed by the daemons contained in the
softwareupdated
: is the main daemon handling the updates. It is set to start periodically (via an XPC activity) three times a day, or when manually triggered through Darwin notifications. It claims thecom.apple.softwareupdated
XPC service, with which clients communicate over Objective-C classes and NSXPC messages.suhelperd
: handles various auxilliary tasks, such as client authorization, credentials, and file maintenance on clients' behalf. It claims thecom.apple.suhelperd
port, over which it provides the legacy (and unentitled) MIG subsystem 675309 to clients. The subsystem consists of 39 messages, which map toSoftwareUpdate.framework _suhelperd_client_XXX
exports. The framework also provides an Objective-C interface through theSUHelperProxy
class.softwareupdate_download_service
: Handles the downloaded updates. It is a relatively light wrapper over theSUDownloadServiceAgent
class. The daemon registers thecom.apple.softwareupdate_download_service
XPC, which clients interface with using theSUDownloadServiceAgent
objective-C class.softwareupdate_notify_agent
: Handles notifications when new software updates are detected. It starts through Darwin notifications (e.g.com.apple.SoftwareUpdate.[updates/OSXAvailable]
), and uses theSUAppStoreAgentNotifier
Objective-C class to relay these to theApp Store.app .softwareupdate_firstrun_tasks
: is run once on system startup, and picks up any installations which required a system reboot at some point.
# | _suhelperd_client export | SUHelperProxy selector |
---|---|---|
675309 | ..authorize_tool | authorizeTool:forRights: |
675310 | ..extend_rights |
|
675311 | ..disconnect_client | disconnect |
675312 | ..prepare_for_logout_and_install | prepareForLogoutAndInstall: |
675313 | ..prepare_loginwindow_for_postlogout_install
_no_console_user | (...same name, mixed case..) |
675314 | ..check_and_fix_dir_permissions | checkAndFixPermissionsAtPath:owner: |
675315 | ..register_product_file | registerProductFile:forProductKey:firmware:
trustLevel:keepOriginal:inForeground: |
675316 | ..client_register_personalized_manifests | registerPersonalizedManifests:forProductKey: |
675317 | ..make_queues | makeQueues |
675318 | ..move_installed_printers_to_library | moveInstalledPrintersToLibraryFromPath: |
675319 | ..remove_metadata_cache | removeMetadataCacheFromUpdates |
675320 | ..move_metadata_cache_to_updates | moveMetadataCacheToUpdatesFromPath: |
675321 | ..move_ppd_cache_to_updates | movePPDVersionCacheToUpdatesFromPath: |
675322 | ..remove_updates_index | removeIndexFromUpdates |
675323 | ..read_updates_index | readUpdatesIndex |
675324 | ..write_updates_index | writeUpdatesIndex: |
675325 | ..create_directory_for_product | createDirectoryForProductKey:Firmware: |
675326 | ..remove_dist_for_product | removeDistForProductKey:withFilename: |
675327 | ..remove_product | removeProductDirectoryForKey: |
675328 | ..remove_recovery_install_result | removeRecoveryInstallResult |
675329 | ..remove_downloaded_packages_for_product | removePackagesForLocalReferences:forProductKey: |
# | _suhelperd_client export | SUHelperProxy selector |
---|---|---|
675330 | ..digest_for_package | getDigest:forPackageAtURL: |
675331 | ..set_any_user_preferences | setObject:forAnyUserPreference: |
675332 | ..clear_any_user_preference | clearAnyUserPreference: |
675333 | ..update_any_user_preferences | SUHelperProxy updateAnyUserPreferences |
675334 | ..create_updates_available_cookie | createUpdatesAvailableCookie |
675335 | ..remove_updates_available_cookie | removeUpdatesAvailableCookie |
675336 | ..clear_critical_update_notification_date | clearCriticalUpdateNotificationDate |
675337 | ..set_app_store_auto_update | setAppStoreAutoUpdate: |
675338 | ..set_macos_auto_update | setMacOSAutoUpdate: |
675339 | ..deletepref_indomain | deletePref:inDomain: |
675340 | ..clear_catalog_to_production_and_notify | clearCatalogToProductionAndNotify |
675341 | ..unenroll_from_seed_program | unenrollFromSeedProgram |
675342 | ..stash_login_credentials | stashLoginCredentialsEnablingFLO: |
675343 | ..commit_login_credentials | commitLoginCredentialsDisablingFLO:
hasBaseSystemUpdates: |
675344 | ..stash_commit_apfs_fde_key | stashAndCommitAPFSFDEKey |
675345 | ..set_applestaged_upgrade_run_flo | setAppleStagedUpgradeShouldFLORun: |
675346 | ..configure_progress_phases | configureProgressPhasesAll: |
675347 | ..retrieve_url_credentials | lookupURLCredentialInSystemKeychainForHost:port: |
675348 | ..arm_basesystem_updates_mechanism | armBaseSystemUpdates:productKey: |
The --history
(to show installed updates) which can be coupled with --all
(to show updates installed by other clients, such as storedownloadd
, the Mac App Store daemon). This capability, like most of the rest, is achieved through the SUUpdateServiceDaemon
object (specifically, getInstallHistoryWithReply:
) over NSXPC to
The combined history of all software updates can also be directly viewed through its backing file,
A verbose log containing the details of installation processes can be found in SUDebugVeryVerbose
key is set in
The Software Catalog
The softwareupdate(8)
utility providing its main interface. Alternatively, with this being a CFPreferences
file, defaults(1)
can be used as well. Output A-5 shows a sample of this property list:
The most important configuration parameter is the CatalogURL
, which specifies the location of Apple's published software catalog. This URL is retrieved by the local system, which uses its contents to determine which updates are available, as well as download them. Apple defines three catalogs - the normal (release), beta and developer (early beta) seeds. Release systems are set to the normal catalog, but the CatalogURL
can be manually modified (using defaults
or softwareupdate -[set/clear]-catalog
). The CatalogURL
strings are also hardcoded into strings(1)
and grep(1)
ing for https://swscan.apple.com/content
URLs, which will reveal URLs with versions from the latest through 10.9, and then the feline names down to Leopard. The public beta seed can be obtained by appending "beta" to the latest MacOS version, the customer seed by "customerseed", and the developer program (normally requiring a registered developer login) - by appending "seed".
The software catalog is a gzip
ped property list. When served by Apple its MIME type, application/x-apple.sucatalog+xml
, is claimed by the Products
dictionary. Each of the dictionary keys is the update identifier (###-#####), and its value is another dictionary, containing a ServerMetadataURL
, and an array of one or more Packages
dictionaries. Each package dictionary specifies a Digest
, Size
, URL
of the update. This is shown in Listing A-6:
The private seedutil
is an undocumented tool hiding in its
seedutil
toolA particularly useful seed program is the DeveloperSeed
- enabling early access to MacOS beta releases when SeedProgram
key
set to a DeveloperSeed
string
value. The "MacOS Developer Beta Access Utility" Apple provides to registered developers performs this automatically, in addition to obtaining the Developer Beta bundle identifier from the IASBundleID
found in a property list at installMajorOSBundle=
argument, which brings up
Software Update Format
Software updates are downloaded (via softwareupdate
's temporary directory in InstallAtLogout
and InstallLater
, according to the update requirement and user choice. The indvidual updates' files may be
As explained in I/5, the xar(1)
. As with standard packages, the
Once uncompressed, the payload files are simple cpio -ivd
(in an empty directory) will unpack from stdin (-i
), preserving directories (-d
) and verbosely detailing contents (-v
).
With Apple's move to coprocessor enabled devices, MacOS updates also contain packages to update eOS/BridgeOS as well. The ill-fated eOS (a.k.a. BridgeOS 1.1, used briefly in the 2016 touchbar macs) are updated through
*OS Software Images
The *OS Software images are provided as
Base .ipsw contents
The
The
Tag | Filename | Description |
---|---|---|
APFS root filesystem | ||
rosi | HFS+ Restore ("Customer") ramdisk | |
HFS+ Update ramdisk | ||
krnl | The XNU kernelcache (+ KPP on pre A10 devices) | |
Build metadata (manifest) property list | ||
Restore property list | ||
logo | The Apple logo graphic | |
chg[0/1] | Battery charging graphic (bright/dim) | |
batF | Battery full (green) graphic | |
bat[0/1] | Battery empty/partially full (red) graphic | |
dtre/rdtr | The device tree | |
glyP | Lightning (needs charge) graphic | |
ibot | iBoot (stage 2) bootloader | |
liqd | Graphic used on A10+ if water damage is detected | |
illb | Low Level (stage 1) bootloader (A10+: same as ibot) | |
recm | "Connect to iTunes" recovery graphic | |
sepi/rsep | Secure Enclave Processor (SEP) firmware image | |
ibec | iBoot Epoch Change (A10+: same as ibot) | |
ibss | iBoot Single Stage (A10+: same as ibot) |
The fsleuth
.
The koly
trailer commonly found in
Looking at the above output, note the "HX" (HFSX magic). This magic value is expected to be at the beginning of the superblock, which is commonly at offset 1024. But this one is at 0x41b - 27 bytes after where it should be. This makes sense, considering the IMG4 header is to be accounted for. The image can be made mountable if those 27 bytes are removed:
As an alternative to extraction (or in cases where your home system is not MacOS), the fsleuth
tool (discussed in II/8) can be used to explore the images. The tool will automatically locate the filesystem signature, enabling operations on the image while still encapsulated by the IMG4 container.
If you try this experiment on both Update and Recovery ramdisks, you will see that they are virtually identical. One of the key differences is the choice of restored
variant that is executed. In the Update ramdisk, it is
com.apple.diskimages.recentcksum
) of its matching disk image. Additional firmware images
In addition to the iOS image files, *OS software images also contain files used for the other firmware components, such as the AOP, baseband, and other embedded processors. The number of these steadily increases with the complexity of the SoC, as shown in Table A-12:
Filename | Format | Provides firmware for |
---|---|---|
zip archive, ELF | Intel baseband | |
zip archive, ARMv7 ELF | Qualcomm (Maverick) baseband | |
64-bit MH_PRELOAD | Always On Processor | |
Config/Layout Data(?) | Lattice Semiconductor iCE5LP4K-SWG36 PLB | |
? | Secure Element firmware | |
Plist (+ ARMv7 images) | Multitouch controller | |
iPhone11,* | ||
? | Savage (development/Production) | |
? | Yonkers(?) (development/Production) | |
64-bit MH_PRELOAD | Image Signal Processor | |
Apple Neural Engine | ||
Audio/Video Encoder | ||
GPU Scheduler/Command Stream Processor | ||
T8020 and later Smart I/O | ||
Plist (+ ARMv7 images) | Haptics Firmware | |
(likely v7/Thumb) | Wireless charging |
Many of the firmware images of the SoC components are Mach-O, embedded in the IM4P. Apple brought back the MH_PRELOAD
file type (0x5) from oblivion, which makes sense in this case as all the images are effectively statically linked. The LC_UNIXTHREAD
directs execution in the images to address 0x0, which in turn branches to the ARMv8 reset handler. The images all refer to "RTKit", which is Apple's Real Time Operating System framework for embedded components. More discussion on the dedicated processors, including some aspects of their software images, can be found in II/1.
OTA Updates
The *OS software update mechanism is somewhat different than that of MacOS. Up until iOS 5, updates could only be initiated over iTunes, by putting the i-Device in restore or update mode. It was only in iOS 5 that Apple introduced the notion of Over-The-Air (OTA) updates, which necessitated an on device daemon to handle the download and installation. Since then, jailbreakers everywhere have grown to loathe the incessant OTA update nag notifications - as they appear in the most inopportune times and often seem intent on wearing down (or tricking) the user to update - an irreversible action due to Apple's draconian iOS-signing policy.
*OS: softwareupdated
The iOS and TvOS daemon is also called
The actual software updates are auto-downloaded into
softwareupdated
's asset folders. Doing so will prevent any updates from being fetched by the system, and disable the nag pop-ups Apple uses to coerce updating iOS - and thereby potentially losing the jailbreak. Another method is to set mesu.apple.com
's IP Address to a non-reachable one (e.g. 127.0.0.1) via OTA Updates are provided as
Directory | File | Description |
---|---|---|
Directory containing a single file, | ||
Bill of Materials for before and after update | ||
See Table A-9 | ||
[app_]patches/ | ||
Symbolic links to add or remove | ||
'pbzx' | ||
Actual filesystem update content |
With the exception of the payload and patch file(s), OTA updates are virtually identical to the IPSWs. And it is regarding the payload files that this author, made a simple but powerful observation: Because payloads were handled post iOS boot, they could not - by design - be encrypted. As discussed in II/@BOOT, access to the firmware decryption key (also known as the "GID Key") is lost once iBoot transfers control to the kernelcache. It follows, therefore, that if one got an OTA and had a previously jailbroken device - and thus access to an unencrypted file system image - it would be a simple matter to reconstruct the updated iOS filesystem by manually applying the patch files to the older files, even outside the device. In a series of articles ("The OTA Trilogy" and its followups[5]), both the initial packaging format -
At this point, another powerful observation was made - even though at the time
With iOS 10 and its counterparts, Apple seems to have given up on filesystem encryption. Still, there are devices - like the Watch and the "HomePod" smart speaker, for which there are no images save OTA ones. The tools you'll find on the companion website are thus still highly useful. The OTA trilogy was extended further, and the tools have evolved, most recently to support PBZX file modifications and to allow search functionality inside the payload archive. This is shown in the following experiment.
Whenever Apple releases an OTA - be it for a system update or a *OS beta version - it is usually a matter of hours, if not less, before the links show up on the iPhone Wiki (https://www.theiphonewiki.com/wiki/OTA_Updates). It is then a simple matter to use a browser, or the command line (wget/curl
) to obtain the image*.
Unpacking the OTA used to be a three step process - first recovering the pbzx chunks, then XZ-ing, and finally unpacking the format. The author's pbzx
tool has since progressed, and is now capable of applying XZ inline, as well as correctly putting together both chunk types (compressed and uncompressed). Using pbzx
is therefore sufficient to end up with an ota archive, on which the author's ota
can be applied:
pbzx
and ota
The most common function of ota
used to be extraction (-e), which can still be used to unpack the entire filesystem (-e '*'
, as demonstrated above), although it is often easier to just mount the DMG from an IPSW directly or (on Linux) using FSleuth.
The search function, however, still deserves special mention - as it enables a quick method by means of which a reverse engineer can find occurrences of a string or pattern across the (packed) filesystem - immediately pinpointing which file in the OTA contains it. The method is far superior to its alternative (searching each individual file), and is exceptionally useful in figuring out entitlements, symbol dependencies and more.
Note, that in-OTA searching is just a much faster, but still context-insensitive search. It therefore makes sense to be as specific and case-sensitive as possible, to reduce partial matches which could end up in false positives.
User-Agent
and other HTTP strings can easily be faked, but Apple could have ostensibly made it a little bit harder to get the updates (for example, some challenge prior to downloading)iOS Beta Program
Apple releases iOS betas as early as June every year, to coincide with the announcements made at the WWDC. These were originally developer-only, but (as with MacOS) Apple has extended the program to die-hard enthusiasts who want to test out the latest and greatest features.
Subscribing to a beta program is similar to MacOS, but due to iOS's strict lockdown can only be performed with an Apple-signed configuration profile. This is a defaults(1)
command in order to set the defaults of the domains listed within, which points the URLs of the
jlutil(j)
The APTicket
In the early versions of iOS, Apple seemed content to rely on the powerful encryption and authentication of boot components. The string of iOS 4.x jailbreaks, however, changed this view. One can imagine the powers that be at Cupertino riled by jailbreaks defacing the cherished Apple logo and replacing it with (*gasp*) a pineapple. Additionally, people made use of iTunes' undocumented ability to restore to any image when the alt (option) key was held down.
It was in iOS 5, therefore, that Apple introduced a formidable defense mechanism in its APTicket. The APTicket is personalized for each device, in that it contains device unique features - notably, the ECID (Exclusive Chip ID), and a boot nonce hash (BNCH) generated by iBoot. Generating a ticket further requires a protocol negotiation with Apple's Tatsu Signing Server (TSS). This makes it time sensitive, and reinforces the concept of a signing window, which is the span of time in which Apple's server will return a valid reply, rather than simply refusing the request. This is one of the ways Apple can ensure Tim Cook can always go on stage in every Apple event and boast the latest iOS version's amazing adoption rates - one reason being, Apple leaves no option but updating/recovering to the latest version.
The APTicket is thus generated by a request to Apple's server, with a property list containing all the attributes of the installation image. A sample of the request is shown in Listing A-16:
The resulting ticket is returned as the base64 payload of a an ApImg4Ticket
key in a property list, and contains hashes for all IPSW components (identified by their four character tags, from Table A-9), the boot nonce hash, and Apple's signature. The filesystem copy of the APTicket can be found in openssl
utility (present by default in MacOS) can be used to view its contents, as shown in Output A-17:
openssl
to display the contents of The boot nonce hash (generated on every installation/recovery by iBoot), coupled with the online transaction, is explicity designed to prevent downgrades. There is, however, a way around this: As it so happens, Apple left behind a way to fix the boot nonce hash to a known value. This can be done by setting the com.apple.System.boot-nonce
NVRAM variable, as is needed during OTA updates. This was figured out by jailbreakers, who created the "Prometheus" tools, which enable pinning the nonce using "nonceenabler", and using "futurerestore". This made Apple not only protect NVRam variables through entitlements, but further try to restrict their writability by mapping them in memory. Clever solutions like Viktor Oreshkin's[7], however, work around this.
As of the A12 and later, Apple further doubles down against downgrade "attacks" by enabling nonce entanglement. This defeats existing methods to pin the nonce by first generating a key (using the device's UID key) from magic bytes (56 82 41 65 65 51 E0 CD F5 6F F8 4C C1 1A 79 EF, present also in the ROM and the kernelcache), and using the value to encrypt the boot nonce, so as to ensure it had to have been created on the specific i-device. There is no presently known way to work around this method.
References
- NewOSXBook.com -
seedutil
reversed source -
http://NewOSXBook.com/src.jl?tree=listings&file=seedutil.m - NewOSXBook.com -
pbzx
unpacker -
http://NewOSXBook.com/src.jl?tree=listings&file=pbzx.c - The iPhone Wiki - iPhone Wiki Firmware Page
- Bridge OS Update XML - https://mesu.apple.com/assets/bridgeos/
com_apple_bridgeOSIPSW/com_apple_bridgeOSIPSW.xml - PlanetBeing - Partial Zip - https://github.com/planetbeing/partial-zip
- NewOSXBook.com - The OTA Saga - http://NewOSXBook.com/articles/OTA.html
- Viktor Oreshkin - "What does NVRAM lock/unlock actually mean" - https://stek29.rocks/2018/06/26/nvram.html
Post Scriptum
The first (technically, second) volume of the series draws to its end, and with it we leave user mode behind. The next volume will deal almost exclusively with the kernel, but also find room for discussing the hardware of the Mac and various i-Devices, as well as one intentional omission - the network stack.
As with Volume III, which (at the time I'm updating this) has been out for two years in which it had seen six major revisions (i.e. additional chapters, four in the first year and two in the second), I hope to keep this volume updated as well - This version has already been revised with minor additions for Darwin 18, 19 (betas) and an entirely new chapter (0x10 - on user mode aspects of networking), and the Appendix. Unlike Volume III, however, wherein the updates are governed primarily by new jailbreaks, I expect this one to be updated more slowly. You can track the current update status of the series at http://NewOSXBook.com/ChangeLog.html.
As with what I've started with "Android Internals", and Volume III - if you find any factual error in this book, (assuming it pertains to the versions covered by the book, and not later Darwin, wherein Apple might have changed the API, or *OS/MacOS subtleties), I will gladly reward you with 0.01BTC, and immortalize you in the book's change log. This is significantly down from the 0.1BTC I offered only about year ago, but then BTC went up from $700 to just breaking $12,000(!) as I finished v1.0, and $19,000(!!) two weeks later with v1.0.1, though it's been relatively stable at around $6,500 or so recently. Fortunately, nobody has caught any such errors, so my stash of 2BTC I've allocated for "damages" is safe :-). Although from the looks of things in mid June 2019 BTC is back around $9,000 - so if you do find anything, better let me know and cash in $90! I hope Eddie (and later, Peter) managed to catch all the typos, but from experience, there's always more. If you find them, please let me know through the book's forum - I'd appreciate that.
Remember, that so much advanced functionality is not without its pitfalls: If you've read this book with a security oriented mindset (as I assume a fair portion of my reader-base is), you'll no doubt notice some potential security vulnerabilities*. Unlike the trivial coding errors which result in memory corruption, a few of these are "baked into" the design - but Apple will likely fix these as they have most others to date - by slapping an entitlement onto them. Unfortunately, the more Apple does so - and they very well should - the more they lock out legitimate (albeit uncommon) use cases, primarily those of my various tools. The com.apple.private.*
entitlements won't be given to anyone outside Cupertino, and it's a shame - as Apple's command line tools fall far short of their promise.
I hope to see you again in Volume II - which is hot off the presses and concludes this magnum opus of a trilogy... Or maybe even see you in person, in one of the trainings offered by Technologeeks.com - both the 5-day "OSX/iOS for Reverse Engineers" and the shorter (3-day) "Applied *OS Security". If you've really read this far - mention this when you book a training, and maybe they'll cut you a 5% discount if you bring the book. Heck, I could even sign your copy, if you want ;-)