An iOS app that reaches memory corruption still has almost nothing. It runs inside a profile that names every service it may talk to, its binary was checked against a signature before it started, and the kernel it wants sits behind entry points that validate their arguments. Each of those is a different subsystem, decided at a different moment, and the bug is worth nothing until you know all three.
Ten posts take them one at a time, in the order the device builds them. This page is the index.
Everything in this article is public. It contains no exploit and no 0day.
What each layer decides
The boot chain decides which kernel runs at all. The SoC leaves reset into a mask ROM written when the chip was fabricated, and from there each stage validates the Image4 signature on the next before handing it control. That is why a Boot ROM bug like checkm8 invalidates every later check on the main processor, though not the Secure Enclave, which boots from a ROM of its own.
XNU is what the chain hands control to: a Mach core for inter-process communication, virtual memory and scheduling, with a BSD layer above it for processes, files and sockets. A Mach port is an unforgeable reference to a kernel object, and holding a send right to the right one is what privilege means here.
AMFI, the AppleMobileFileIntegrity policy module, decides what is allowed to execute, and its answer turns on a 20-byte hash of the code directory called the cdhash. The sandbox decides what an already-running process may touch, and its answer is the profile it was launched with. Both are MACF policies, the Mandatory Access Control Framework XNU inherited from TrustedBSD, and the kernel enforces them, not the process being checked.
Then come the surfaces a confined process can still reach. IOKit is the widest: hundreds of drivers, many of them vending a user client you open and call into with a selector and a buffer. Mach messages, MIG (the Mach Interface Generator, which produces the marshalling code) and XPC, the higher-level framework layered on top of them, are the other, and they lead into a more privileged daemon that may or may not check who is calling.
The hardening layer decides what a bug is worth once you have one. The zone allocator sorts allocations into buckets by the layout signature of the type, which fixes what may take a slot once it is freed. Pointer authentication signs the pointers worth hijacking with a key no memory write can reach. SPTM and TXM (Secure Page Table Monitor, Trusted Execution Monitor) took page-table writes and code-signing decisions out of the kernel’s privilege level, and memory tagging on the newest silicon makes the corrupting write itself fault.
Userland is where the reversing happens. An Objective-C object begins with a word you have to decode before it means anything, and the framework it belongs to is a range inside one merged image rather than a file on disk.
The ten posts
- The iOS chain of trust. The Boot ROM verifies iBoot, iBoot verifies the kernelcache, and Image4 is the container all of it is signed in. Then checkm8.
- XNU under the hood. Mach and BSD side by side, the port as the unit of capability, and what
tfp0actually is once you go looking for it. - The iOS code-signing pipeline. Which binaries are allowed to execute: one MACF policy module and a verdict keyed on the cdhash, from the trust cache through CoreTrust to
amfid. - The iOS sandbox. What a running process may touch. The profile is the exact list, so reading it is usually how you pick the next target.
- IOKit up close. How a userland call lands in a driver’s dispatch table, and CVE-2022-32832, which needs root before it is reachable at all, walked from the selector to the corruption.
- Mach messages, MIG and XPC. How a process asks a more privileged one to do something, and the three functions that decide whether the callee knows who it is answering.
- The zone allocator up close. What a memory-corruption bug is worth after
kalloc_type, and why the exploits moved down to physical pages. - Pointer authentication. What arm64e signs, and what a bypass has to look like when the key is out of reach.
- SPTM, TXM and memory tagging. The two monitors that took page tables and code-signing decisions away from the kernel, and the tagging that rests on them.
- The Objective-C runtime and the shared cache. One word to decode before an object means anything, and libraries that live inside the shared cache.
How to read it
In order it is one argument, built the way the device is. Out of order works too: each post stands on its own, glosses its terms, and links back to the one that introduced them. It assumes C, enough ARM64 assembly to read a function, and a disassembler you are comfortable in. It assumes nothing about Apple platforms.
Nine of the ten carry a section headed Hands-on, and none of them needs a jailbroken device or a Corellium instance. They run on an Apple silicon Mac with stock macOS and System Integrity Protection left on, plus the Xcode command line tools, and ipsw and pyimg4 for the firmware side. The transcripts were taken on macOS 26.4.1, so re-run them rather than quote them.
What changed since 2021
If your model of this stack dates from iOS 14 or 15, five things have changed under it.
kalloc_type(#7), iOS 15 and wider in iOS 16, sorts the heap by the type signature of each allocation, so freeing an object and taking its slot with a different one stops working by default.- SPTM and TXM (#9) replaced PPL, the Page Protection Layer, from iOS 17 and macOS 14, on A15 and later and on every Mac except the one built around the base M1. The kernel now calls two monitors instead of doing that work itself.
- Memory Integrity Enforcement (#9), the synchronous memory tagging announced in September 2025, ships on the A19 and A19 Pro from iOS 26 and on the M5 from macOS 26. Many linear overflows and use-after-frees now fault at the write.
- Credentials (#2) are out of reach of a plain write. The pointer to them moved into the read-only
proc_roback in the iOS 15 and macOS 12 generation, and the credential itself now comes from the read-only allocator. - Launch constraints (#3), iOS 16, bind each system binary to the one context it may be launched from, which killed two old signing tricks: repurposing a privileged helper, and reusing an old Apple-signed binary.
What is out of scope
This is the local story: an attacker who already has code running on the device, working from a sandbox towards the kernel.
The remote entry points are deliberately absent. WebKit and JavaScriptCore for the one-click case, iMessage, BlastDoor and the image parsers for the zero-click case, baseband and Wi-Fi for the over-the-air case. So is the Secure Enclave, which is closed enough to want a post to itself. They are a second season. The closest thing already here is the post on JavaScript engine exploitation, which works at engine level rather than on iOS.
Where this leaves us
One dependency runs through all of it. A third-party app gets the same container profile as every other one, and what widens it is entitlements that only mean anything because AMFI validated the signature carrying them, which in turn only means anything because the kernel enforcing it booted from a verified chain. That is why the layer a bug lands in usually says more about what it is worth than the corruption does.
An index is a menu. The parts that matter are the details it leaves out: which field of which struct, which OS version turned a technique off. The chain of trust is where that starts.
Notes and sources
Everything in the series is drawn from open source, vendor documentation, published research, and a Mac running a stock, unmodified macOS. Four references sit behind all ten posts.
- Apple, Apple Platform Security, the vendor’s description of the boot chain, code signing and the hardware mitigations, and the only source for intent as opposed to behaviour.
- Apple, the XNU source, where a question about a struct layout, a return value or a version boundary gets settled. Several corrections in this series came from a header rather than a write-up.
- Jonathan Levin, *OS Internals, Volume II for kernel mode and Volume III for security, at a depth no blog post reaches.
ipswby blacktop andpyimg4by m1stadev, which turn an Apple firmware image into files you can read, and where the firmware-side hands-on sections start.
