A Reverse Engineer’s Anatomy of the macOS Boot Chain & Security Architecture

1.0 The Silicon Root of Trust: Pre-Boot & Hardware Primitives

The security of the macOS platform on Apple Silicon is not defined by the kernel; it is defined by the physics of the die. Before the first instruction of kernelcache is fetched, a complex, cryptographic ballet has already concluded within the Application Processor (AP). This section dissects the immutable hardware logic that establishes the initial link in the Chain of Trust.

1.1 The Reset Vector & Boot ROM (SecureROM)

The Apple Silicon boot process begins in a state of absolute trust, anchored by the Boot ROM (often colloquially referred to as SecureROM). This code is mask-programmed into the silicon during fabrication. It is immutable, unpatchable, and serves as the hardware root of trust for the entire platform.

1.1.1 Execution at Exception Level 3 (EL3): Analyzing RVBAR_EL3

Upon Power-On Reset (POR), the cores of the M-series SoC (and A-series) initialize in the highest privilege state defined by the ARMv8/v9 architecture: Exception Level 3 (EL3).

While Apple does not utilize TrustZone in the traditional sense (preferring their proprietary Secure Enclave for secure world operations), the AP Boot ROM executes at EL3 to perform the necessary low-level hardware initialization that requires access to secure configuration registers that are subsequently locked or hidden from EL2/EL1.

The execution flow begins at the address defined in the Reset Vector Base Address Register (RVBAR_EL3). On recent Apple Silicon (M1/M2/M3), the memory map places the Boot ROM at a high base address, typically 0x100000000.

The Initial Instruction Stream:
The very first instructions executed by the silicon are responsible for establishing a sane C execution environment from a raw hardware state. A disassembly of the entry point typically reveals the following sequence:

  1. Interrupt Masking: DAIF bits are set to mask all interrupts (IRQ, FIQ, SError, Debug). The Boot ROM operates in a strictly polled mode; interrupts are nondeterministic and introduce attack surface.
  2. Cache Invalidation: The instruction and data caches are invalidated to prevent cold-boot attacks or stale data usage.
  3. Stack Setup: The Stack Pointer (SP_EL3) is initialized to point to a dedicated region of on-chip SRAM (Static RAM). Note that DRAM is not initialized at this stage. The Boot ROM runs entirely within the constraints of the SoC's internal SRAM (often referred to as L2 cache-as-RAM in older exploits, though modern SoCs have dedicated boot SRAM).
  4. MMU Configuration: The System Control Register (SCTLR_EL3) is written to enable the MMU, mapping the Boot ROM text as Read-Only/Executable and the SRAM stack/heap as Read-Write/No-Execute.

RE Note: Apple’s implementation of EL3 is ephemeral. Unlike Android/Qualcomm devices where EL3 hosts a persistent Secure Monitor (QSEE), Apple Silicon demotes the exception level to EL2 (Hypervisor) or EL1 (Kernel) before handing off control to the next stage. Once the Boot ROM exits, EL3 is effectively locked out, and the RVBAR_EL3 is often locked to prevent re-entry.

1.1.2 The GID Key (Group ID): Hardware-entangled Decryption

The Boot ROM's primary objective is to load the Low-Level Bootloader (LLB). However, the LLB stored on the NAND flash (or NOR SPI flash in some configurations) is encrypted. To decrypt it, the Boot ROM utilizes the GID Key (Group ID Key).

The GID Key is a 256-bit AES key fused into the silicon during manufacturing. It is shared across all processors of the same class (e.g., all M3 Pro chips share a GID, but it differs from the M3 Max).

The "Black Box" AES Engine:
Crucially, the GID Key is not accessible via software. There is no Memory-Mapped I/O (MMIO) register that an attacker can read to dump the key. Instead, the AES engine operates as a black box:

  1. Input: The Boot ROM writes the encrypted ciphertext (the LLB payload) into the AES engine's input FIFO.
  2. Control: The Boot ROM sets a specific control bit in the AES configuration register (e.g., AES_CMD_USE_GID).
  3. Operation: The hardware AES engine loads the GID key from the fuses directly into the crypto core, performs the decryption, and flushes the key from the internal registers.
  4. Output: The plaintext is written to the output FIFO, which the Boot ROM then reads into SRAM.

This architecture ensures that even if an attacker gains arbitrary code execution within the Boot ROM (as seen in the checkm8 exploit on older A-series chips), they cannot extract the GID key to decrypt firmware images offline. Decryption must happen on-device.

1.1.3 The Public Key Accelerator (PKA): Hardware-Enforced Verification

Decryption provides confidentiality, but not integrity. To prevent the execution of malicious firmware, the Boot ROM enforces strict code signing using the Public Key Accelerator (PKA).

The PKA is a dedicated hardware block optimized for asymmetric cryptography (RSA and ECC). The verification flow is as follows:

  1. Root of Trust Hash: The SHA-384 hash of the Apple Root CA public key is burned into the device's eFuses. This is the immutable anchor.
  2. Manifest Parsing: The Boot ROM parses the Image4 (img4) container of the LLB. It extracts the Image4 Manifest (IM4M), which contains the payload's signature and the public key used to sign it.
  3. Key Verification: The Boot ROM hashes the public key found in the manifest and compares it against the hash in the eFuses. If they do not match, the boot halts (DRAM is never initialized, and the device enters DFU mode).
  4. Signature Verification: The Boot ROM offloads the signature verification to the PKA. It passes the SHA-384 hash of the payload and the RSA/ECC signature. The PKA performs the mathematical verification and returns a boolean result to a status register.

Fault Injection Hardening:
Modern Apple Boot ROMs employ glitch-resistant logic around the PKA check. Rather than a simple B.EQ (Branch if Equal) instruction following the PKA result—which could be bypassed via voltage glitching—the code often employs redundant checks, loop invariants, or specific register values that must be populated by the PKA hardware itself to allow the boot flow to proceed.

1.1.4 RE Focus: Dev vs. Prod Fused Silicon

For the reverse engineer, distinguishing between Development (Dev) and Production (Prod) fused silicon is vital. The behavior of the Boot ROM changes fundamentally based on the "Security Domain" fuse.

  • Production (CPFM 01): The standard consumer state. JTAG is physically disabled. The GID key is locked to the production value. The Boot ROM enforces the full Apple Root CA chain.
  • Development (CPFM 00/03): Used internally by Apple engineers.
    • JTAG Enablement: The DBGEN signal is asserted, allowing hardware debuggers (like Lauterbach or Astris) to halt the core immediately after reset.
    • Demotion: Dev-fused chips often allow "demotion," enabling the device to boot unsigned or custom-signed firmware images.
    • GID Key Variance: Dev chips often use a different GID key, meaning firmware encrypted for Prod devices cannot be decrypted on Dev hardware, and vice versa.

Identifying Silicon State:
You can identify the fuse status by querying the CHIP_ID or MOJO registers via USB when the device is in DFU mode.

  • 0x8000... usually indicates a Production fuse.
  • 0x0000... or specific bitmasks in the ECID response indicate Development/Debug fusing.

The "Un-dumpable" Region:
Once the Boot ROM prepares to jump to the next stage (LLB), it performs a lockdown sequence. It writes to the memory controller to unmap its own address range (0x100000000). Any subsequent attempt by the LLB or Kernel to read the Boot ROM address space will result in a bus error or read back zeros. This "hide-and-seek" mechanism is why dumping the Boot ROM requires a vulnerability present during the Boot ROM execution window (like checkm8 or a voltage glitch at reset).

1.2 Proprietary ISA Extensions (arm64e+)

While the M-series chips ostensibly implement the ARMv8.6-A (and increasingly ARMv9) specification, Apple has aggressively extended the Instruction Set Architecture (ISA) with proprietary logic. For the reverse engineer, standard ARM documentation is insufficient. Understanding the security posture of macOS Tahoe requires mastering these custom extensions, as they form the hardware enforcement layer for the new kernel isolation model.

1.2.1 Pointer Authentication (PAC): The Cryptographic Control Flow

Apple’s implementation of ARMv8.3-PAuth is the most pervasive security mitigation in the XNU kernel. It repurposes the unused high-order bits of 64-bit virtual addresses (typically bits 63–47, depending on Translation Control Register TCR_EL1 settings) to store a cryptographic signature, or Pointer Authentication Code (PAC).

The Key Hierarchy:
The hardware maintains five distinct 128-bit keys in system registers, accessible only at EL1 or higher. These keys segregate pointer types to limit the utility of a signing gadget:

  • APIAKey / APIBKey (Instruction): Signs code pointers (function pointers, return addresses).
  • APDAKey / APDBKey (Data): Signs data pointers. Crucial for protecting C++ vtables in IOKit (OSObject).
  • APGAKey (Generic): Signs arbitrary data blobs, effectively a hardware-accelerated MAC.

The AUT Failure Mechanism (Canonical Non-Valid):
For the reverse engineer analyzing crash dumps, understanding the failure mode is critical. When an AUT* instruction (e.g., AUTIA) is executed on a corrupted or forged pointer, the CPU does not immediately raise an exception.

Instead, the hardware corrupts the pointer in a deterministic way to ensure it causes a translation fault upon dereference.

  1. Validation: The CPU recalculates the PAC.
  2. Mismatch: If the calculated PAC does not match the bits in the pointer, the CPU flips specific high-order bits (typically bit 62 for data pointers, or bit 61 for instruction pointers, depending on Top Byte Ignore settings).
  3. Result: The pointer becomes "canonical non-valid." It looks like a kernel pointer (high address) but falls into a reserved, unmapped range.
  4. Crash: The subsequent LDR or BLR triggers a Data Abort or Prefetch Abort.

RE Tip: If you see a crash where x0 is 0x007f... or 0x00ff... (a pointer with high bits set but not fully canonical), you are looking at a PAC authentication failure, not a standard NULL dereference or heap corruption.

1.2.2 Branch Target Identification (BTI): The Landing Pads

Often deployed in tandem with PAC (-mbranch-protection=standard), BTI mitigates Jump-Oriented Programming (JOP). It enforces a state machine on indirect branches.

  • Marking Pages: The Global Page Table (GPT) or PTE entries now include a Guarded Page (GP) bit.
  • The BTI Instruction: This is a "hint" instruction (NOP on older silicon). It acts as a valid landing pad.
  • Enforcement: When the CPU executes an indirect branch (BR, BLR) targeting a Guarded Page, the very next instruction must be a BTI instruction of the correct type (c for call, j for jump, jc for both).

If the target is not a BTI instruction, the CPU raises a Branch Target Exception. In XNU, this manifests as a SIGILL with a specific subcode. For exploit development, this necessitates finding gadgets that not only perform the desired operation but are also preceded by a valid landing pad.

1.2.3 New in Tahoe: The Guarded Execution Feature (GXF)

This is the most significant architectural divergence in the Apple Silicon era. Standard ARM defines a vertical privilege stack (EL0 -> EL1 -> EL2). Apple has introduced Lateral Exception Levels, referred to as Guarded Levels (GL).

GXF allows the processor to switch between execution modes that share the same architectural Exception Level (EL1) but possess vastly different hardware permissions and system register views.

  • GL0: The standard XNU Kernel context.
  • GL1: The Trusted Execution Monitor (TXM) context.
  • GL2: The Secure Page Table Monitor (SPTM) context.

The Proprietary Opcodes:
Transitions are not handled by HVC or SMC. Apple added custom instructions to the ISA:

  • GENTER (Opcode 0x00201420): Synchronous entry into a Guarded Level. It behaves like a function call but atomically switches the hardware context (SPRR state, stack pointer, and system registers).
  • GEXIT (Opcode 0x00201400): Returns from GL to the caller.

Reverse Engineering the Transition:
In disassembly, GENTER takes a dispatch ID (selector) in x15 and arguments in x0-x7. The entry point for the transition is hardcoded in the proprietary system register GXF_ENTRY_EL1 (S3_6_C15_C8_2). This register is locked early in the boot process (by iBoot), preventing the kernel from hijacking the monitor's entry vector.

1.2.4 New in Tahoe: Shadow Permission Remapping Registers (SPRR)

To enforce isolation between GL0, GL1, and GL2, Apple replaced the older APRR (Access Permission Remapping Registers) with the more robust SPRR (Shadow Permission Remapping Registers).

In standard ARM MMUs, the Page Table Entry (PTE) bits AP[2:1] directly define Read/Write permissions. In Apple Silicon with SPRR enabled, these bits are repurposed as an index into a hardware permission table.

The Indirection Layer:

  1. PTE Index: The PTE specifies a permission index (e.g., Index 5).
  2. Context Lookup: The hardware checks the current execution mode (GL0, GL1, or GL2).
  3. Resolution: It looks up Index 5 in the SPRR_PERM_EL1 register specific to that mode.

The Security Implication:
This allows for "View-Based" memory protection.

  • Index 5 in GL2 (SPTM): Resolves to Read-Write (RW).
  • Index 5 in GL0 (Kernel): Resolves to Read-Only (RO).

This is how the SPTM protects page tables. The physical pages containing the translation tables are marked with a specific SPRR index. The hardware configuration for GL0 (Kernel) maps that index to Read-Only. Even if an attacker has a kernel-level arbitrary write primitive, the MMU will reject the write to the page table because the SPRR configuration for GL0 forbids it. The only way to write to that page is to execute GENTER to switch to GL2, where the SPRR configuration permits the write.

2.0 The Secure Enclave Processor (SEP): The Parallel Computer

If the Application Processor (AP) is the brain of the device, the Secure Enclave Processor (SEP) is its conscience. It is not merely a coprocessor; it is a fully independent computer-on-a-chip, sharing the same die but architecturally severed from the AP. It runs its own kernel (an Apple-customized L4 microkernel), manages its own peripherals, and holds the keys to the kingdom (UID/GID). In the Tahoe architecture, the SEP’s role has expanded from simple key management to becoming the root of authority for biometric intent and hardware attestation.

2.1 SEP Initialization & Boot

The SEP boot process is designed to be resilient against a fully compromised Application Processor. From the moment power is applied, the SEP assumes the AP is hostile.

2.1.1 The SEPROM: SRAM Execution and the Memory Protection Engine (MPE)

Like the AP, the SEP begins execution from an immutable on-die Boot ROM, the SEPROM.

The Hardware Environment:
The SEP core (typically a customized ARM core, historically evolving from Cortex-A7 derivatives) initializes in a highly constrained environment. It does not initially have access to the system's main DRAM. Instead, it executes strictly within a dedicated, on-die SRAM region. This isolation prevents early-boot DMA attacks from the AP or Thunderbolt peripherals.

The Memory Protection Engine (MPE):
As the sepOS is too large to fit entirely in SRAM, it must eventually reside in the device's main DRAM (Unified Memory on M-series). To do this securely, the SEP utilizes a hardware Memory Protection Engine (MPE).

The MPE sits inline between the SEP core and the memory controller. It creates a cryptographic window into physical memory that is opaque to the rest of the SoC.

  1. Ephemeral Keys: Upon SEP reset, the MPE generates a random, ephemeral AES key. This key exists only in the MPE hardware registers and is never exposed to software (even sepOS).
  2. AES-XEX Encryption: Data written by the SEP to DRAM is encrypted transparently using AES in XEX (XOR-Encrypt-XOR) mode.
  3. Authentication: The MPE calculates a CMAC tag for every cache line. This tag is stored alongside the encrypted data.

RE Implication: If you attempt to dump the physical memory range assigned to the SEP from the AP (kernel mode), you will see high-entropy noise. Furthermore, any attempt to modify a single bit of this memory via the AP will invalidate the CMAC tag. The next time the SEP reads that line, the MPE will detect the forgery and trigger a hardware panic, locking down the Enclave until a full system reset.

2.1.2 The Boot Monitor: Hardware Enforcement of OS-Bound Keys

On modern silicon (A13/M1 and later), Apple introduced the Secure Enclave Boot Monitor to mitigate the risk of Boot ROM exploits (like checkm8) compromising the chain of trust for key derivation.

In older architectures, the SEPROM would verify the sepOS signature and then jump to it. If the SEPROM was exploited, the attacker could jump to a malicious payload while retaining access to the hardware UID key. The Boot Monitor closes this gap by enforcing System Coprocessor Integrity Protection (SCIP).

The Boot Flow:

  1. Payload Staging: The AP (iBoot) loads the sep-firmware.img4 payload into a region of physical memory.
  2. Mailbox Signal: The AP signals the SEP via a hardware mailbox register.
  3. Verification: The SEPROM parses the Image4 container. It verifies the signature against the Apple Root CA hash fused into the SEP's eFuses.
  4. The Handoff: Crucially, the SEPROM cannot simply jump to the loaded image. The SCIP hardware prevents execution of mutable memory.
  5. Monitor Intervention: The SEPROM invokes the Boot Monitor hardware block.
    • The Monitor resets the SEP core to a known clean state.
    • The Monitor calculates a cryptographic hash of the loaded sepOS memory range.
    • The Monitor updates the SCIP registers to permit execution of that specific range.
    • The Monitor locks the hash into a dedicated register in the Public Key Accelerator (PKA).

OS-Bound Key Derivation:
This finalized hash is the critical component. When the sepOS later requests keys (e.g., to decrypt user data), the hardware Key Derivation Function (KDF) mixes the hardware UID with this locked hash.

$$ K_{derived} = KDF(UID, Hash_{sepOS}) $$

If an attacker modifies a single byte of the sepOS (even with a Boot ROM exploit), the Boot Monitor calculates a different hash. Consequently, the KDF derives a different key, and the encrypted user data remains mathematically inaccessible. This is "Bound Security"—the data is bound not just to the device, but to a specific, signed software version.

2.1.3 Anti-Replay Mechanisms: The Integrity Tree

A classic attack vector against secure enclaves is the Replay Attack: capturing a snapshot of the encrypted RAM (e.g., when the passcode retry counter is 0) and restoring it later after the counter has incremented.

To prevent this, the SEP implements a hardware-enforced Integrity Tree (Merkle Tree).

  1. The Root of Trust: The root node of the integrity tree is stored in dedicated on-chip SRAM within the Secure Enclave complex. This memory is physically distinct from the main DRAM and cannot be addressed by the AP.
  2. Tree Structure: The protected memory region (where sepOS data and the Secure Storage Manager reside) is divided into blocks. Each block's hash is stored in a parent node, recursively up to the root.
  3. Atomic Updates: When the SEP writes to protected memory (e.g., incrementing a failed attempt counter), the MPE updates the data, recalculates the hashes up the tree, and atomically updates the root hash in the on-chip SRAM.
  4. Verification: On every read, the MPE verifies the path from the data block up to the SRAM root.

If an attacker replays an old DRAM state, the hash of the replayed block will not match the current root hash stored in the internal SRAM. The MPE detects the mismatch (Anti-Replay Violation) and halts the SEP. This mechanism ensures that the SEP has a strictly monotonic view of time and state, rendering snapshot fuzzing and counter rollbacks impossible.

2.2 SEP Runtime Architecture

Once the sepOS is bootstrapped and verified, the Secure Enclave transitions into its runtime state. At this point, it functions as a fully autonomous operating system running an Apple-customized variant of the L4 microkernel. For the reverse engineer, understanding the runtime architecture is crucial for analyzing how the SEP communicates with the hostile "Rich Execution Environment" (the AP running XNU) and how it persists sensitive state.

2.2.1 The Mailbox Interface: Analyzing the Shared Memory IPC

Communication between the Application Processor (AP) and the SEP is strictly asynchronous and interrupt-driven. Unlike the tight coupling of the SPTM (which uses synchronous instruction traps), the SEP interaction is mediated by a hardware mechanism known as the Mailbox, which relies on the proprietary Apple Interrupt Controller (AIC) to manage signaling.

The Physical Transport: AIC and Ring Buffers
There is no shared virtual memory space; the two processors exchange messages via physical memory ring buffers and hardware interrupts.

  1. The Ring Buffers (Inbox/Outbox):
    The system reserves two physical memory regions in DRAM, carved out by iBoot and described in the Device Tree (typically under the sep node as inbox-size and outbox-size). These function as circular buffers.

    • Control Structures: Each buffer is managed by a pair of pointers (Write Head, Read Tail) stored in shared SRAM or at the start of the DRAM region.
    • Coherency: Because the SEP and AP share the same Unified Memory fabric, cache coherency is critical. The AppleSEPDriver must ensure appropriate barriers (DMB, DSB) are used when updating the Write Head to ensure the SEP sees the payload before the interrupt fires.
  2. The Doorbell (Apple Interrupt Controller):
    To signal a message, the sender cannot simply write to memory; it must trigger an exception on the receiver. On Apple Silicon, this is handled by the AIC, not a standard ARM GIC.

    • AP $\rightarrow$ SEP: The kernel writes to a specific AIC "Set" register (mapped in the AP's MMIO space). This asserts a hardware IRQ line wired to the SEP's core.
    • SEP $\rightarrow$ AP: When the SEP replies, it writes to its own AIC interface, which triggers a specific IRQ (often IRQ 0 or IRQ 1 relative to the SEP driver's view) on the AP. The kernel's interrupt handler (AppleSEPDriver::interruptOccurred) acknowledges this by writing to the AIC "Clear" register.

The L4 IPC Protocol (A7IOP):
The data payload within the ring buffers follows a serialized format, historically referred to as the Apple A7 I/O Protocol (A7IOP), though it has evolved significantly.

  • Message Header: Every message begins with a 64-bit header containing:
    • Endpoint ID (8 bits): The destination service within the sepOS (e.g., 0x10 for Biometrics, 0x14 for Secure Storage).
    • Tag (8 bits): A transaction ID used to correlate asynchronous replies.
    • Length (16 bits): The size of the payload.
  • The AppleSEPDriver: On the XNU side, this kext manages the ring buffer logic. It serializes kernel requests (e.g., "Unlock Keybag") into the mailbox format, updates the Write Head, rings the AIC doorbell, and sleeps the calling thread on a condition variable until the SEP triggers the reply interrupt.

RE Focus: Fuzzing the Boundary
The mailbox is the primary attack surface for the SEP. Vulnerabilities here (parsing malformed messages) can lead to code execution within the Enclave.

  • Endpoint Fuzzing: The sepOS kernel dispatches messages to user-mode L4 tasks based on the Endpoint ID. Fuzzing specific endpoints (especially legacy or debug endpoints left enabled in production) is a standard methodology.
  • Shared Memory Hazards (DART): While the mailbox buffers are used for control messages (metadata), bulk data (like a firmware update or a large biometric template) is passed via DART-mapped shared memory. The AP maps a page, pins it, and passes the physical address to the SEP in a mailbox message. The SEP then maps this page into its own address space.
    • TOCTOU: Race conditions here are a classic target: modifying the data in the shared buffer after the SEP has validated the header/signature but before it processes the body. The SEP attempts to mitigate this by copying data to internal SRAM before processing, but large payloads may be processed in-place, exposing a window of opportunity.

2.2.2 The Secure Storage Component (xART): Encrypted Persistent Storage

The SEP has no internal non-volatile storage (NAND) of its own. It must rely on the AP's NAND flash to store persistent data (like the user's passcode hash, biometric templates, and credit card tokens). However, it cannot trust the AP to store this data securely.

To solve this, Apple introduced the Secure Storage Component, often referred to in firmware and kexts as xART (eXtended Anti-Replay Technology).

The Architecture:

  1. The Physical Chip: On modern devices, xART is backed by a dedicated, tamper-resistant EEPROM or NOR flash chip physically connected to the SEP via a private I2C or SPI bus. This chip is not accessible to the AP.
  2. The Logical Volume: The SEP treats the external NAND (managed by the AP) as a "dumb" block device. It encrypts its filesystem using keys derived from the hardware UID and the anti-replay counters stored in the dedicated xART chip.

The Anti-Replay Guarantee:
When the SEP writes a file (e.g., updating the failed passcode attempt counter):

  1. It encrypts the file data.
  2. It generates a new random nonce or increments a counter.
  3. It writes the encrypted file to the AP's filesystem (via the Mailbox).
  4. Crucially, it writes the hash of the file and the new counter to the dedicated xART storage chip.

When reading the file back:

  1. The SEP requests the file from the AP.
  2. It reads the expected hash/counter from the xART chip.
  3. It verifies the file against the xART record.

If the AP (or an attacker with physical access to the NAND) tries to revert the file to an older version (e.g., resetting the counter to 0), the hash will not match the record in the xART chip. The SEP will detect the rollback and reject the data. This mechanism ensures that the SEP's state is strictly monotonic and immune to external storage manipulation.

2.2.3 RE Focus: Reverse Engineering the sepOS L4 Syscall Table

For the advanced reverse engineer, the holy grail is understanding the sepOS kernel itself. Since it is based on L4, it relies heavily on synchronous IPC for system calls.

Identifying the Syscall Handler:
In the disassembled sepOS binary (which can be decrypted if you have a Boot ROM exploit for the specific device class, or by analyzing unencrypted development builds if available), the exception vector table is the starting point.

  • Look for the SVC (Supervisor Call) exception handler.
  • This handler typically branches to a dispatch table based on the immediate value in the SVC instruction or a register (often x0 or x8).

Mapping the Endpoints:
The sepOS is modular. It consists of the kernel and several user-mode "apps" or "tasks" running within the Enclave.

  • SEPOS Kernel: Handles memory management, scheduling, and IPC routing.
  • L4 Apps: Distinct binaries for distinct functions.
    • biometrickitd: Handles FaceID/TouchID processing.
    • securekeyvault: Manages the Keychain and Data Protection classes.
    • sigp: The Secure Enclave Signal Processor (for neural network operations).

By tracing the IPC messages dispatched from the Mailbox handler, you can map which L4 task handles which service. For example, a message with Endpoint ID 0x10 might route to the biometrickitd task. Analyzing the message parsing logic within that specific task reveals the proprietary protocol used for biometric authentication.

Tooling Note: Standard tools like IDA Pro or Ghidra require custom loaders for sepOS binaries. The memory layout is non-standard, and the binary format (Mach-O) often has stripped headers or non-standard segment protections that must be manually reconstructed based on the SCIP configuration found in the Boot Monitor logic.

3.0 The Chain of Trust: Firmware & Bootloaders

With the hardware root of trust established and the Secure Enclave operating as a parallel authority, the Application Processor begins the process of bootstrapping the mutable software stack. This phase is governed by the Image4 serialization format and a strict chain of cryptographic handover.

3.1 Low-Level Bootloader (LLB)

The Low-Level Bootloader (LLB) is the first piece of mutable code executed by the Application Processor. Loaded by the Boot ROM from the boot partition of the NAND (or NOR SPI on some development boards), it executes initially within the constraints of the SoC's SRAM. Its primary directive is architectural: it must bridge the gap between the raw silicon state and the feature-rich environment required by iBoot.

3.1.1 Parsing the Image4 (img4) Container

To the reverse engineer, "firmware" on Apple Silicon is synonymous with Image4. LLB is not a raw binary; it is encapsulated in an Image4 container, a format based on ASN.1 (Abstract Syntax Notation One) and DER (Distinguished Encoding Rules). Understanding this structure is prerequisite to any firmware analysis.

The Image4 container consists of three distinct sequences:

  1. IM4P (Payload): The actual executable code (the LLB binary).

    • Encryption: The payload is encrypted using AES-256. On production devices, this is wrapped with the GID Key. This means the payload is opaque to external analysis unless decrypted on-device (or via a GID oracle).
    • Compression: Once decrypted, the payload is typically compressed (LZSS or LZFSE).
    • Type Tag: A 4-character code (e.g., ibot, illb) identifying the component.
  2. IM4M (Manifest): The signature and constraints, commonly known as the ApTicket.

    • The Signature: An RSA or ECDSA signature over the SHA-384 hash of the payload.
    • The Body: A set of entitlements and constraints (tags) that dictate where and how this payload can run.
    • Certificate Chain: The manifest includes the certificate chain leading back to the Apple Root CA (burned into the PKA fuses).
  3. IM4R (Restore Info): (Optional) Contains hardware-specific personalization data used during the restore process, such as the unique nonce generated by the SEP.

The Validation Logic:
When the Boot ROM loads LLB (and when LLB subsequently loads iBoot), it performs the following image4_validate routine:

  1. Parse the ASN.1 structure to separate IM4M and IM4P.
  2. Hash the IM4P (ciphertext).
  3. Locate the corresponding hash in the IM4M (under the specific tag, e.g., illb).
  4. Verify the IM4M signature using the PKA.
  5. If valid, decrypt the IM4P using the AES engine (GID context).

3.1.2 DRAM Training and Memory Controller Configuration

The most critical hardware initialization task performed by LLB is DRAM Training.

When LLB starts, the system is running on internal SRAM (a few megabytes at most). The external LPDDR4X/LPDDR5 Unified Memory is inaccessible because the Memory Controller (MCU) is uninitialized. The physical characteristics of RAM—signal timing, voltage margins, and skew—vary slightly between every physical device due to manufacturing tolerances.

The Training Sequence:

  1. Reading SPD/Calibration Data: LLB reads calibration data from the device tree or dedicated EEPROM areas.
  2. PHY Configuration: It configures the Physical Layer (PHY) interface of the memory controller.
  3. Training Loop: LLB executes a complex algorithm that writes patterns to DRAM and reads them back, adjusting delay lines (DLLs) and drive strengths until the signal is stable.
  4. Remapping: Once training is complete, the MCU is brought online. LLB then reconfigures the Memory Management Unit (MMU) to map the vast expanse of DRAM into the address space.

RE Implication:
If you are attempting to exploit the Boot ROM or early LLB, you are constrained to SRAM. You cannot load large payloads or use heap spraying techniques that require gigabytes of memory until after LLB has successfully trained the DRAM. This creates a "choke point" for early-boot exploits.

3.1.3 Verifying the Exclusive Chip ID (ECID) and Board ID

Apple utilizes a mechanism called Personalization (or Taming) to prevent firmware replay attacks. You cannot simply take a valid, signed LLB from one iPhone and run it on another, nor can you downgrade to an older, vulnerable LLB version.

This enforcement happens inside the Image4 parser logic within LLB (checking the next stage) and the Boot ROM (checking LLB).

The Constraint Tags:
The IM4M manifest contains specific tags that bind the signature to the hardware:

  • ECID (Exclusive Chip ID): A 64-bit unique integer derived from the silicon die's coordinates on the wafer.
  • BORD (Board ID): Identifies the PCB model (e.g., 0x10 for a specific iPhone logic board).
  • CHIP (Chip ID): Identifies the SoC model (e.g., 0x8101 for M1).
  • SDOM (Security Domain): 0x1 for Production, 0x0 for Development.

The Check:
During boot, the executing code reads the actual values from the hardware fuses and compares them against the values present in the signed IM4M.

  • If Hardware.ECID != Manifest.ECID, the boot halts.
  • If Hardware.BORD != Manifest.BORD, the boot halts.

This mechanism, combined with the Nonce (a random value generated by the SEP during updates and baked into the IM4M), ensures that the firmware is:

  1. Authentic: Signed by Apple.
  2. Targeted: Valid only for this specific device.
  3. Fresh: Valid only for this specific boot/update cycle (preventing downgrades).

Note: In the "Tahoe" architecture, this verification logic is hardened against fault injection. The comparison is often performed using redundant variables and bitwise checks that resist simple instruction skipping (e.g., glitching a B.NE instruction).

3.2 iBoot (Stage 2 Bootloader)

Once LLB has initialized the DRAM and verified the next stage, it hands off execution to iBoot. While LLB is a hardware-focused shim, iBoot is a sophisticated, single-threaded operating system in its own right. It contains a full USB stack, a display driver (for the Apple logo), a filesystem driver (APFS/HFS+), and the logic required to bootstrap the XNU kernel. In the Tahoe architecture, iBoot's role has expanded to become the orchestrator of the new security domains.

3.2.1 Device Tree (DT) Flattening

The hardware configuration of an Apple Silicon device is not discoverable via standard buses like PCI enumeration alone. Instead, iBoot relies on a Device Tree (DT)—a hierarchical data structure (similar to OpenFirmware or Linux FDT) that describes the SoC's topology.

The Source:
The raw Device Tree is embedded within the iBoot binary (or loaded as a separate devicetree.img4 payload). It contains nodes describing CPUs, memory maps, interrupt controllers (AIC), and peripherals.

The Flattening Process:
Before jumping to the kernel, iBoot "flattens" this tree into a binary format (FDT) and populates the /chosen node with runtime parameters.

  • kaslr-seed: A high-entropy random value generated by the TRNG. The kernel uses this to randomize its slide.
  • memory-map: A critical array of structures defining physical memory regions. iBoot marks regions used by the Boot ROM, LLB, and itself as reserved, ensuring the kernel does not overwrite them.
  • boot-args: The command-line arguments passed to the kernel (e.g., debug=0x14e, -v). On production devices, these are strictly filtered; only specific flags are allowed unless the device is "demoted" or in a specific research state.

3.2.2 New in Tahoe: Loading the Security Monitors

In pre-Tahoe architectures (iOS 14 / macOS 11), iBoot would simply load the kernelcache and jump to it. In the Tahoe era (A15/M2+), iBoot must construct the Guarded Execution Environment before the kernel can exist.

Allocation and Reservation:
iBoot parses the device tree to identify physical memory ranges reserved for the new monitors. It carves these out of the available DRAM:

  • SPTM Region: Reserved for the Secure Page Table Monitor.
  • TXM Region: Reserved for the Trusted Execution Monitor.

Payload Loading:
iBoot locates the specific Image4 payloads within the kernelcache container:

  • Ap,SecurePageTableMonitor: The GL2 binary.
  • Ap,TrustedExecutionMonitor: The GL1 binary.

It decrypts and verifies these payloads just like any other firmware component. However, instead of loading them into standard memory, it loads them into the reserved physical regions identified above.

Locking SPRR Regions:
This is the critical security pivot. Before handing off control, iBoot configures the Shadow Permission Remapping Registers (SPRR) for the initial state.

  1. It configures the GL2 (SPTM) SPRR view to have Read/Write/Execute access to its own memory region.
  2. It configures the GL1 (TXM) SPRR view to have access to its region.
  3. Crucially, it configures the GL0 (Kernel) SPRR view to mark the SPTM and TXM regions as Inaccessible (No-Access).

This ensures that when the processor eventually drops to EL1 (GL0) to run XNU, the kernel is physically incapable of reading or modifying the monitor code, even though it resides in the same physical DRAM.

3.2.3 LocalPolicy & BAA: The Shift to Local Signing

For macOS, Apple introduced a mechanism to allow users to boot older OS versions or custom kernels (Permissive Security) without breaking the hardware chain of trust. This is managed via LocalPolicy.

The Problem:
The Boot ROM and LLB enforce strict signature checks against Apple's global signing server (TSS). If you want to boot a custom kernel, you can't get a valid signature from Apple.

The Solution:

  1. LocalPolicy: A policy file stored on the Data Volume (in the iSCPreboot volume). It specifies the security mode (Full, Reduced, Permissive) and the hash of the custom kernel collection.
  2. BAA (Basic Attestation Authority): When a user authorizes a downgrade (via Recovery Mode authentication), the Secure Enclave generates a Local Signing Key.
  3. Re-Signing: The LocalPolicy is signed by this Local Key (inside the SEP).
  4. Boot Time: iBoot fetches the LocalPolicy. It asks the SEP to verify the signature. If the SEP confirms the policy is valid (and matches the user's intent), iBoot proceeds to load the custom kernel hash specified in the policy, effectively "blessing" it for this boot cycle.

This allows "Permissive Security" to exist while keeping the Boot ROM and LLB strictly locked down.

3.2.4 RE Focus: Decrypting iBoot Payloads via the AES MMIO Interface

To analyze iBoot, one must decrypt it. Since the GID key is fused into the silicon and physically disconnected from the CPU's register file, it cannot be extracted via software. Reverse engineers must instead turn the device into a Decryption Oracle by manipulating the dedicated AES hardware peripheral.

The kbag Mechanism:
The Image4 payload (IM4P) is encrypted with a random, per-file symmetric key (the target key). This target key is wrapped (encrypted) with the GID key and stored in the IM4P header as a Keybag (kbag). To decrypt the firmware, one must unwrap this kbag.

The Hardware Distinction (ISA vs. MMIO):
It is critical to distinguish between the ARMv8 Crypto Extensions (instructions like AESE, AESD) and the Apple AES Peripheral.

  • ARMv8 Crypto: Operates on keys loaded into standard NEON/SIMD registers (v0-v31). Useful for TLS or disk encryption where the key is known to the OS.
  • Apple AES Peripheral: A memory-mapped I/O (MMIO) block, typically located at a base offset like 0x23D2C0000 (on M1/T8103) or similar 0x2... ranges on newer SoCs. This peripheral has exclusive hardware access to the GID key fuses.

The Oracle Exploit:
Using a Boot ROM exploit (like checkm8 on A-series) or a specialized iBoot exploit, researchers execute a payload that drives this MMIO interface directly:

  1. Reset: Reset the AES peripheral via the AES_CTRL register to clear internal state.
  2. Key Selection: Write to the configuration register to select the GID Key as the decryption source. This sets an internal mux; the key itself is never exposed to the bus.
  3. FIFO Loading: Write the kbag (IV + Ciphertext) into the AES_DATA_IN FIFO registers.
  4. Execution: Trigger the engine. The hardware pulls the GID key from the fuses, performs the AES-256-CBC unwrap, and pushes the result to the output buffer.
  5. Extraction: Read the unwrapped target key (typically formatted as iv:key) from the AES_DATA_OUT register.

Bit-Flipping and Timing Countermeasures:
Modern Apple Silicon (A12+/M1+) implements countermeasures against this oracle usage. The AES engine may enforce a state machine that requires the output of a GID decryption to be immediately DMA'd to executable memory and jumped to, rather than read back into a general-purpose register. Bypassing this often requires Fault Injection (voltage glitching) to corrupt the state machine or precise timing attacks to race the hardware's "sanitize on read" logic, allowing the extraction of the plaintext key before the hardware scrubs it.

4.0 The Security Monitor Layer (GL1/GL2): The New Hypervisor

In the "Tahoe" architecture, the XNU kernel has been demoted. It no longer possesses the ultimate authority to define the virtual memory layout of the system. That power has been migrated to a hardware-enforced monitor running in a proprietary execution state. This section dissects the mechanics of this new layer, which effectively functions as a lightweight, silicon-enforced hypervisor for the kernel itself.

4.1 The Secure Page Table Monitor (SPTM) - GL2

The Secure Page Table Monitor (SPTM) operates at Guarded Level 2 (GL2). While architecturally sharing the EL1 exception level with the kernel, the Guarded Execution Feature (GXF) ensures that GL2 has a strict superset of permissions compared to the kernel's GL0. The SPTM is the sole entity permitted to write to the physical pages that constitute the translation tables (TTBR0/TTBR1).

4.1.1 The GENTER and GEXIT Instructions: Context Switching

Transitions into the SPTM are not handled by standard ARM exception vectors (VBAR_EL1). Instead, they utilize the proprietary GENTER instruction, which performs a synchronous, atomic context switch.

The GENTER ABI:
To invoke the SPTM, the kernel populates specific registers and executes the opcode.

  • Opcode: 0x00201420 (Little Endian).
  • x15 (Selector): The Dispatch ID. This integer identifies which SPTM primitive to execute.
  • x0 - x7 (Arguments): The parameters for the call (e.g., physical addresses, permission flags).
  • x16 / x17: Often used as scratch or secondary dispatch modifiers in newer revisions.

The Hardware Transition:
Upon execution of GENTER:

  1. SPRR Switch: The hardware swaps the active Shadow Permission Remapping Register configuration. The memory regions containing the SPTM code and data—previously invisible to the kernel—become Read/Write/Execute. Conversely, the kernel's own text might become Read-Only or Non-Executable depending on the monitor's logic.
  2. Stack Switch: The Stack Pointer (SP) is switched to the SP_GL2 register, pointing to a dedicated secure stack within the SPTM's private memory.
  3. PC Jump: Execution jumps to the vector defined in GXF_ENTRY_EL1.

The Return:
The SPTM returns control to the kernel using GEXIT (0x00201400). This restores the GL0 SPRR configuration and the kernel's stack pointer. Crucially, GEXIT clears sensitive registers to prevent data leakage from the secure context.

4.1.2 The Frame Table (FTE): Tracking Physical Reality

To enforce security, the SPTM cannot rely on the kernel's data structures (like vm_page_t), as they are mutable by a compromised kernel. Instead, the SPTM maintains its own "God View" of physical memory called the Frame Table.

The Frame Table is a linear array of Frame Table Entries (FTE), located in SPTM-private memory. There is one FTE for every 16KB page of physical RAM.

FTE Structure (Reconstructed):
While the exact struct evolves, it generally contains:

  • Type (Bitfield): The strict classification of the page.
    • XNU_DATA: Generic kernel heap/stack.
    • XNU_TEXT: Immutable kernel code.
    • PAGE_TABLE: A page containing translation entries (TTEs).
    • USER_DATA: Memory mapped to EL0 processes.
    • SPTM_PRIVATE: Internal monitor structures.
  • Refcount: Tracks how many virtual mappings point to this physical page.
  • OwnerID: Identifies the Trust Domain (e.g., Kernel vs. Exclave).

The Security Invariant:
The SPTM enforces that a physical page can only be mapped into a virtual address space if the mapping permissions are compatible with the page's Type. For example, a page marked XNU_DATA in the FTE cannot be mapped as Executable. A page marked PAGE_TABLE cannot be mapped as Writable by the kernel.

4.1.3 The Dispatch Table: Reverse Engineering the Selectors

The interface between XNU and the SPTM is a strict, register-based API. However, unlike the stable syscall numbers of the BSD layer, the SPTM Dispatch IDs (Selectors) are not guaranteed to remain static across macOS versions. Apple frequently rotates these IDs between major (and occasionally minor) releases to frustrate static analysis tools and exploit chains that rely on hardcoded offsets.

The ABI:

  • x15: The Dispatch ID (Selector).
  • x0 - x7: Arguments (Physical Addresses, Permission Bitmasks, ASIDs).
  • x16 / x17: Scratch registers, occasionally used for secondary modifiers or sub-ops.

Heuristic Identification:
Since relying on static IDs (e.g., 0x00) is brittle, reverse engineers must fingerprint the logic of the handler functions within the Ap,SecurePageTableMonitor binary to identify the primitives.

  • sptm_retype(ppn, old_type, new_type) (Often ID 0x00 or 0x01):

    • Fingerprint: Look for a function that accepts a Physical Page Number (PPN), reads the corresponding Frame Table Entry (FTE), and performs a Sanitization Loop. The SPTM must zero-fill (bzero) or cache-invalidate the page before transitioning it from XNU_DATA to PAGE_TABLE to prevent the kernel from initializing a page table with pre-computed malicious entries.
    • Logic: assert(refcount == 0); memset(pa, 0, PAGE_SIZE); fte->type = new_type;
  • sptm_map(asid, va, ppn, perms):

    • Fingerprint: Look for a function that walks the translation tables (reading physical memory) and performs a Permission Check against the FTE. It will contain logic that explicitly compares the requested perms (e.g., Write) against the fte->type (e.g., XNU_TEXT).
    • Logic: if (fte->type == XNU_TEXT && (perms & WRITE)) panic(); write_tte(...);
  • sptm_unmap(asid, va):

    • Fingerprint: Look for the TLB Invalidation sequence. After clearing a TTE, the SPTM must execute TLBI instructions (e.g., TLBI ASIDE1IS) to ensure the hardware translation lookaside buffer is coherent. The kernel is forbidden from executing TLBI instructions that affect the secure context; it must delegate this to the SPTM.
  • sptm_map_iommu(dart_id, context_id, dva, ppn, perms):

    • Fingerprint: Look for writes to MMIO regions associated with DART controllers, rather than standard RAM. This function validates that the ppn is not a protected kernel page before mapping it into a device's IOVA space.

RE Implication:
Automated analysis scripts should not rely on CMP x15, #0x1. Instead, they should symbolically execute the GENTER handler in the SPTM binary, identifying the dispatch table jump, and then classify the target functions based on the presence of DC ZVA (cache zero), TLBI, or FTE array access patterns.

4.1.4 RE Focus: Analyzing Panic Strings and the State Machine

The SPTM is designed to be Fail-Secure. Unlike standard kernel APIs that return KERN_FAILURE, the SPTM treats invalid requests as evidence of kernel compromise.

The Panic Mechanism:
If XNU sends a malformed request (e.g., trying to retype a page that is still mapped), the SPTM does not return. It triggers a system-wide panic.

  • Panic String: "received fatal error for a selector from TXM" or "invalid state transition".
  • Analysis: These strings are gold for reverse engineers. They confirm that the SPTM enforces a strict Finite State Machine (FSM) for memory pages.

Mapping the State Machine:
By analyzing the panic logic, we can deduce the allowed transitions:

  1. FREE $\rightarrow$ XNU_DATA (Allocation)
  2. XNU_DATA $\rightarrow$ PAGE_TABLE (Retype for MMU use - requires sanitization)
  3. PAGE_TABLE $\rightarrow$ XNU_DATA (Teardown - requires unmapping all entries)
  4. XNU_DATA $\rightarrow$ XNU_TEXT (KEXT loading - One-way transition!)

Any attempt to deviate from this graph (e.g., trying to turn PAGE_TABLE directly into XNU_TEXT) results in an immediate halt. This prevents "Page Table Spraying" and other heap manipulation techniques used to gain kernel execution.

4.2 The Trusted Execution Monitor (TXM) - GL1

If the SPTM is the brawn—enforcing the physics of memory mapping—the Trusted Execution Monitor (TXM) is the brains. Operating at Guarded Level 1 (GL1), the TXM is the supreme arbiter of system policy. It represents the architectural decoupling of "mechanism" from "policy." While the SPTM handles how a page is mapped, the TXM decides if it is allowed to be mapped executable.

4.2.1 Decoupling AMFI: Moving Code Signature Verification

Historically, the Apple Mobile File Integrity (AMFI) kernel extension was the primary enforcement point for code signing. However, as a KEXT running in EL1, AMFI was susceptible to kernel-level memory corruption. Exploits like tfp0 could patch amfi_get_out_of_my_way or swizzle the MAC (Mandatory Access Control) hooks to bypass signature checks.

In the Tahoe architecture, the core verification logic has been lifted out of the kernel and placed into the TXM. Crucially, the TXM does not necessarily parse the full CMS blob on every request; its primary currency of trust is the Code Directory Hash (CDHash).

The Verification Flow:

  1. Load & Hash: The kernel (XNU) loads a binary into memory (typed as XNU_DATA). It parses the LC_CODE_SIGNATURE load command and calculates the CDHash (SHA-256) of the Code Directory.
  2. The Query: XNU issues a GENTER call to the TXM. It passes the CDHash and the physical address of the memory range.
  3. Trust Cache Lookup: The TXM first checks its internal Trust Caches (located in GL1 memory).
    • Static Trust Cache: Contains CDHashes for all immutable OS binaries (from the Cryptex).
    • Dynamic Trust Cache: Contains CDHashes for recently verified third-party apps.
  4. Cold Validation: If the CDHash is not found (a cache miss), the system enters a "Cold Start" validation path. The kernel (often aided by amfid in userland for complex policy checks) must provide the CMS signature blob to the TXM. The TXM performs the cryptographic verification of the blob against the Apple Root CA (or Developer ID Root) within the secure world. If valid, the CDHash is promoted to the Dynamic Trust Cache.
  5. Bless: Once the CDHash is validated (either via Cache Hit or Cold Validation), the TXM updates its internal state to "bless" the specific physical pages associated with that CDHash.
  6. Enforcement (GL2): When XNU subsequently asks the SPTM to map those pages as Executable (RX), the SPTM queries the TXM: "Is this physical page verified?" If the TXM has not blessed the CDHash associated with those pages, the SPTM denies the Execute permission.

RE Implication: Patching the kernel to ignore signature errors is now futile. Even if XNU "thinks" a binary is signed and attempts to set the executable bit in the PTE, it lacks the hardware authority to do so. The SPTM will simply refuse the PTE update because the TXM never validated the CDHash.

4.2.2 The Trust Cache: Static vs. Dynamic

To avoid the performance penalty of cryptographic verification on every page fault, the TXM manages the Trust Cache—a database of known-good CDHashes.

  • The Static Trust Cache:
    This is loaded by iBoot and passed to the TXM during the GL1 initialization. It contains the hashes of every binary in the OS (now encapsulated in the immutable Cryptexes). This cache resides in GL1 memory and is strictly Read-Only.

  • The Dynamic Trust Cache:
    This handles third-party applications and JIT regions. When a user launches an app, the TXM verifies the signature once and adds the CDHash to the Dynamic Trust Cache.

    • Query Interface: The kernel queries the Trust Cache via a specific GENTER selector.
    • Attack Surface: The Dynamic Trust Cache is a mutable structure in GL1. A logic bug in the TXM's management of this cache (e.g., a race condition during entry removal or a hash collision attack) is a high-value target for persistence.

4.2.3 Developer Mode Enforcement and Downgrade Protection

The TXM is also the guardian of the device's security posture, specifically Developer Mode.

In previous iterations, enabling debugging capabilities was often a matter of setting nvram variables or boot-args (like cs_enforcement_disable=1). In Tahoe, these states are managed by the TXM.

The State Transition:
Enabling Developer Mode requires a reboot and explicit user authorization (Secure Intent via physical buttons). The TXM persists this state (likely via the Secure Enclave's secure storage).

Downgrade Protection:
The TXM enforces that the system cannot transition from a "Production" state to a "Developer" state without a full reboot and authentication ceremony. This prevents a kernel-level attacker from dynamically relaxing security policies to load unsigned modules.

Furthermore, the TXM validates the LocalPolicy (signed by the SEP) to determine if the system is booting in "Permissive Security" mode. If the LocalPolicy signature is invalid or missing, the TXM defaults to "Full Security," rejecting any code not signed by the Apple Root CA, regardless of what the kernel requests.

5.0 XNU Kernel Initialization: Entering EL1

The handoff from iBoot to the XNU kernel marks the transition from a single-threaded bootloader to a symmetric multiprocessing (SMP) operating system. However, in the Tahoe architecture, this is no longer a handover of absolute power. The kernel enters Exception Level 1 (EL1) not as a master, but as a client of the Guarded Level 2 (GL2) monitor.

The entry point is defined in osfmk/arm64/start.s. At this precise moment, the system state is fragile: the MMU is likely disabled (or running on an identity map provided by iBoot), interrupts are masked (DAIF bits set), and the stack pointer is essentially arbitrary. The kernel's first objective is to orient itself within physical memory, calculate the KASLR slide, and establish the virtual memory structures required to turn on the lights.

5.1 The start routine and KASLR

The _start symbol is the architectural entry point. Unlike x86_64, where the kernel might handle its own decompression and relocation, the Apple Silicon kernel is loaded as a raw Mach-O executable (within the kernelcache container) directly into physical memory by iBoot.

The Register State at Entry:

  • x0: Physical address of the boot_args structure (version 2).
  • x1: Physical address of the Device Tree base (if not inside boot_args).
  • x2: 0 (Reserved).
  • x3: 0 (Reserved).
  • sp: Invalid/Temporary.

5.1.1 Deriving the Kernel Slide: The Dual-Slide Paradigm

Kernel Address Space Layout Randomization (KASLR) on Apple Silicon is a cooperative effort between iBoot and XNU. Unlike x86_64, where the kernel might handle its own relocation, the Apple Silicon kernel is loaded as a raw Mach-O executable directly into physical memory by iBoot. However, in the Tahoe architecture, KASLR has evolved from a simple obfuscation technique into a compartmentalized security boundary.

The boot_args Structure:
Upon entry at _start, the kernel immediately parses the boot_args structure (version 2) pointed to by x0. This structure acts as the handover manifest, containing the virtBase (the static link address, typically 0xFFFFFFF007004000) and the physBase (the actual physical load address in DRAM).

The Slide Calculation:
The slide is not generated by the kernel at runtime; it is consumed. iBoot generates a high-entropy value from the TRNG, populates the /chosen/kaslr-seed property in the Device Tree, and physically relocates the kernel text in DRAM to match this slide.

The kernel calculates its own slide using the delta between the compile-time base and the runtime physical base (adjusted for the static virtual-to-physical offset):

$$ \texttt{vm_kernel_slide} = \texttt{boot_args.virtBase} - \texttt{CompileTimeBase} $$

The Tahoe Constraint: Entropy Decorrelation:
In the Tahoe architecture, the system operates under a Dual-Slide Paradigm. The SPTM (GL2) and the Kernel (GL0/EL1) reside in the same physical DRAM but operate in distinct translation regimes.

  1. Kernel Slide: Randomized by iBoot based on kaslr-seed.
  2. SPTM Slide: Randomized by iBoot based on a separate, decorrelated entropy source (or a cryptographic derivation of the master seed that is not exposed to EL1).

Security Implication:
This decorrelation is critical. A kernel-level memory leak (e.g., an infoleak revealing a kernel pointer) allows an attacker to calculate vm_kernel_slide. In previous architectures, if the monitor (PPL) was mapped at a fixed offset relative to the kernel, a kernel leak would instantly reveal the monitor's location.

In Tahoe, knowing vm_kernel_slide yields zero information about the virtual address of the SPTM. The SPTM's virtual mapping is established by iBoot in the GL2 translation tables (TTBR1_EL2 context) before the kernel executes. The kernel is aware of the SPTM's physical pages (marked as "Reserved" in the memory map to prevent the VM subsystem from overwriting them), but it is architecturally blind to the SPTM's virtual location.

RE Focus: Finding the Slide:
For a reverse engineer with a kernel panic log or a JTAG connection, identifying these slides requires inspecting distinct registers:

  • Kernel Slide: Inspect TTBR1_EL1. The translation table base points to the physical location of the kernel's L1 table. The high bits of the PC (Program Counter) at the exception vector reveal the virtual slide.
  • SPTM Slide: This is invisible from EL1. To find it, one must inspect TTBR1_EL2 (or the proprietary system register aliasing the GL2 translation base) via JTAG while the core is halted in the GL2 context.
  • Static Analysis: The vm_kernel_slide global variable in XNU is one of the first initialized. In a raw memory dump, locating the boot_args struct (often at the start of a physical page aligned to 16KB) will reveal the virtBase directly.

5.1.2 Initializing the MMU: TCR_EL1 and the SPTM Handshake

Before the kernel can execute C code safely, it must enable the Memory Management Unit (MMU). On standard ARMv8, this involves populating translation tables and writing to TTBR0_EL1 (User) and TTBR1_EL1 (Kernel), then setting SCTLR_EL1.M.

On Tahoe, this process is fundamentally altered because the kernel cannot write to its own page tables.

The Bootstrap Problem:
How does the kernel build its initial page tables if it requires the SPTM to map pages, but the SPTM requires the kernel to make hypercalls?

The Solution: The Bootstrap Tables:
iBoot constructs a set of initial "Bootstrap Page Tables" before handing off control. These tables are identity-mapped (Physical == Virtual) for the PC and stack, but also contain the kernel's high-virtual mappings.

  1. TCR_EL1 Setup: The kernel configures the Translation Control Register (TCR_EL1).
    • T1SZ / T0SZ: Defines the size of the virtual address space (typically 36-bit or 39-bit on iOS, 48-bit on macOS).
    • TG1: Granule size (16KB is standard for Apple Silicon, unlike the 4KB standard of Android/Linux).
    • IPS: Intermediate Physical Address Size (matches the SoC capability, e.g., 40 bits).
    • TBI1 (Top Byte Ignore): Critical for PAC. This bit must be set to 1. It tells the MMU to ignore the top byte (bits 63-56) during address translation, allowing PAC signatures to exist in valid pointers without causing translation faults.

The SPTM Handshake (The First GENTER):
Once TCR_EL1 is configured, the kernel must transition from the iBoot-provided bootstrap tables to its own managed tables.

  1. Allocation: The kernel allocates physical pages for the new L1/L2/L3 translation tables from the XNU_DATA pool.
  2. Sanitization: The kernel zeroes these pages.
  3. Retype: The kernel executes GENTER (Selector 0x00 - sptm_retype) to convert these pages from XNU_DATA to PAGE_TABLE.
  4. Mapping: The kernel executes GENTER (Selector 0x01 - sptm_map) to populate the entries, replicating the kernel text and static data mappings.
  5. Activation: Finally, the kernel writes the physical address of the new L1 table to TTBR1_EL1.

The SCTLR_EL1 Lockdown:
The final step of start is writing to the System Control Register (SCTLR_EL1).

  • M (MMU Enable): Set to 1.
  • C (Cache Enable): Set to 1.
  • WXN (Write-XOR-Execute): Set to 1.

In Tahoe, the SPTM monitors writes to SCTLR_EL1. If the kernel attempts to disable WXN (a common technique in older jailbreaks to patch kernel text), the SPTM intercepts the system register write and panics the device. The hardware configuration enforced by GL2 ensures that SCTLR_EL1 modifications are privileged operations that must comply with the system's security policy.

Once the MMU is active and the kernel is running on its own page tables (managed by SPTM), the start routine branches to arm_init, beginning the high-level initialization of the BSD subsystem and IOKit.

5.2 Hardware Security Enforcements (The "Kill Switch" Registers)

As the kernel initialization sequence progresses through start, it reaches a critical inflection point. The memory management structures are initialized, and the kernel is about to transition from a setup phase to a runtime phase. To prevent a compromised runtime kernel from modifying its own logic, the initialization routine must engage the hardware "Kill Switches."

These are proprietary system registers that, once written to, become immutable until a full system reset. In the pre-Tahoe era, these registers were the primary defense against persistent kernel compromises. In the Tahoe architecture, they serve as a hardware-enforced baseline that the SPTM relies upon to maintain the invariant of Kernel Text Immutability.

5.2.1 KTRR (Kernel Text Read-Only Region): The Physical Lock

Kernel Text Read-Only Region (KTRR) is Apple’s hardware solution to the "W^X" (Write XOR Execute) problem at the physical memory controller level. While the MMU (via page tables) controls virtual access permissions, page tables are mutable data structures. If an attacker gains arbitrary read/write (KRW) access to the kernel, they could theoretically modify the page tables to make the kernel text writable.

KTRR bypasses the MMU entirely. It enforces permissions based on Physical Addresses (PA) directly within the Memory Controller (MCU).

The Register Interface:
KTRR is controlled via a set of proprietary system registers, typically accessible via MSR instructions at EL1 (or GL2 in Tahoe).

  • KTRR_LOWER_EL1 (S3_4_c15_c2_3): Defines the physical start address of the protected range.
  • KTRR_UPPER_EL1 (S3_4_c15_c2_4): Defines the physical end address.
  • KTRR_LOCK_EL1 (S3_4_c15_c2_2): The kill switch. Writing 1 to the lock bit enables the protection.

The "RoR" (Read-only Region) Mechanism:
Once the lock bit is set:

  1. Write Protection: Any write transaction (store instruction or DMA) targeting a physical address within the [LOWER, UPPER] range is dropped by the memory controller. Depending on the SoC generation, this either fails silently or triggers a synchronous external abort (SError).
  2. Execute Protection: The memory controller ensures that instruction fetches are permitted from this region.
  3. Immutability: The KTRR_LOCK_EL1 register itself becomes Read-Only. It cannot be unlocked by software.

The Tahoe Evolution (Virtualization of KTRR):
On M3/M4 chips running the SPTM, the kernel's interaction with KTRR changes. Since the kernel (EL1) is deprivileged, it cannot be trusted to set up KTRR correctly (it might set the range to zero, leaving itself unprotected).

  • SPTM Enforcement: The SPTM configures the physical KTRR registers during its own initialization (in Ap,SecurePageTableMonitor).
  • Virtualization: When the XNU kernel executes the legacy instructions to write to KTRR_LOWER/UPPER in start, the hardware traps these accesses to GL2. The SPTM validates that the kernel is attempting to cover the correct physical range (matching the XNU_TEXT entries in the Frame Table) and effectively "mocks" the success of the operation to the kernel, while ensuring the hardware is actually locked down according to the SPTM's policy.

RE Focus: The KTRR Slide Alignment
Because KTRR operates on physical ranges, it lacks the granularity of 4KB/16KB pages. It typically operates on larger blocks (e.g., 1MB or L2 cache line boundaries). This forces the KASLR slide to be aligned to the KTRR granularity. If you are brute-forcing the KASLR slide, knowing the KTRR alignment constraint significantly reduces the entropy search space.

5.2.2 Kernel Integrity Protection (KIP): Extending the Shield

KTRR protects the static kernel binary (kernelcache). However, modern macOS relies heavily on the Boot Kernel Collection (BKC) and Auxiliary Kernel Collection (AKC)—large caches of drivers and extensions loaded during boot. These reside in memory adjacent to the kernel but are technically distinct payloads.

Kernel Integrity Protection (KIP) is the architectural evolution designed to protect these dynamic-but-immutable regions.

The Mechanism:
Unlike KTRR, which typically defines a single contiguous range, KIP (often implemented via the GXF or APRR logic on newer chips) allows for a more flexible definition of immutable regions.

  1. Registration: During the start routine, the kernel iterates over the loaded extensions (IOKit drivers).
  2. Sealing: Once the extensions are linked and relocated, the kernel issues a call to "seal" the region. In Tahoe, this is a GENTER call to the SPTM (Selector sptm_retype or sptm_protect).
  3. Retyping: The SPTM updates the Frame Table Entries (FTE) for the physical pages backing the drivers. It transitions them from XNU_DATA (Writable) to XNU_TEXT (Executable/Read-Only).

The "One-Way" Door:
The security invariant enforced here is that memory typed as XNU_TEXT is never writable by EL1.

  • If the kernel attempts to write to a KIP-protected page, the SPRR configuration for GL0 (Kernel) triggers a permission fault.
  • If the kernel attempts to ask the SPTM to map it as writable, the SPTM checks the FTE, sees XNU_TEXT, and panics.

This effectively turns the kernel extensions into ROM. This mitigates a massive class of rootkits that historically operated by patching IOKit vtables or function pointers in memory.

5.2.3 The System Control Register (SCTLR_EL1) Lockdown

The final "Kill Switch" is the configuration of the ARM processor itself. The SCTLR_EL1 register controls the MMU, caches, and alignment checks.

Critical Bits:

  • WXN (Write-XOR-Execute): Bit 19. When set, any memory region mapped as Writable is implicitly treated as Non-Executable (XN). This prevents the execution of shellcode on the heap or stack.
  • M (MMU Enable): Bit 0.
  • A (Alignment Check): Bit 1.

The Trap-and-Emulate Trap:
In a standard ARM system, EL1 can modify SCTLR_EL1 at will. An attacker with code execution could simply write to SCTLR_EL1 to disable WXN, map their shellcode, and execute it.

In the Tahoe architecture, SCTLR_EL1 is a trapped resource.

  1. The Trap: The hardware is configured (via HCR_EL2 or proprietary GXF controls) to trap writes to SCTLR_EL1 to the monitor layer (GL2/SPTM).
  2. The Policy: When the kernel executes MSR SCTLR_EL1, x0, the CPU switches to the SPTM.
  3. The Check: The SPTM inspects the value in x0. It enforces a strict policy: WXN must always be 1.
  4. The Verdict: If the kernel attempts to clear WXN, the SPTM denies the write and panics the system with a "Security Violation" code.

This ensures that the fundamental security properties of the execution environment (W^X) cannot be disabled, even by the kernel itself. The kernel is permanently locked into a secure configuration from the moment start completes.

5.3 Exclaves: The Microkernel within the Monolith

The introduction of Exclaves in the Tahoe architecture represents the most profound structural change to the Apple OS ecosystem since the transition from Mac OS 9 to OS X. It is an admission that the monolithic kernel architecture (XNU) has become too large, too complex, and too mutable to serve as the ultimate Trusted Computing Base (TCB) for high-value assets.

Exclaves introduce a Microkernel architecture running side-by-side with the monolithic XNU kernel on the same Application Processor cores. Unlike the Secure Enclave (which is a separate coprocessor with limited compute power), Exclaves harness the full performance of the M-series P-cores and E-cores while maintaining cryptographic isolation enforced by the SPTM.

5.3.1 The L4 Influence: Domains, Conclaves, and IPC

The architecture of the Exclave system is heavily indebted to the L4 microkernel family (specifically seL4). It prioritizes minimalism, capability-based security, and strict isolation.

The Hierarchy of Isolation:

  1. The Secure Kernel (ExclaveOS): A tiny, formally verifiable kernel that manages scheduling and IPC within the secure world. It runs at a privilege level guarded by the SPTM (likely GL1 or a restricted GL2 domain).
  2. Domains: The highest level of separation. The "Insecure Domain" hosts XNU and userland. The "Secure Domain" hosts Exclave workloads.
  3. Conclaves: Within the Secure Domain, workloads are siloed into Conclaves. A Conclave is a lightweight container consisting of an address space, a set of capabilities (handles to resources), and threads.
    • Example: The FaceID Conclave cannot access the memory of the Wallet Conclave, even though both run within the Exclave environment.

Memory Management via SPTM:
The isolation is enforced by the SPTM's Frame Table. Physical pages assigned to an Exclave are typed in the FTE (likely as EXCLAVE_DATA or SK_DATA).

  • XNU View: The kernel sees these physical pages as "reserved" or "stolen" in the device tree. Any attempt by XNU to map these pages via sptm_map will result in a panic, as the SPTM forbids mapping Exclave-owned pages into the XNU_DOMAIN.
  • Exclave View: The ExclaveOS requests mappings from the SPTM. The SPTM updates the translation tables specific to the Exclave context (a distinct TTBR or context ID).

5.3.2 RE Focus: The RingGate Mechanism and XNUProxy

For the reverse engineer, the critical question is: How does the Kernel talk to an Exclave? They share no virtual memory, run in different hardware contexts, and the SPTM actively prevents XNU from mapping Exclave physical pages. The bridge is a mechanism internally referred to as RingGate, facilitated by a kernel extension named XNUProxy (com.apple.driver.XNUProxy).

The Communication Stack:

  1. Tightbeam (The IDL):
    Apple has introduced a new Interface Definition Language (IDL) called Tightbeam. It replaces the legacy Mach Interface Generator (MIG) for secure world communication. Unlike MIG, which is loosely typed and message-centric, Tightbeam is strongly typed and buffer-centric, designed to minimize parsing ambiguity.

    • Userland Analysis: The serialization logic is visible in /usr/lib/libTightbeam.dylib. Analyzing this library reveals the wire format: a header containing a protocol version and message ID, followed by a packed struct of arguments. Symbols like tb_message_encode, tb_client_call, and tb_endpoint_create are the primary indicators of this traffic.
    • Kernel Analysis: In XNUProxy, look for the TBTransport C++ classes that wrap the raw ring buffer management.
  2. The Downcall (XNU $\rightarrow$ Exclave):
    When XNU needs a service (e.g., "Authenticate this Passkey"), it cannot call the function directly.

    • Marshaling: XNUProxy serializes the request using Tightbeam into a shared memory ring buffer.
    • The Gate: The kernel executes a specific instruction to trigger the world switch. This is a GENTER instruction targeting a specific Dispatch ID reserved for the Secure Kernel (distinct from the SPTM/TXM selectors).
    • Context Switch: The hardware (mediated by SPTM) saves the EL1 state, switches the SPRR configuration to the Exclave view, and jumps to the ExclaveOS entry point.
    • Dispatch: ExclaveOS inspects the ring buffer and routes the message to the appropriate Conclave's IPC port.
  3. The Upcall (Exclave $\rightarrow$ XNU):
    Exclaves are not completely autonomous; they often rely on XNU for file system I/O or networking (since writing a full network stack in a microkernel is impractical).

    • The Exclave writes a request to the outbound ring buffer.
    • It triggers an interrupt or executes a GEXIT yield.
    • XNUProxy receives the notification, reads the request (e.g., "Write this blob to disk"), performs the operation via standard VFS calls, and returns the result via a Downcall.

Analyzing the XNUProxy Kext:
This kext is the Rosetta Stone for Exclave interaction. It maintains the mapping between Mach Ports (in XNU) and Tightbeam Endpoints (in Exclaves).

  • Endpoint Mapping: Reverse engineering the XNUProxy binary reveals structures that map 64-bit integer IDs to specific Exclave services. It effectively acts as a NAT (Network Address Translation) layer between Mach Ports and Exclave Endpoints.
  • Memory Loaning (The "DART" Window): While control messages go through ring buffers, large data transfers (like camera frames or biometric buffers) occur via Memory Loaning.
    • XNUProxy pins a userland page and retrieves its physical address.
    • It passes this Physical Page Number (PPN) to the Exclave via Tightbeam.
    • The Exclave requests the SPTM to map this specific PPN into its address space.
    • Vulnerability Class: This "Loaned Memory" is a prime target for TOCTOU (Time-of-Check to Time-of-Use) attacks. If the kernel (or a malicious user thread) modifies the content of the physical page after the Exclave has validated the header but before it processes the body, the Exclave's parser could be compromised. The SPTM likely enforces "Shared" vs "Exclusive" ownership states in the Frame Table to mitigate this, but the logic is complex and fragile.

5.3.3 Use Case: Secure Control of Privacy Indicators and Passkeys

The "Killer App" for Exclaves in macOS Tahoe is the hardware-enforced privacy indicator (the green/orange dots).

The Pre-Tahoe Vulnerability:
In previous macOS versions, the "Green Dot" (camera active) was drawn by WindowServer or the kernel. If an attacker achieved kernel-level code execution (Ring 0), they could hook the display driver and suppress the dot while recording the user.

The Tahoe Solution:

  1. Hardware Ownership: The physical frame buffer region corresponding to the status bar indicators is not mapped in the XNU domain. It is owned exclusively by a specific Privacy Conclave.
  2. The DART Lock: The Display Coprocessor's IOMMU (DART) is configured by the SPTM such that the main display pipe cannot write to the indicator pixels. Only the secure overlay pipe, controlled by the Exclave, can write there.
  3. Sensor Interrupts: When the camera sensor is energized, the GPIO interrupt is routed directly to the Exclave (via the AIC - Apple Interrupt Controller).
  4. The Drawing Path: The Exclave receives the "Camera On" interrupt. It writes directly to the secure frame buffer overlay to render the green dot.
  5. Immutability: Because XNU cannot map the physical memory of the secure overlay, and cannot reconfigure the DART (locked by SPTM), a compromised kernel is physically incapable of erasing the indicator.

Passkeys and FIDO:
Similarly, the cryptographic operations for Passkeys (WebAuthn) have moved to an Exclave.

  • Private Keys: The private keys for Passkeys are generated and stored within the Exclave's secure storage (mediated by SEP).
  • Biometric Binding: The Exclave communicates directly with the SEP to verify FaceID/TouchID.
  • Isolation: Even if malware injects code into LocalAuthentication daemon or the kernel, it cannot extract the private key material, as it resides in a memory domain that simply does not exist in the attacker's address space.

6.0 The Mach Subsystem: The Nervous System

While the SPTM and Exclaves represent the new fortress walls of the Apple Silicon architecture, the Mach subsystem remains the internal nervous system that coordinates activity within the XNU kernel. Originating from the NeXTSTEP era, Mach provides the fundamental primitives for Inter-Process Communication (IPC), thread scheduling, and virtual memory management.

For the reverse engineer, Mach is the primary vector for local privilege escalation (LPE). Despite decades of hardening, the complexity of state management in Mach messaging remains a fertile ground for logic bugs, race conditions, and reference counting errors. In the Tahoe era, Mach has been retrofitted with heavy PAC enforcement to protect its object graph.

6.1 Mach Ports & IPC Primitives

At the conceptual level, Mach is an object-oriented kernel. The fundamental unit of addressing is the Mach Port. To a userland process, a port is merely a 32-bit integer handle (mach_port_name_t). To the kernel, it is a complex, reference-counted data structure (ipc_port) that acts as a unidirectional communication channel.

6.1.1 Port Rights: Receive, Send, Send-Once, and Dead Names

The security model of Mach is capability-based. Possessing a port name is meaningless without the associated Port Right. The kernel tracks these rights in the process's IPC space.

  • MACH_PORT_RIGHT_RECEIVE: The ownership right. Only one task can hold the Receive right for a specific port at any given time. This task is the destination for messages sent to the port.
    • Kernel Structure: The ipc_port struct contains a pointer (ip_receiver) to the ipc_space of the task holding this right.
  • MACH_PORT_RIGHT_SEND: The ability to queue messages into the port. Multiple tasks can hold send rights to the same port. This is the standard "client" handle.
  • MACH_PORT_RIGHT_SEND_ONCE: A "fire-and-forget" right that vanishes after a single message is sent. This is critical for the Request/Reply pattern (RPC). When a client sends a message, it typically includes a MAKE_SEND_ONCE right to its own reply port. The server uses this to send exactly one reply, preventing the server from spamming the client later.
  • MACH_PORT_RIGHT_DEAD_NAME: If the task holding the Receive right dies or destroys the port, all outstanding Send rights in other tasks are instantly transmuted into Dead Names. Any attempt to send a message to a dead name returns MACH_SEND_INVALID_DEST.

RE Focus: The ipc_port Structure and PAC:
In previous generations, a common exploit technique involved "Fake Ports"—spraying the heap with crafted data that looked like an ipc_port struct and then tricking the kernel into using it.

In the arm64e/Tahoe architecture, the ipc_port structure is heavily fortified:

  1. ip_object: The base header of the port.
  2. ip_kobject: A pointer to the underlying kernel object (e.g., a task, a thread, or a user-client). This pointer is PAC-signed.
  3. ip_context: A 64-bit context value, also PAC-signed.

If an attacker attempts to forge a port, they must generate a valid signature for the ip_kobject pointer. Without the APDAKey (Data Key A), the kernel will panic upon AUT execution during message delivery.

6.1.2 The IPC Space (ipc_space) and the Global Name Server

Every task (process) in macOS has an associated IPC Space (ipc_space). This structure acts as the translation layer between the userland integer handles (mach_port_name_t) and the kernel pointers (ipc_port *).

The Translation Table (is_table):
The IPC space is implemented as a dynamic table (or radix tree for large spaces) of IPC Entries (ipc_entry).

  • Index: The userland handle (e.g., 0x103) is essentially an index into this table.
  • Entry: The ipc_entry struct contains:
    • ie_object: A pointer to the actual ipc_port (or ipc_pset). Crucially, in Tahoe, this pointer is signed with PAC.
    • ie_bits: A bitfield storing the rights (Receive, Send, etc.) and the generation number (to detect stale handles).

The Lookup Process (ipc_right_lookup_write):
When a user executes mach_msg(), the kernel:

  1. Retrieves the current_task()->itk_space.
  2. Uses the handle passed in msgh_remote_port to index into the is_table.
  3. Validates the ie_bits to ensure the task actually possesses the required right (e.g., MACH_PORT_RIGHT_SEND).
  4. Authenticates the ie_object pointer using AUT.
  5. Retrieves the ipc_port.

The Global Name Server:
Mach does not have a string-based global namespace in the kernel. "Service Discovery" is implemented in userland by launchd (the Bootstrap Server).

  • Special Ports: The kernel does maintain a small array of "Special Ports" attached to the Host and Task objects.
    • HOST_PORT: Represents the kernel itself.
    • HOST_PRIV_PORT: The "God Mode" port (see Section 6.2).
    • TASK_BOOTSTRAP_PORT: The handle to launchd.

When a process calls bootstrap_look_up("com.apple.foo"), it is actually sending a Mach message to the port stored in its TASK_BOOTSTRAP_PORT slot.

6.1.3 Copy-on-Write (CoW) optimizations in Out-of-Line (OOL) message passing

Mach messages are not limited to small scalars. They can transfer massive amounts of data using Out-of-Line (OOL) descriptors. This mechanism relies on Virtual Memory (VM) tricks rather than data copying, making it highly efficient but historically dangerous.

The Mechanism:

  1. Sender: Includes a mach_msg_ool_descriptor_t in the message, pointing to a buffer in its address space (e.g., 100MB of data).
  2. Kernel Processing: The kernel does not copy the 100MB. Instead, it walks the sender's VM map.
  3. Copy-on-Write (CoW): The kernel marks the physical pages backing that buffer as Read-Only in the sender's map.
  4. Receiver: The kernel maps those same physical pages into the receiver's address space, also as Read-Only.
  5. Faulting: If either the sender or receiver tries to write to the buffer, the MMU triggers a fault. The kernel catches this, allocates a new physical page, copies the data, and updates the mapping for the writer. This preserves the illusion of a copy.

The Tahoe/SPTM Intersection:
In the Tahoe architecture, this VM manipulation is complicated by the SPTM.

  • Permission Downgrade: When the kernel marks the pages as CoW (Read-Only), it cannot simply update the PTEs. It must issue a GENTER call (sptm_protect or sptm_map) to the SPTM to downgrade the permissions of the physical pages in the sender's address space.
  • The Vulnerability Class: This complexity introduces a race window. If the kernel logic fails to correctly lock the VM map object before requesting the SPTM update, or if the SPTM state machine has a logic flaw regarding shared pages (refcount > 1), it might be possible to modify the physical page after the message has been "sent" but before the receiver reads it. This is known as a Double Fetch or Physically-Backed-Virtually-Disjoint attack.

RE Focus:
Analyze vm_map_copyin and vm_map_copyout in the XNU source (or binary). Look for how vm_map_entry structures are flagged with needs_copy and how these flags translate into SPTM calls. The interaction between Mach IPC (which assumes it controls VM) and the SPTM (which actually controls VM) is the friction point where new bugs will likely emerge.

6.2 The host_priv Port

In the lexicon of XNU exploitation, the Host Privilege Port (host_priv) has historically been synonymous with "Game Over." It is the Mach port representing the kernel task itself. Possessing a send right to this port allows a userland process to invoke the host_priv_server subsystem, granting the ability to manipulate physical memory, load kernel extensions (legacy), and control processor sets.

However, in the Tahoe architecture, the omnipotence of host_priv has been severely curtailed. The Secure Page Table Monitor (SPTM) and the Trusted Execution Monitor (TXM) have effectively neutered the "God Mode" capabilities traditionally associated with this handle.

6.2.1 The "God Mode" Handle: Generation and Restriction

The host_priv port is backed by the realhost kernel global structure. Unlike standard task ports, which map to a task_t, the host_priv port maps to the host object, but with a specific bit set in its ip_kobject pointer or context indicating privileged access.

Generation at Boot:
During the kernel bootstrap (osfmk/kern/host.c), the system initializes the host subsystem.

  1. host_init(): Allocates the realhost structure.
  2. Port Allocation: Calls ipc_port_alloc_kernel() to create the port.
  3. Kobject Association: The port is associated with the realhost structure.
    • PAC Enforcement: On arm64e, the ip_kobject pointer stored in the port structure is signed using the APDAKey (Data Key A) combined with the address of the port structure as the context. This prevents an attacker with a kernel read/write primitive from simply overwriting the ip_kobject of a user port to point to realhost.

Distribution:
The kernel is extremely stingy with this port. It is vended primarily to:

  • kernel_task: The kernel itself holds the receive right.
  • launchd (PID 1): Receives a send right during the userland bootstrap handoff.
  • kextd / kernelmanagerd: Required for managing kernel extensions (though this role is diminishing with DriverKit).

The "TFP0" Fallacy in Tahoe:
Historically, obtaining host_priv allowed an attacker to call task_for_pid(host_priv, 0, &kernel_task). This returned the kernel_task port, granting full vm_read/vm_write access to the kernel's entire address space (TFP0).

In the Tahoe architecture, this chain is broken by the SPTM:

  1. task_conversion_eval: Even if you possess host_priv, the kernel checks the System Integrity Protection (SIP) policy managed by the TXM. If SIP is active, task_for_pid(0) returns KERN_FAILURE.
  2. The Immutable Kernel: Even if you bypass the SIP check (e.g., via a logic bug), and obtain the kernel_task port, the vm_write operations are intercepted.
    • If you attempt to write to kernel text (XNU_TEXT), the SPTM panics the system (Permission Violation).
    • If you attempt to write to page tables (PAGE_TABLE), the SPTM panics the system.
    • If you attempt to write to Data Const (XNU_DATA_CONST), the SPTM panics.

Thus, in Tahoe, host_priv degrades from a "Write-Anywhere" primitive to a "Logic Control" primitive. It allows you to invoke privileged kernel APIs, but it does not grant direct memory corruption capabilities against protected regions.

6.2.2 Task Special Ports: The Privileged Directory

While host_priv itself is restricted, it acts as the directory service for a set of Special Ports that control specific subsystems. These are accessed via host_get_special_port (MIG ID 205).

The realhost structure maintains a static array of these ports: ipc_port_t special[HOST_MAX_SPECIAL_PORT + 1].

Critical Special Ports:

  • HOST_KEXTD_PORT (ID 1): The handle used to communicate with the kernel extension manager. Historically used to force the kernel to load a malicious kext. In Tahoe, kext loading is gated by the TXM, which verifies the signature and the LocalPolicy.
  • HOST_SEATBELT_PORT (ID 7): The control port for the Sandbox (sandbox.kext). Possession of this port allows a process to query and potentially manipulate sandbox policies (though policy application is usually one-way).
  • HOST_AMFID_PORT (ID 18): The communication channel for amfid. This port is critical for the Code Signing validation loop.
  • HOST_AUTOMOUNTD_PORT (ID 11): Used to trigger filesystem mounts.

RE Focus: The host_set_special_port Trap:
A common post-exploitation technique involves overwriting one of these special ports with a port controlled by the attacker (e.g., replacing the KEXTD port). This allows the attacker to intercept kernel upcalls intended for system daemons.

  • Mitigation: The host_set_special_port routine checks the caller's entitlements. Only a process with the com.apple.private.set-special-port entitlement (usually only launchd) can overwrite these entries.
  • Bypass: Attackers look for logic bugs where a race condition allows replacing a port before the entitlement check completes, or finding a daemon that holds this entitlement and coercing it to perform the set operation (Confused Deputy).

6.2.3 RE Focus: Fuzzing Mach Message Parsing (MIG)

Since host_priv exposes a wide attack surface via the Mach IPC interface, it is a primary target for fuzzing. The kernel handles these messages using the Mach Interface Generator (MIG).

The host_priv_server Routine:
When a message is sent to host_priv, the kernel's IPC dispatcher calls host_priv_server. This is an auto-generated function that deserializes the Mach message and dispatches it to the implementation (e.g., kern_host.c).

Vulnerability Classes in MIG:

  1. Type Confusion: MIG relies on the message header to define the size and type of arguments. If the userland client sends a malformed message (e.g., claiming a descriptor is OOL memory when it is actually inline data), the kernel's unmarshaling logic might misinterpret the data, leading to heap corruption.
  2. Reference Counting Leaks: If a MIG routine returns an error (e.g., KERN_INVALID_ARGUMENT) after it has incremented the reference count on a port or VM object but before it consumes it, the object leaks. In the kernel, this can lead to a refcount overflow (though 64-bit refcounts make this hard) or a Use-After-Free if the cleanup logic is flawed.
  3. TOCTOU on OOL Memory: As discussed in Section 6.1.3, if the message includes Out-of-Line memory, the kernel maps it Copy-on-Write. If the MIG handler verifies the content of the memory and then uses it later, the userland process might be able to race the kernel and modify the physical page (via a side-channel or SPTM state confusion) between the check and the use.

The Tahoe Hardening:
In the Tahoe kernel, MIG-generated code has been hardened with PAC.

  • Function Pointers: The dispatch tables used by host_priv_server are signed.
  • Context: The ipc_kmsg structure (representing the message in flight) is heavily protected to prevent modification of the message contents after validation but before processing.

However, logic bugs in the implementation of the host calls (the C functions called by MIG) remain reachable. For example, host_processor_set_priv allows manipulating CPU sets. If the logic fails to account for a processor being in a low-power state or being managed by an Exclave, it could trigger an inconsistent state in the scheduler.

7.0 IOKit & Driver Architecture

While the Mach subsystem provides the primitives for IPC and scheduling, IOKit provides the object-oriented framework for device drivers. Historically, IOKit has been the "soft underbelly" of the XNU kernel. Written in a restricted subset of C++, it relies heavily on virtual function dispatch, complex inheritance hierarchies, and manual reference counting (OSObject::retain/release).

In the Tahoe architecture, IOKit has undergone a radical hardening process. The transition to Apple Silicon has allowed Apple to enforce strict Control Flow Integrity (CFI) on C++ objects using PAC, while the SPTM enforces the immutability of the driver code itself.

7.1 IOKit Initialization

The initialization of IOKit is the bridge between the static hardware description provided by iBoot (the Device Tree) and the dynamic, runtime object graph that constitutes the macOS driver environment.

7.1.1 The IORegistry: Populating the Device Tree into C++ Objects

When the kernel boots, the hardware topology is described by the Flattened Device Tree (FDT) located at the physical address passed in x1 to _start. IOKit's first major task is to hydrate this binary blob into a live graph of IORegistryEntry objects.

The IOPlatformExpert:
The bootstrap process is driven by the IOPlatformExpert class (specifically IOPlatformExpertDevice on Apple Silicon).

  1. Unflattening: The kernel parses the FDT. For every node in the tree (e.g., arm-io, uart0, aic), it instantiates an IORegistryEntry.
  2. The IODeviceTree Plane: These objects are attached to the IODeviceTree plane of the Registry. This plane represents the physical topology as reported by iBoot.
  3. Property Mapping: Properties from the FDT (like reg, interrupts, compatible) are converted into OSData, OSString, or OSNumber objects and attached to the registry entries.

Matching and Driver Loading:
Once the Registry is populated, IOKit begins the Matching phase (IOService::startMatching).

  1. The compatible String: IOKit iterates over the registry entries. It compares the compatible property (e.g., apple,s5l8960x-uart) against the IOKitPersonalities dictionary defined in the Info.plist of every loaded driver.
  2. The Probe/Start Lifecycle: When a match is found, the driver's C++ class is instantiated.
    • init(): Constructor.
    • probe(): The driver verifies the hardware is actually present (rarely used on SoCs where hardware is static).
    • start(): The driver initializes the hardware, maps MMIO regions, and registers interrupts.

RE Focus: The "Missing" Hardware:
On Tahoe systems, you will notice gaps in the IORegistry compared to the raw Device Tree.

  • Reserved Regions: The SPTM and TXM reserve specific hardware blocks (e.g., the Secure Storage controller or specific GPIO banks for privacy LEDs).
  • Filtering: During the unflattening process, the kernel checks the memory map. If a device node's reg property overlaps with a physical range reserved by the SPTM, the kernel skips creating an IORegistryEntry for it. This prevents the kernel from even attempting to map the MMIO registers for secure hardware, effectively making that hardware invisible to the OS.

7.1.2 Boot Kernel Collection (BKC) vs. Auxiliary Kernel Collection (AKC)

Gone are the days of loading individual .kext bundles from /System/Library/Extensions. To optimize boot speed and enforce immutability, macOS now uses Kernel Collections.

The Boot Kernel Collection (BKC):
This is a single, massive Mach-O binary contained within the kernelcache Image4 payload (Ap,BootKernelCollection).

  • Content: It contains the XNU kernel and all "Essential" drivers required to mount the root filesystem, start launchd, and initialize the display.
  • Linkage: All internal symbols are pre-linked. There are no external relocations required at boot time for these drivers.
  • Protection (KIP): This is the critical security distinction. Because the BKC is loaded by iBoot, its physical pages are known before the kernel starts. The SPTM marks these pages as XNU_TEXT (Immutable) in the Frame Table. The kernel cannot modify the BKC code or read-only data, even with a write primitive. It is effectively ROM.

The Auxiliary Kernel Collection (AKC):
Drivers not required for early boot (e.g., Bluetooth, Wi-Fi, Audio) are packaged into the AKC.

  • Loading: The AKC is loaded later in the boot process by kernelmanagerd (userland).
  • Verification: When kernelmanagerd passes the AKC to the kernel, the kernel must verify its signature. In Tahoe, this verification is offloaded to the TXM. The TXM checks the signature against the LocalPolicy.
  • Sealing: Once verified and linked, the kernel issues a GENTER call to the SPTM to "seal" the AKC memory region. The SPTM transitions the pages from XNU_DATA to XNU_TEXT. Once sealed, the AKC becomes just as immutable as the BKC.

RE Implication:
If you are reverse engineering a driver, you must determine if it lives in the BKC or AKC.

  • BKC Drivers: Their offsets are static relative to the kernel slide.
  • AKC Drivers: They are loaded at a randomized offset after the kernel boot. You must traverse the kmod_info linked list in memory to find their load addresses.

7.1.3 RE Focus: PAC-signing of C++ Vtables (OSObject) and BLRAA

The OSObject class is the root of the IOKit inheritance hierarchy. In C++, dynamic dispatch is handled via Virtual Tables (vtables)—arrays of function pointers. Historically, attackers would overwrite the vtable pointer in an object to point to a fake vtable controlled by the attacker (vtable hijacking).

In the arm64e architecture, Apple has fundamentally altered the C++ ABI for kernel code to mitigate this.

The Signed Vtable Pointer:
In a standard C++ object, the first 8 bytes are the pointer to the vtable. In XNU on arm64e, this pointer is signed.

  • Key: APDAKey (Data Key A).
  • Context: The context is derived from the address of the object itself (specifically, the storage location of the vtable pointer) combined with a salt (often the type hash).

$$ \texttt{SignedPtr} = \texttt{PAC}(\texttt{VtableAddr}, \texttt{Key=APDA}, \texttt{Context}=\&Object) $$

Address Diversity:
Because the object's address is part of the signature context, an attacker cannot simply copy a valid vtable pointer from Object A to Object B. The signature for Object A is valid only at Object A's address. Moving it invalidates the PAC. This kills "vtable reuse" attacks.

The BLRAA Dispatch:
When the kernel calls a virtual function (e.g., object->release()), the compiler emits a specialized instruction sequence:

LDR     x0, [x20]       ; Load the object pointer
LDR     x16, [x0]       ; Load the signed vtable pointer
AUTDA   x16, x0         ; Authenticate Data Key A, Context = Object Address (x0)
LDR     x10, [x16, #0x18] ; Load the target function pointer from the vtable
BLRAA   x10, x16        ; Branch with Link, Authenticating Key A, Context = Vtable Address

Note the two-stage authentication:

  1. AUTDA: Authenticates that the vtable pointer belongs to this object. If the pointer was overwritten, x16 becomes a canonical non-valid pointer.
  2. BLRAA: The function pointers inside the vtable are also signed (using APIAKey). The BLRAA instruction authenticates the function pointer (using the vtable address as context) and branches.

The "Recursive" PAC:
This creates a chain of trust:

  • The Object trusts the Vtable Pointer (via APDAKey).
  • The Vtable trusts the Function Pointers (via APIAKey).
  • The SPTM trusts the Vtable Memory (via XNU_TEXT immutability).

For a reverse engineer, this means that patching a vtable in memory is impossible (SPTM), and forging an object requires the ability to sign pointers with the APDAKey for an arbitrary address—a capability that requires a "Signing Oracle" gadget, which BTI aims to eliminate.

7.2 DriverKit (dexts)

The introduction of DriverKit represents a strategic retreat for the XNU kernel. For decades, the kernel’s attack surface was effectively the sum of the core kernel plus every third-party driver loaded into the address space. A vulnerability in a Wacom tablet driver or a USB-to-Serial adapter was functionally identical to a vulnerability in the scheduler: both yielded EL1 code execution.

DriverKit bifurcates this model by moving hardware drivers into userland, executing as System Extensions (.dext). While they look and feel like drivers to the developer (using a C++ subset similar to Embedded C++), architecturally they are unprivileged processes. In the Tahoe architecture, this isolation is not merely a sandbox; it is a hardware-enforced chasm guarded by the TXM and SPTM.

7.2.1 Moving drivers to userland: IOUserClient and Entitlement Checks

A dext does not have direct access to the kernel's task port, nor does it run with root privileges by default. It operates within a tightly constrained sandbox, communicating with the kernel via a specialized IPC bridge.

The IOUserServer Proxy:
When a dext is matched and loaded (managed by kernelmanagerd), the kernel instantiates a shadow object known as IOUserServer. This kernel-side object acts as the proxy for the userland driver.

  • The Shim: When the kernel needs to call a function in the driver (e.g., Start()), it calls a method on IOUserServer.
  • Serialization: IOUserServer serializes the arguments into a specialized Mach message format (distinct from standard MIG).
  • The Upcall: The message is sent to the dext process. The DriverKit runtime (linked into the dext) deserializes the message and invokes the implementation of the IOService subclass in userland.

The IOUserClient Interface:
Conversely, when the dext needs to talk to the kernel (e.g., to register an interrupt handler or map memory), it cannot call kernel APIs directly. It uses IOUserClient.

  • Restricted API Surface: The dext can only invoke a specific subset of kernel functionality exposed via IOUserClient traps. These traps are heavily scrutinized.
  • OSAction: Interrupts are no longer handled via ISRs (Interrupt Service Routines) in the driver. Instead, the kernel handles the physical IRQ, masks it, and dispatches an OSAction event to the dext via a Mach notification. This eliminates the entire class of vulnerabilities related to interrupt context safety and spinlock deadlocks in third-party code.

Entitlements as the Gatekeeper (TXM Enforcement):
In Tahoe, the ability of a dext to bind to specific hardware is governed by Entitlements.

  • Hardware Binding: A dext cannot simply mmap any MMIO region. It must possess specific entitlements (e.g., com.apple.developer.driverkit.transport.usb) to access specific device families.
  • TXM Verification: When kernelmanagerd launches the dext, the signature and entitlements are validated by the Trusted Execution Monitor (TXM).
    • The TXM verifies the CDHash against the Trust Cache.
    • The TXM validates that the provisioning profile allows the specific HID or USB entitlements requested.
    • If the TXM returns a failure, the kernel refuses to establish the IOUserServer link, and the driver fails to start. This prevents a compromised kernelmanagerd from launching a rogue driver with elevated hardware access.

RE Focus: The IOUserClass Metaclass:
Reverse engineering a dext requires understanding the OSMetaClass infrastructure in userland. The dext binary contains OSMetaClass information that describes the RPC interface. By parsing the __DATA,__const sections, one can reconstruct the vtables and the mapping between the kernel-side dispatch IDs and the userland C++ methods.

7.2.2 Memory Mapping Constraints and IOMMU (DART) Protection

The most dangerous capability of a driver is Direct Memory Access (DMA). A malicious or buggy driver could program a peripheral (like a GPU or Network Card) to write data to physical address 0x0 (or wherever the kernel text resides), bypassing CPU-enforced protections like KTRR.

To mitigate this, Apple Silicon employs a pervasive IOMMU architecture known as DART (Device Address Resolution Table).

The DART Architecture:
Every DMA-capable peripheral on the SoC sits behind a DART. The device does not see Physical Addresses (PA); it sees I/O Virtual Addresses (IOVA). The DART translates IOVA $\rightarrow$ PA, enforcing permissions (Read/Write) at the page level.

DriverKit Memory Model:

  1. IOMemoryDescriptor: When a dext allocates a buffer for DMA, it creates an IOMemoryDescriptor.
  2. Mapping: The dext calls IOMemoryDescriptor::CreateMapping. This triggers a call into the kernel.
  3. The Kernel's Role: The kernel allocates physical pages (XNU_DATA) and pins them.
  4. DART Programming: The kernel programs the DART associated with the specific hardware device controlled by the dext. It maps the physical pages to an IOVA range visible to that device.

The Tahoe/SPTM Enforcement:
In the Tahoe architecture, the kernel is no longer trusted to program the DARTs directly. If the kernel could write to DART registers, it could map the kernel's own text segment as writable to the GPU, then tell the GPU to overwrite it (a DMA attack).

  • SPTM Ownership: The physical pages containing the DART translation tables (or the MMIO registers controlling the DART) are typed as SPTM_PRIVATE or a specific hardware-protected type in the Frame Table.
  • The sptm_map_iommu Selector: When the kernel needs to map a buffer for a dext, it issues a GENTER call to the SPTM.
    • Validation: The SPTM verifies that the physical pages being mapped are owned by the dext (or are valid shared memory). It strictly forbids mapping any page typed XNU_TEXT, PAGE_TABLE, or SPTM_PRIVATE into a DART.
    • Execution: The SPTM performs the write to the DART hardware.

MMIO Mapping Restrictions:
Similarly, when a dext needs to control hardware registers (MMIO), it requests a mapping.

  • The kernel cannot simply map physical device memory into the dext's address space.
  • The SPTM validates the physical address against a Device Allow List (embedded in the Device Tree and signed/verified by iBoot).
  • This ensures that a USB driver can only map the USB controller's registers, and cannot map the registers for the Secure Enclave Mailbox or the KTRR controller.

RE Implication:
Exploiting a dext to gain kernel privileges is exponentially harder in Tahoe. Even if you gain code execution in the dext (Userland), you cannot issue arbitrary syscalls (sandbox), you cannot map kernel memory (VM isolation), and you cannot use the hardware device to perform DMA attacks against the kernel (SPTM-enforced DART). The attacker is contained within a hardware-enforced cage, limited to the specific capabilities of that one peripheral.

7.3 The Graphics Stack (AGX)

If the XNU kernel is the central nervous system, the Apple Graphics (AGX) stack is a secondary, alien brain grafted onto the SoC. On M-series silicon, the GPU is not merely a peripheral; it is a massive, autonomous compute cluster running its own proprietary operating system, managing its own memory translation, and executing a command stream that is almost entirely opaque to the main OS.

For the reverse engineer, AGX represents the largest and most complex attack surface in the kernel. The driver (AGX.kext) is enormous, the firmware is encrypted (until load), and the hardware interface is undocumented. In the Tahoe architecture, Apple has moved to aggressively sandbox this beast, wrapping the GPU's memory access in strict DART (Device Address Resolution Table) policies enforced by the SPTM to prevent DMA-based kernel compromises.

7.3.1 RTKit: The Proprietary RTOS running on the GPU Coprocessor (ASC)

The GPU does not execute driver commands directly. Instead, the M-series SoC includes a dedicated Apple Silicon Coprocessor (ASC)—typically a hardened ARMv8-R or Cortex-M class core—that manages the GPU hardware. This coprocessor runs RTKit, Apple’s proprietary Real-Time Operating System.

The Firmware Blob:
The kernel driver does not contain the logic to drive the GPU hardware registers directly. Instead, upon initialization (AGX::start), it loads a firmware payload from the filesystem (e.g., /usr/share/firmware/agx.bin or embedded in the kext).

  • Format: The firmware is a standard Mach-O binary, often multi-architecture.
  • Sections: It contains __TEXT and __DATA segments just like a userland program.
  • RTKit Structure: Reverse engineering the firmware reveals a microkernel architecture. It has a scheduler, an IPC mechanism, and a set of "Endpoints" (services).

The RTKit IPC Protocol:
Communication between the XNU kernel (AGX.kext) and the ASC (RTKit) occurs via a shared memory mailbox protocol.

  1. Mailbox Registers: The AP writes to a specific MMIO register to ring the doorbell of the ASC.
  2. Message Buffer: The message payload is placed in a shared memory ring buffer.
  3. Endpoints: The protocol is endpoint-based. The kernel sends messages to specific service IDs running on the ASC:
    • EP_PM: Power Management (Voltage/Clock gating).
    • EP_GR: Graphics Rendering (Command submission).
    • EP_COMP: Compute (GPGPU/Metal).

RE Focus: The RTKit State Machine:
The AGX.kext contains extensive logging strings and state tracking for RTKit. By analyzing the RTKit class in the kext, one can reconstruct the message structures.

  • Crash Logs: When the GPU hangs, RTKit writes a "Coredump" to a shared buffer. The kernel captures this. Analyzing these logs reveals the internal memory layout of the ASC and the state of the GPU pipeline at the time of the crash.
  • Filter Bypass: Historically, vulnerabilities existed where the kernel could send malformed IPC messages to the ASC, causing memory corruption inside the GPU firmware. While this doesn't directly yield Kernel R/W, compromising the ASC allows an attacker to use the GPU as a confused deputy for DMA attacks (see 7.3.3).

7.3.2 IOMobileFramebuffer (IOMFB): Secure Framebuffers and Exclave Compositing

While AGX.kext handles rendering, IOMobileFramebuffer.kext (IOMFB) handles the display controller (DCP). This driver is responsible for the "Swap Chain"—taking the rendered frames and scanning them out to the display panel.

The Unified Memory Architecture (UMA):
On Apple Silicon, the Framebuffer is just a region of system DRAM. WindowServer (userland) renders into an IOSurface. The physical pages backing this surface are passed to IOMFB, which programs the Display Coprocessor (DCP) to read from them.

The Security Criticality:
IOMFB is a high-value target because it handles complex shared memory structures (IOMFBSharedMemory) mapped into both the kernel and userland (WindowServer).

  • Fuzzing Surface: The Connect method and external methods of IOMobileFramebufferUserClient have historically been riddled with race conditions and bounds-checking errors.

Tahoe and the "Secure Overlay":
In the Tahoe architecture, IOMFB's control over the display is no longer absolute. To support the Hardware Privacy Indicators (Green/Orange dots), the display pipeline has been bifurcated.

  1. Standard Pipe: Managed by IOMFB/WindowServer. Draws the desktop/apps.
  2. Secure Pipe: Managed by an Exclave. Draws the privacy indicators.

Hardware Compositing:
The compositing of these two pipes happens in the display hardware, not in memory.

  • The Exclave owns a small, reserved framebuffer region.
  • The Display Controller overlays this region on top of the standard framebuffer during scanout.
  • Immutability: Because the Secure Pipe's framebuffer memory is owned by the Exclave (and protected by the SPTM), neither the kernel nor the GPU can write to it. This guarantees that if the camera is on, the green dot will be visible, even if the kernel is compromised.

7.3.3 DART: The IOMMU Wall and DMA Containment

The GPU is effectively a DMA engine with the capability to read and write vast swathes of system memory. Without restriction, a compromised GPU firmware (or a malicious shader exploiting a GPU hardware bug) could overwrite kernel text or page tables.

To prevent this, the AGX hardware—and indeed every DMA-capable peripheral on the Apple Silicon SoC—sits behind a strict IOMMU known as the DART (Device Address Resolution Table).

DART Architecture and Stream IDs (SIDs):
The DART translates Device Virtual Addresses (DVA) used by the peripheral into Physical Addresses (PA) in DRAM. However, the translation is not monolithic; it is segmented by the source of the traffic.

  • Stream IDs (SIDs): Every transaction on the SoC's Network-on-Chip (NoC) carries a hardware-generated Stream ID identifying the initiator (e.g., GPU Firmware, Vertex Fetcher, Display Controller).
  • Context Banks: The DART maintains multiple translation contexts (similar to distinct TTBR roots).
  • SID Matching: The DART hardware is configured to map specific SIDs to specific Context Banks. This allows isolation between different workloads on the same peripheral (e.g., isolating WindowServer rendering commands from a background compute shader).

The Tahoe Enforcement (SPTM):
In pre-Tahoe systems, the kernel (AGX.kext or IOMapper) managed the DART page tables and the SID configuration registers directly. This meant a kernel attacker could disable DART, remap SIDs to privileged contexts, or map kernel memory into the GPU's address space to bypass KTRR.

In Tahoe, DART management is privileged to the SPTM.

  1. Ownership: The physical pages containing the DART translation tables (L1/L2 TTEs) and the MMIO registers controlling SID configuration are typed as SPTM_PRIVATE (or a specific IOMMU_TABLE type) in the Frame Table.
  2. Mapping Requests: When AGX.kext needs to map a user's IOSurface for GPU access:
    • It pins the user pages.
    • It issues a GENTER call (Selector sptm_map_iommu).
    • It passes the DART ID, the Context ID, the DVA, and the PA.
  3. Validation: The SPTM verifies:
    • The PA is valid USER_DATA (not Kernel Text, not Page Tables).
    • The DART ID corresponds to the GPU.
    • SID Integrity: Crucially, the SPTM enforces the immutable binding between SIDs and Contexts. It ensures that the kernel cannot reconfigure the DART to allow an untrusted SID (e.g., the Neural Engine) to write to a Context Bank reserved for the Secure Enclave or Display Pipe.
  4. Execution: The SPTM writes the DART PTE.

RE Focus: The "GART" Attack Surface:
Despite SPTM protection, the logic requesting the mapping still resides in the kernel.

  • Aliasing: Can the kernel trick the SPTM into mapping the same physical page to two different DART contexts with different permissions?
  • Stale TLBs: Does the SPTM correctly flush the DART TLB (tlb_flush) immediately after unmapping? If not, the GPU might retain access to a page that has been freed and reallocated to the kernel, leading to a Use-After-Free via DMA.
  • Side Channels: The DART configuration registers (e.g., TCR, TTBR, and SID match registers) are trapped by the hardware to GL2. Attempting to write to the DART control base address from EL1 should trigger a synchronous exception. Reverse engineering the IOMapper class in IOKit will reveal the specific GENTER trampolines used to bridge these operations.

8.0 Userland Bootstrap: The Birth of PID 1

The initialization of the XNU kernel concludes with the mounting of the root filesystem (the Signed System Volume) and the spawning of the first userland process. In the UNIX tradition, this is init (PID 1). In macOS, this is launchd.

However, launchd is far more than a SysV init replacement. It is the Mach Port Broker, the central registry for the operating system's IPC namespace, and the gatekeeper for service lifecycle management. In the Tahoe architecture, launchd's role is further hardened; it is the first process to run under the full scrutiny of the Trusted Execution Monitor (TXM), and its ability to bootstrap the rest of the system relies on a complex dance of entitlements and port rights.

8.1 launchd: The Mach Port Broker

The transition from kernel mode to user mode is a singularity. The kernel thread executing bsd_init constructs a user context and never returns. From this point forward, the system's security relies on the correct enforcement of Mach port rights and the immutability of the bootstrap namespace.

8.1.1 Transition from kernel to userland: The first execve

The kernel routine bsd_init (in bsd/kern/bsd_init.c) is responsible for hand-crafting the first process.

The Loading Sequence:

  1. Path Resolution: The kernel looks for the init binary. Historically /sbin/launchd, in the Tahoe/Cryptex era, this path is a firmlink or symlink resolving into the OS Cryptex (e.g., /System/Cryptexes/OS/sbin/launchd).
  2. load_init_program: This kernel function mimics an execve syscall from within the kernel. It allocates a proc_t structure and a task_t structure.
  3. TXM Validation (The First Check):
    Before the kernel can map launchd into memory, it must validate the code signature.
    • XNU parses the launchd binary to extract the CDHash.
    • XNU issues a GENTER to the TXM.
    • The TXM verifies that the launchd CDHash exists in the Static Trust Cache (loaded by iBoot). Since launchd is part of the immutable OS, it must be in the static cache. If it is not, the system panics immediately (integrity violation).
  4. SPTM Mapping:
    Once validated, XNU requests the SPTM to map the physical pages containing the launchd text segment.
    • The SPTM updates the Frame Table Entries (FTE) for these pages to USER_DATA (or a specific immutable user type).
    • The SPTM writes the translation table entries (TTEs) for the new process's address space, marking the text segment as RX (Read-Execute) and the data segment as RW (Read-Write).

The host_priv Handoff:
Crucially, launchd is the only process in the system that receives the Host Privilege Port (host_priv) directly from the kernel without asking for it.

  • During load_init_program, the kernel calls ipc_port_alloc_kernel to retrieve the host_priv port.
  • It inserts a send right to this port into launchd's IPC space (ipc_space).
  • This right is placed at a fixed, known port name (often mach_task_self_ + n), or retrieved by launchd immediately upon startup via task_get_special_port.

This handle gives launchd the authority to configure the rest of the system, including setting the system clock, loading kernel extensions (via kernelmanagerd), and shutting down the system.

8.1.2 Initializing the Bootstrap Port (subset of the Name Service)

Mach does not have a global, string-based name service in the kernel. If Process A wants to talk to "com.apple.windowserver", the kernel has no idea what that string means. The mapping of String -> Mach Port is the responsibility of the Bootstrap Server. launchd is the Bootstrap Server.

The Bootstrap Port:
Every task in XNU has a special port slot called TASK_BOOTSTRAP_PORT.

  • Initialization: When launchd starts, it allocates a receive right for a new port. It calls task_set_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, new_port) to register this port as the bootstrap port for itself.
  • Inheritance: When launchd spawns a child process (e.g., syslogd), the child inherits this bootstrap port handle. This connects every process in the system back to launchd.

The Namespace Hierarchy:
The bootstrap namespace is not flat; it is hierarchical to support security domains and user separation.

  1. Startup Domain: The root domain, populated by LaunchDaemons (system-wide services running as root or specific users).
  2. User Domain: Created when a user logs in. Populated by LaunchAgents.
  3. Session Domain: Specific to a GUI login session (Aqua).

RE Focus: The bootstrap_check_in Routine:
When a daemon starts (e.g., locationd), it must retrieve the receive rights for the Mach ports it is supposed to service.

  1. The daemon calls bootstrap_check_in(bootstrap_port, "com.apple.locationd", &service_port).
  2. launchd receives this message.
  3. Security Check: launchd inspects the Audit Token (appended to the Mach message trailer by the kernel). It verifies that the PID sending the check-in request matches the PID that launchd just spawned for that label.
  4. Port Transfer: If the check passes, launchd moves the Receive Right for the service port (which it created earlier) into the daemon's IPC space.

This prevents "Port Squatting," where a malicious process tries to register "com.apple.password-server" before the real daemon starts. Only the process spawned by launchd as that service can claim the port.

8.1.3 Parsing launchd.plist and the binary protocol for XPC service lookups

launchd configuration is driven by Property Lists (.plist) located in /System/Library/LaunchDaemons, /Library/LaunchDaemons, etc. However, launchd does not simply read these files at runtime; it compiles them into an internal graph.

The MachService Dictionary:
The key element for reverse engineers is the MachServices dictionary in the plist.

<key>MachServices</key>
<dict>
    <key>com.apple.securityd</key>
    <true/>
</dict>

This entry tells launchd: "Allocate a Mach port. Associate it with the string 'com.apple.securityd'. If anyone sends a message to this port, launch this binary."

Demand Launching (The Lazy Allocation):
launchd does not start all daemons at boot. It creates the ports and listens on them.

  1. The Lookup: A client (e.g., Safari) calls xpc_connection_create_mach_service("com.apple.securityd", ...).
  2. The Resolution: Under the hood, libxpc sends a message to the bootstrap port asking for the send right associated with that string.
  3. The Trigger: launchd looks up the string. It sees the service is not running. It performs posix_spawn to start the daemon.
  4. The Handoff: While the daemon is launching, launchd holds the client's request. Once the daemon checks in (see 8.1.2), launchd hands the send right to the client.

The Binary Protocol (XPC vs. MIG):
Historically, launchd used the Mach Interface Generator (MIG) for these lookups (bootstrap.defs). In modern macOS, this has largely been superseded by the XPC protocol, which runs over raw Mach messages but uses a proprietary serialization format.

  • xpc_pipe: This is the low-level mechanism. Messages sent to launchd are often XPC dictionaries serialized into a Mach message body.
  • Magic Bytes: XPC messages start with a specific magic header (often 0x42774242 - 'BwBB' in ASCII for binary blobs).
  • Structure: The payload is a serialized tree of objects (dictionaries, arrays, strings, file descriptors, and Mach ports).

RE Focus: launchd's job_dispatch:
Reverse engineering the launchd binary (which is stripped, but symbols can often be correlated from open-source releases of liblaunch) reveals the job_dispatch or demux routine.

  • This routine parses the incoming Mach message.
  • It identifies if it is a legacy MIG request (subsystem 400) or an XPC request.
  • It routes the request to the appropriate subsystem (Job Management, Service Lookup, or XPC Domain management).

The "Domain" Concept:
In Tahoe, launchd enforces strict Domain Isolation. A process in the "App Sandbox" domain cannot look up services in the "System" domain unless those services are explicitly allowlisted in the sandbox profile (com.apple.security.exception.mach-lookup.global-name). launchd enforces this by checking the sender's entitlements (via the kernel audit token) against the requested service name before returning the port right. If the check fails, launchd returns BOOTSTRAP_UNKNOWN_SERVICE (or effectively 0), and the client sees "Connection Invalid."

8.2 The Dynamic Linker (dyld)

If launchd is the architect of the userland process hierarchy, dyld (the dynamic linker) is the mason that lays the bricks for every single process. In the macOS ecosystem, dyld is not merely a library loader; it is a privileged extension of the kernel’s execution logic, responsible for enforcing the final mile of the platform’s security policies, including Library Validation, symbol resolution, and the application of Address Space Layout Randomization (ASLR).

On Apple Silicon and the Tahoe architecture, dyld has evolved significantly. It no longer relies on legacy load commands for rebasing; it utilizes Chained Fixups to enable page-in linking, and its operations are tightly coupled with the kernel’s memory management subsystem, which is in turn subservient to the Secure Page Table Monitor (SPTM).

8.2.1 Mapping the Dyld Shared Cache (DSC)

The Dyld Shared Cache (DSC) is the defining characteristic of the macOS memory layout. It is a massive, pre-linked artifact (often exceeding 4GB) containing the combined text and data segments of nearly all system frameworks (CoreFoundation, libSystem, Foundation, UIKit, etc.).

The Shared Region:
To optimize memory usage, the kernel maintains a Shared Region. This is a reserved area of the virtual address space (typically starting at 0x180000000 on arm64) where the DSC is mapped.

  • Physical Sharing: The physical pages backing the DSC are shared across all processes.
  • Split Segments: The DSC is split into three primary mappings to satisfy W^X (Write XOR Execute) requirements:
    1. __TEXT: Read-Execute (RX). Contains immutable code.
    2. __DATA_CONST: Read-Only (RO). Contains pointers and data that are resolved at link time and never change.
    3. __DATA_DIRTY: Read-Write (RW). Contains data that must be modified per-process (e.g., Objective-C class realization).

The Tahoe/SPTM Constraint:
In the Tahoe architecture, the mapping of the DSC is a privileged operation guarded by the SPTM.

  1. Boot Load: The DSC is loaded from the OS Cryptex (/System/Cryptexes/OS/System/Library/dyld/).
  2. Immutable Mapping: When the kernel initializes the Shared Region, it issues GENTER calls to the SPTM. The SPTM updates the Frame Table Entries (FTE) for the physical pages backing the DSC __TEXT segment to USER_TEXT (or a specific shared immutable type).
  3. Enforcement: Once mapped, the SPTM forbids any entity (including the kernel) from writing to these physical pages. This renders the system libraries immutable even in the face of a kernel compromise. Hooking a system function like open in the shared cache via kernel memory writing is physically impossible; the attacker must use __DATA_DIRTY indirection or userland hooks.

8.2.2 RE Focus: Code Signature Validation (fcntl(F_ADDFILESIGS)) and the Call to TXM

dyld is the primary enforcer of Library Validation. This security hardening ensures that a process can only load libraries that are signed by Apple or signed by the same Team ID as the main executable.

The Validation Flow:
When dyld loads a Mach-O image (via dlopen or load command parsing):

  1. mmap: It maps the file into memory. At this stage, the pages are not yet executable.
  2. Signature Registration: dyld calls fcntl(fd, F_ADDFILESIGS_RETURN, ...) passing a fsignatures_t struct. This tells the kernel: "I want to execute code from this file. Here is the offset to the code signature (CMS blob)."

The Kernel $\rightarrow$ TXM Handoff:
In previous architectures, the kernel (specifically AMFI.kext) would parse the blob and verify the signature. In Tahoe, the kernel is untrusted for verification.

  1. Marshaling: The kernel identifies the physical pages containing the signature blob.
  2. The Gate: The kernel executes GENTER to invoke the Trusted Execution Monitor (TXM) at GL1.
  3. TXM Verification:
    • The TXM parses the CMS blob in the secure world.
    • It verifies the cryptographic chain of trust against the Trust Cache (for platform binaries) or the Apple Root CA (for third-party).
    • It checks for constraints (e.g., is this a restricted binary? Does it have the com.apple.private.security.no-sandbox entitlement?).
  4. The Verdict: The TXM returns a verdict to the kernel. Crucially, it also updates the SPTM's view of those physical pages.
    • If valid, the TXM "blesses" the pages associated with the code directory hash (CDHash).
  5. Page Fault Enforcement:
    • When dyld later jumps into the library, a page fault occurs.
    • The kernel attempts to map the page as Executable (RX).
    • The SPTM intercepts the PTE update. It checks if the physical page has been blessed by the TXM.
    • If yes, the Execute bit is set. If no (e.g., the kernel lied about the signature verification), the SPTM denies the mapping, and the process crashes with SIGKILL (Code Signature Invalid).

RE Tip: If you are analyzing a crash where dyld terminates a process immediately after loading a dylib, check the system logs for amfid or kernel messages related to TXM rejection. The error code returned by fcntl is often sanitized; the real reason (e.g., "Revoked Certificate") lies in the secure monitor logs, which may be redacted in production builds.

8.2.3 ASLR in Userland: Chained Fixups and the Death of dyld_info

Modern dyld (dyld 4.0+) on Apple Silicon has deprecated the legacy LC_DYLD_INFO rebasing opcodes in favor of Chained Fixups (LC_DYLD_CHAINED_FIXUPS). This architectural shift is not merely a performance optimization to reduce dirty pages; it is a fundamental change in how the binary format represents memory addresses, tightly coupling ASLR with Pointer Authentication (PAC).

The Problem with Legacy Rebasing:
In the legacy model, dyld parsed a list of opcodes (REBASE_OPCODE_DO_*) to locate pointers in the __DATA segment and add the ASLR slide. This required dyld to touch every page containing a pointer at launch time, triggering massive I/O and dirtying pages that might never be used.

The Chained Fixup Solution:
In a Chained Fixup binary, the "pointers" stored in the __DATA segment on disk are not memory addresses. They are 64-bit metadata structures that form a linked list (a chain) within each 16KB page.

The On-Disk Structure:
The LC_DYLD_CHAINED_FIXUPS load command points to a header (dyld_chained_fixups_header) containing offsets to "Starts" arrays (dyld_chained_starts_in_image).

  • Page-Level Granularity: The metadata identifies the offset of the first fixup in every page.
  • The Chain: The data at that offset is a 64-bit struct (e.g., dyld_chained_ptr_64_rebase).
    • next (Bits 51-62): The offset (in 4-byte strides) to the next fixup in the same page. A value of 0 terminates the chain for that page.
    • target (Bits 0-35): The offset in the __TEXT segment (for rebasing) or the ordinal in the import table (for binding).
    • high8 (Bits 56-63): Additional attributes (e.g., auth diversity).

Page-In Linking (Lazy Fixups):
When dyld loads a library, it does not iterate the whole file. It sets up the memory mapping.

  1. The Fault: When the process execution flow hits a pointer in __DATA (e.g., a vtable entry or a global string pointer), a page fault occurs.
  2. The Trap: The kernel (or dyld via a user-fault handler) detects that the page requires fixups.
  3. The Walk: The fixup logic looks up the "Start" offset for that specific page. It walks the linked list defined by the next bits.
  4. The Patch: For each entry in the chain:
    • It extracts the target.
    • It adds the ASLR slide (for rebase) or resolves the symbol (for bind).
    • Crucially for arm64e: It calculates the PAC signature.
  5. The Write: It overwrites the 64-bit metadata struct with the final, signed, slid pointer. The page is now "dirty" and valid.

PAC Integration (DYLD_CHAINED_PTR_ARM64E):
On Apple Silicon, the fixup format is often DYLD_CHAINED_PTR_ARM64E. This struct contains the diversity data (salt) for the pointer.

  • dyld does not just write Base + Offset.
  • It writes PAC(Base + Offset, Key, Context).
  • This binds the ASLR slide to the process-specific PAC keys (APIAKey, APDAKey) at the exact moment of paging in.

RE Implication: The "Broken" Binary:
For the reverse engineer, this mechanism means that static analysis of a raw Mach-O binary from disk is misleading.

  • If you open a raw binary in an older disassembler (or hexdump), the global pointers will look like garbage (e.g., 0x0040...). These are the chain metadata structs, not addresses.
  • CFG Destruction: Without processing these chains, the Control Flow Graph (CFG) is broken. Virtual function calls and global callbacks point to nowhere.

Tooling Requirement:
To analyze these binaries statically, you must "unchain" them—simulating the dyld fixup process to convert metadata back into generic, unslid pointers.

  • ipsw: The ipsw tool (specifically ipsw dyld fixup) can parse the chains and output a "fixed" Mach-O where the __DATA segment contains valid pointers relative to the file base.
  • dyld_shared_cache_util: Apple's own tool (if available) or open-source equivalents must be used to extract and fixup binaries from the DSC.
  • IDA Pro / Ghidra: Modern versions automatically detect LC_DYLD_CHAINED_FIXUPS and apply the fixups in the database view, but the underlying file on disk remains "chained."

8.3 Cryptexes (Cryptographic Extensions)

The introduction of the Signed System Volume (SSV) in macOS Big Sur solved the problem of persistence; by rooting the filesystem trust in a cryptographic hash verified by iBoot, Apple ensured that the OS partition was immutable. However, this immutability introduced a significant logistical problem: patching a single binary (e.g., WebKit or dyld) required a full OS update, a re-hashing of the entire volume, and a new signature from Apple’s Taming Server (TSS).

To resolve this tension between security and agility, Apple introduced Cryptexes (Cryptographic Extensions). A Cryptex is a cryptographically sealed, versioned filesystem image that is grafted onto the system hierarchy at boot or runtime. In the Tahoe architecture, the Cryptex subsystem is the primary mechanism for the "Split OS" design, decoupling the core kernel/BSD environment from the rapidly evolving userland frameworks.

8.3.1 The "Split OS" Architecture: /System/Cryptexes/OS

In modern macOS, the root filesystem (/) is essentially a skeleton. It contains configuration files and the kernel, but the vast majority of executable code—including dyld, libSystem, and the frameworks—resides within the OS Cryptex.

The Image4 Container:
A Cryptex is distributed as an Image4 (img4) container.

  • Payload (IM4P): A disk image (typically APFS or HFS+) containing the directory hierarchy.
  • Manifest (IM4M): The signature. Crucially, the OS Cryptex is often Personalized. It is signed by TSS with the device's ECID, binding the specific version of the userland libraries to the specific silicon, preventing "library replay" attacks where a user might try to load a vulnerable dyld from an older OS version on a newer kernel.

The Mounting Process (apfs_graft):
The kernel does not mount Cryptexes using standard mount syscalls. It uses a specialized mechanism within the APFS kext known as Grafting.

  1. iBoot Handoff: For the OS Cryptex, iBoot loads the image into memory (or identifies its location on NAND) and verifies the IM4M signature against the hardware root of trust.
  2. Trust Cache Extraction: The Cryptex contains a wrapper/trustcache file. This is a binary blob containing the CDHashes of every executable inside the Cryptex.
  3. TXM Ingestion: Before the kernel grafts the filesystem, it extracts this Trust Cache and passes it to the Trusted Execution Monitor (TXM) via GENTER. The TXM verifies the signature of the Trust Cache itself. If valid, these hashes are added to the Static Trust Cache in GL1 memory.
  4. Grafting: The kernel calls apfs_graft. This stitches the Cryptex filesystem into the Virtual File System (VFS) namespace.
    • The OS Cryptex is grafted at /System/Cryptexes/OS.
    • Firmlinks: To maintain POSIX compatibility, the system uses APFS Firmlinks. When a process accesses /usr/lib/libSystem.B.dylib, the VFS transparently redirects the lookup to /System/Cryptexes/OS/System/Library/Frameworks/....

RE Focus: The "Real" Path:
For reverse engineers, this abstraction can be confusing.

  • dyld: The dynamic linker itself lives in the Cryptex. The binary at /usr/lib/dyld on the SSV is often a shim or a hardlink to the Cryptex mount.
  • Debugging: When attaching a debugger to launchd or early boot processes, path resolution may return the /System/Cryptexes/... path. Scripts that rely on hardcoded paths to /System/Library must be updated to handle this redirection.

8.3.2 Rapid Security Response (RSR): Patching via Overlay Mounts

The Cryptex architecture enables Rapid Security Response (RSR), allowing Apple to ship security fixes (e.g., for a WebKit zero-day) without a full OS update.

The Patch Cryptex:
An RSR update is essentially a "Sparse Cryptex." It does not contain a full OS; it contains only the binaries that have changed.

The Overlay Mechanism:
When an RSR is applied:

  1. cryptexd: The system daemon cryptexd (running as root) manages the staging of the update.
  2. Verification: The RSR Cryptex is verified by the TXM. Since RSRs are often generic (not personalized to ECID to allow faster distribution), the TXM verifies them against a specific "RSR Root" or a sub-CA in the Apple PKI.
  3. Union Mounting: The kernel mounts the RSR Cryptex over the existing OS Cryptex mount point using a union-like filesystem strategy.
    • If a file exists in the RSR Cryptex (e.g., WebKit.framework), the VFS layer serves that file.
    • If it does not, the VFS falls back to the base OS Cryptex.

The "Antipatch" and Reversibility:
A critical security requirement for RSR is reversibility. If a security patch causes a boot loop, the system must be able to recover.

  • The Antipatch: RSRs include logic to revert the state.
  • Pre-Boot Authentication: Because the RSR modifies the executable code of the system, its presence must be attested to during boot. The state of "RSR Applied" vs. "Base OS" is managed by LocalPolicy and verified by iBoot/LLB. If the boot fails repeatedly, iBoot can discard the RSR overlay and boot the "Known Good" base OS Cryptex, effectively removing the patch.

Security Implications for the Researcher:

  • Atomicity: The RSR update is atomic. You cannot have a "half-patched" system. The TXM ensures that either the full RSR Trust Cache is loaded, or none of it is.
  • Persistence: RSRs do not modify the SSV. They persist in the Preboot volume and are re-grafted at every boot.
  • Analysis: To analyze an RSR patch, one cannot simply diff the filesystem. One must extract the RSR Cryptex (.dmg inside the update payload), mount it, and compare the binaries against the base OS Cryptex. The "delta" is the patch.

9.0 The Security Daemon Hierarchy

While the kernel and the hardware monitors (SPTM/TXM) enforce the immutable laws of the system physics (memory protections, page table integrity), the complex, mutable business logic of macOS security is delegated to a hierarchy of userland daemons. These daemons operate with high privileges, often holding special ports or entitlements that allow them to influence kernel policy. For the reverse engineer, these daemons represent the "Policy Engine" of the OS—and historically, the most fertile ground for logic bugs and sandbox escapes.

9.1 amfid (Apple Mobile File Integrity Daemon)

The Apple Mobile File Integrity Daemon (amfid) is the userland arbiter of code execution policy. While the TXM (GL1) handles the cryptographic verification of platform binaries and the enforcement of the Static Trust Cache, it lacks the context to evaluate the complex web of third-party provisioning profiles, developer certificates, and MDM constraints.

In the Tahoe architecture, amfid functions as the Policy Decision Point (PDP) for third-party code, while the kernel and TXM act as the Policy Enforcement Points (PEP).

9.1.1 The Interaction between launchd, the Kernel (MACF), and amfid

amfid does not poll for binaries; it is interrupt-driven by the kernel via the Mandatory Access Control Framework (MACF) hooks.

The Bootstrap Race:
amfid is a critical system daemon launched by launchd early in the boot process. Because amfid is responsible for verifying signatures, it presents a bootstrap paradox: Who verifies amfid?

  • The Solution: amfid is a platform binary located in the OS Cryptex. Its CDHash is present in the Static Trust Cache loaded by iBoot.
  • TXM Verification: When launchd spawns amfid, the kernel consults the TXM. The TXM finds the hash in the immutable static cache and blesses the pages immediately. No upcall is required.

The Verification Upcall (The "Slow Path"):
When a user launches a third-party application (e.g., /Applications/Calculator.app), the flow traverses the boundary between Kernel and Userland multiple times.

  1. The Hook: The kernel executes execve. The MACF hook mpo_vnode_check_signature is triggered in AppleMobileFileIntegrity.kext.
  2. TXM Miss: The kernel queries the TXM via GENTER. The TXM checks the Static Trust Cache and the Dynamic Trust Cache. For a newly launched third-party app, this lookup fails.
  3. The Upcall: The kernel constructs a Mach message containing the file's path, the CDHash, and the detached signature blob (if applicable). It sends this message to the HOST_AMFID_PORT (Special Port 18).
  4. amfid Processing: amfid receives the message. It performs the heavy lifting of parsing the CMS blob, validating the certificate chain (via IPC to trustd), and checking provisioning profiles.
  5. The Verdict: amfid returns a boolean verdict to the kernel via host_set_exception_ports or a dedicated MIG reply.
  6. TXM Update: If amfid approves the binary, the kernel issues a second GENTER call to the TXM, instructing it to add the validated CDHash to the Dynamic Trust Cache.
    • Security Note: In Tahoe, the TXM likely verifies the signature blob again independently to ensure the kernel/amfid didn't lie about the cryptographic validity, but it relies on amfid for the policy decision (e.g., "Is this developer ID allowed on this system?").

RE Focus: The MIG Interface:
The communication interface is defined in mfi_server.defs (reverse engineered). The key routine is verify_code_directory.

  • Input: audit_token_t, mach_port_t file_port, off_t offset, int64_t file_size.
  • Attack Surface: Malformed Mach messages sent to amfid's service port can trigger type confusion in the daemon. However, amfid checks the sender's audit token to ensure requests originate only from the kernel (kernel_task).

9.1.2 Validating Code Directories (CDHash), Entitlements, and Provisioning Profiles

The core logic of amfid resides in its ability to link a binary's Code Directory (CD) to a valid Provisioning Profile. This is the mechanism that enforces the "Walled Garden" on iOS and the Notarization requirements on macOS.

The Validation Logic (MISValidateSignatureAndCopyInfo):
amfid links against libmis.dylib (Mobile Installation Service), which exports the symbol MISValidateSignatureAndCopyInfo. This function is the heart of the check.

  1. CDHash Extraction: amfid reads the LC_CODE_SIGNATURE load command from the binary. It hashes the Code Directory slots to compute the CDHash.
  2. Entitlement Extraction: It parses the embedded entitlements (XML/plist) from the signature blob.
  3. Profile Correlation: If the binary is signed by a developer certificate (not Apple), amfid looks for an embedded embedded.mobileprovision file.
    • PKCS#7 Verification: It verifies the signature of the profile itself (signed by Apple).
    • Allowlist Check: It compares the entitlements requested by the binary against the Entitlements dictionary in the profile.
    • Device Check: For development profiles, it verifies the device's UDID is present in the ProvisionedDevices array.
  4. Constraint Enforcement:
    • Restricted Entitlements: Certain entitlements (e.g., com.apple.private.security.no-sandbox) are "Restricted." They can only be granted if the provisioning profile is signed by a specific Apple internal certificate chain. amfid enforces this hierarchy.

The "Union" of Trust:
On macOS, amfid also interfaces with the Ticket system (Notarization).

  • It checks if a valid Notarization Ticket is stapled to the binary or present in the system's ticket database (/var/db/SystemPolicy).
  • If a ticket is found, amfid treats the binary as "Apple-Notarized," relaxing certain constraints compared to a purely ad-hoc signed binary.

RE Focus: libmis.dylib Reversing:
This library is heavily obfuscated and stripped. However, analyzing the failure paths of MISValidateSignature... is crucial for understanding why a specific binary is being killed.

  • Return Codes: The function returns an integer status. 0 is success. Other values map to specific failures (e.g., MIS_PROFILE_EXPIRED, MIS_ENTITLEMENT_MISMATCH).
  • Logging: amfid writes detailed failure reasons to the system log (os_log), but often only if a specific boot-arg or configuration profile is installed (AmfiDeveloperMode).

9.1.3 Exception Handling: How get-task-allow and Debugging Entitlements are Processed

One of amfid's most critical roles is gating access to process debugging. The ability to attach a debugger (task_for_pid) is effectively a full compromise of the target process.

The get-task-allow Entitlement:
In a standard production environment, a process cannot be debugged unless it possesses the com.apple.security.get-task-allow entitlement.

  • Xcode Builds: When you build and run an app from Xcode, it signs the binary with a development certificate and injects this entitlement.
  • App Store Builds: The App Store distribution process strips this entitlement.

The amfid Decision Matrix:
When the kernel parses a binary, it extracts the entitlements and passes them to amfid. amfid validates that the get-task-allow entitlement is permitted by the provisioning profile.

  • Valid: amfid tells the kernel to set the CS_GET_TASK_ALLOW flag in the process's cs_flags (Code Signing Flags).
  • Invalid: amfid strips the entitlement or rejects the signature entirely.

The task_for_pid Check:
When debugserver (or lldb) calls task_for_pid(target_pid), the kernel checks the target's cs_flags.

  • If CS_GET_TASK_ALLOW is set: Access granted (subject to root/procmod checks).
  • If CS_GET_TASK_ALLOW is clear: Access denied (KERN_FAILURE).

Developer Mode (The Tahoe Shift):
In the Tahoe architecture, the mere presence of the entitlement is insufficient. The system must be in Developer Mode.

  1. State Check: amfid queries the TXM to determine if Developer Mode is active.
  2. Enforcement: If Developer Mode is disabled, amfid will reject the execution of any binary signed with a development certificate, even if the profile is valid. This prevents an attacker from side-loading a malicious "debuggable" app onto a user's device to inspect memory, unless the user has explicitly lowered the device's security posture via the TXM-mediated reboot ceremony.

Unrestricted Debugging (SIP Disable):
On macOS, disabling System Integrity Protection (SIP) historically allowed unrestricted debugging (csrutil disable).

  • Legacy: The kernel checked the CSR_ALLOW_UNRESTRICTED_DEBUGGING bit in the NVRAM boot-args.
  • Tahoe: The SIP state is managed by the LocalPolicy and enforced by the TXM. amfid retrieves this policy state. If SIP is disabled, amfid relaxes the signature validation logic, effectively telling the kernel "Everything is valid," allowing the execution of unsigned code and the attachment of debuggers to system processes (except those protected by Exclaves).

9.2 sandboxd & The Seatbelt Policy

If amfid is the bouncer checking IDs at the door, the Seatbelt subsystem (marketed as App Sandbox) is the straightjacket strapped to the guest once they are inside. Originating from the TrustedBSD project, the macOS Sandbox is a Mandatory Access Control (MAC) mechanism that restricts a process's access to resources—files, network sockets, Mach ports, and IOKit drivers—regardless of the user's UID.

In the Tahoe architecture, the Sandbox has evolved from a path-based filter into a semantic, metadata-driven enforcement engine that is tightly coupled with the kernel's VFS layer and the new Data Vault primitives.

9.2.1 Compiling SBPL (Sandbox Policy Language) to Byte Code

The definition of a sandbox profile is written in SBPL (Sandbox Policy Language), a Scheme-like (LISP) dialect. However, the kernel does not contain a LISP interpreter. The translation from human-readable policy to kernel-enforceable logic is the responsibility of sandboxd and the libsandbox.dylib runtime.

The Compilation Pipeline:

  1. Profile Selection:

    • App Store Apps: The system applies the generic container profile.
    • System Daemons: Daemons specify their profile name in launchd.plist (e.g., com.apple.syslogd.sb).
    • Platform Profile: In modern macOS, individual SBPL files for system services are increasingly deprecated in favor of a monolithic Platform Profile embedded directly into the Boot Kernel Collection. This reduces the attack surface by removing the need to parse text files at runtime.
  2. The Compiler (sandboxd):
    When a process initializes the sandbox (via sandbox_init_with_parameters), an XPC call is made to sandboxd.

    • sandboxd parses the SBPL.
    • It resolves variable expansions (e.g., ${HOME}, ${TemporaryDirectory}).
    • It compiles the rules into a proprietary Byte Code format (often referred to as "TinyScheme" or "Filter Machine" code).
  3. The Byte Code Structure:
    The compiled blob is a serialized state machine. Reverse engineering this format reveals a graph of operations:

    • Opcodes: OP_MATCH_PATH, OP_MATCH_PATTERN, OP_CHECK_ENTITLEMENT, OP_ALLOW, OP_DENY.
    • Filters: The logic is optimized into a decision tree. For example, file operations are often grouped by path prefix to minimize evaluation time.

RE Focus: Reversing the Binary Blob:
The kernel receives this blob via the __mac_syscall interface (specifically the sandbox_set_profile command).

  • Extraction: You can intercept these blobs by hooking sandbox_check_common in the kernel or sandbox_compile_entitlements in userland.
  • Decompilation: Tools like sbs (Sandbox Scrutinizer) or custom IDA scripts can lift the bytecode back into a pseudo-SBPL format.
  • The "Profile Validation" Check: In Tahoe, the kernel performs a sanity check on the bytecode. It verifies that the profile does not contain "impossible" instructions or infinite loops that could hang the kernel thread (a DoS vector).

9.2.2 The Sandbox Kernel Extension: Hooking Syscalls via the MAC Framework

The enforcement engine is Sandbox.kext. It hooks into the XNU kernel using the Mandatory Access Control Framework (MACF).

The MACF Hooks:
XNU is instrumented with hundreds of mac_ hooks placed at critical security bottlenecks.

  • Filesystem: mpo_vnode_check_open, mpo_vnode_check_rename, mpo_vnode_check_unlink.
  • IPC: mpo_mach_port_check_send, mpo_mach_port_check_receive.
  • IOKit: mpo_iokit_check_open_service.

The Evaluation Flow:

  1. Trigger: A sandboxed process calls open("/etc/passwd", O_RDONLY).
  2. Hook: The kernel executes mac_vnode_check_open.
  3. Dispatch: The MAC framework iterates through registered policies. Sandbox.kext claims the hook.
  4. Credential Lookup: The kext retrieves the kauth_cred_t of the calling process. Attached to this credential is the Sandbox Label, which contains the pointer to the compiled bytecode profile.
  5. Evaluation: The Sandbox engine executes the bytecode against the request arguments.
    • Input: Operation (file-read), Path (/etc/passwd).
    • Logic: The engine traverses the decision tree.
  6. Caching (The Performance Critical Path):
    Evaluating bytecode on every syscall is prohibitively expensive. Sandbox.kext maintains a Check Cache.
    • If the decision is made, the result is cached in the vnode's label or a per-process cache.
    • Subsequent accesses to the same file bypass the bytecode engine and use the cached verdict.
    • RE Vulnerability: Cache invalidation bugs are critical. If a file is moved or permissions change, but the Sandbox cache remains stale, enforcement can be bypassed.

The Tahoe/SPTM Intersection:
While the Sandbox policy is software-defined, the integrity of the hooks is hardware-enforced.

  • Immutable Hooks: The function pointers in the mac_policy_conf structure (which point to Sandbox.kext functions) reside in XNU_DATA_CONST.
  • SPTM Enforcement: If a rootkit attempts to unhook the Sandbox by overwriting these pointers, the SPTM will panic the system, as XNU_DATA_CONST is read-only to EL1. This prevents the classic "unhooking" attacks used by older jailbreaks.

9.2.3 Containerization: Data Vaults and Group Containers

In the Tahoe era, Apple has moved beyond simple path-based rules (which are fragile and prone to symlink attacks) toward Semantic Containerization.

Data Vaults:
Data Vaults are the "Nuclear Option" for privacy. They protect sensitive user data (e.g., Messages, Photos, Health) from everyone, including the root user.

  • Implementation: A Data Vault is a directory flagged with a specific VFS attribute (ACL or extended attribute).
  • Enforcement: The kernel checks for this attribute during lookup. Access is denied unless the calling process possesses a specific, restricted entitlement (e.g., com.apple.private.security.storage.AppDataVault).
  • The "Root" Fallacy: Even if you run as root (UID 0) with host_priv, you cannot ls or cat a Data Vault. The Sandbox check happens before the DAC (Discretionary Access Control) check.
  • RE Focus: Look for rootless_check_datavault_flag in the kernel. This function verifies the entitlement against the vnode's flags.

Group Containers:
To allow IPC and data sharing between apps and their extensions (e.g., a Widget and its parent App), the Sandbox enforces Group Containers.

  • The application-group Entitlement: Defines the shared identifier (e.g., group.com.example.app).
  • Container Manager: The system daemon containermanagerd manages the lifecycle of these directories (~/Library/Group Containers/).
  • Sandbox Logic: The profile compiler automatically injects rules allowing read/write access to ${HOME}/Library/Group Containers/<group-id>. This injection is based on the entitlements present in the binary's code signature, binding the filesystem access directly to the cryptographic identity of the executable.

If amfid validates the code's identity and sandboxd restricts the code's reach, tccd is the daemon responsible for managing the most fragile component of the security model: the user.

The Transparency, Consent, and Control (TCC) subsystem is the "User Intent Oracle." It governs access to privacy-sensitive sensors (Camera, Microphone), personal data (Contacts, Calendars, Photos), and privileged system capabilities (Full Disk Access, Screen Recording). In the Tahoe architecture, tccd has evolved from a simple prompt generator into a complex attribution engine that must defend against "Consent Hojacking" and "Attribution Spoofing."

9.3.1 The TCC Database: Schema, Integrity, and SIP

The state of user consent is persisted in SQLite databases. There is a bifurcation of state:

  • System TCC: /Library/Application Support/com.apple.TCC/TCC.db (Root-owned, system-wide permissions like Full Disk Access).
  • User TCC: ~/Library/Application Support/com.apple.TCC/TCC.db (User-owned, per-session permissions like Camera/Microphone).

The Schema:
The core of the database is the access table. For the reverse engineer, the critical columns are:

  • service: The string identifier of the privilege (e.g., kTCCServiceSystemPolicyAllFiles, kTCCServiceMicrophone).
  • client: The bundle identifier or path of the requesting binary.
  • client_type: 0 (Bundle ID) or 1 (Absolute Path).
  • auth_value: 0 (Denied), 1 (Unknown), 2 (Allowed), 3 (Limited).
  • csreq: The Code Signing Requirement.

The csreq Blob (The Cryptographic Anchor):
TCC does not trust paths. If it did, an attacker could simply overwrite /Applications/TrustedApp.app with malware and inherit its camera permissions.
Instead, TCC stores a compiled Code Signing Requirement (CSReq) blob in the database.

  • When a process requests access, tccd obtains the process's code signature.
  • It validates the running code against the csreq stored in the database row.
  • Logic: (Current_Code_Signature) satisfies (Stored_CSReq).

Protection Mechanisms:
While the database is a standard SQLite file, access to the file handle is heavily guarded.

  • SIP (System Integrity Protection): The System TCC database is protected by SIP. Even root cannot write to it. Only tccd (which has the com.apple.private.tcc.manager entitlement) and specific Apple-signed updaters can modify it.
  • Data Vaults: In newer macOS versions, the directory containing the database is often flagged as a Data Vault, requiring kernel-enforced entitlements just to traverse the directory structure.

RE Focus: The tccutil Fallacy:
The command-line tool tccutil is often used to reset permissions. However, it communicates with tccd via XPC. It does not touch the DB directly. Reverse engineering tccd reveals that it holds an exclusive lock on the DB handle, and any attempt to modify the DB file directly (e.g., via a bypass) usually results in database corruption or the daemon crashing and reloading from a backup, due to WAL (Write-Ahead Log) integrity checks.

9.3.2 The Attribution Chain: Determining Who is Asking

The hardest problem tccd solves is Attribution. When a user clicks a button in an app, and that app asks a daemon to take a photo, who is the "Client"? The App? Or the Daemon?

The XPC Audit Token:
When a message arrives at tccd (via com.apple.tccd.system or com.apple.tccd), the primary identifier is the Audit Token attached to the Mach message trailer.

  • tccd parses the token to retrieve the PID, UID, and GID.
  • It uses csops (or the kernel-backed CS_VALID flag) to ensure the PID maps to a valid, signed binary.

The "Responsible Process" Problem:
Consider Terminal.app running a shell script that calls curl. If curl asks for network access, the user should see "Terminal.app would like to access...", not "curl".
To handle this, TCC supports Attribution Chains.

  1. responsible_pid: A process can designate another process as responsible for its actions (using launchd attributes or specific XPC flags).
  2. Validation: tccd verifies the relationship. It checks if the requesting process is a child of the responsible process, or if the responsible process possesses the com.apple.private.tcc.allow entitlement which permits it to proxy requests.

The Access Object:
Internally, tccd creates a TCCAccessRequest object.

  • Subject: The process requesting access.
  • Accessor: The process that will actually touch the resource (often the same as Subject).
  • Attributor: The process to be displayed in the UI.

RE Focus: The tccd State Machine:
By analyzing the service_listener function in tccd, one can map the decision logic:

  1. Entitlement Check: Does the client have com.apple.private.tcc.allow for this service? If yes, Grant immediately (no prompt).
  2. Database Lookup: Does a row exist in TCC.db?
    • If yes, validate csreq. If valid, return auth_value.
  3. User Prompt: If no record exists, construct a UserNotification.
    • Secure UI: The prompt is not drawn by tccd. It is handed off to UserNotificationCenter (and CoreServicesUIAgent). This prevents the requesting app from drawing over the prompt (Clickjacking) because the prompt window resides in a higher window level (Shield Window) managed by WindowServer.

9.3.3 RE Focus: XPC Attacks against TCC Endpoint Validation

For the vulnerability researcher, tccd is a prime target. If you can trick tccd into believing you are Finder.app, you gain Full Disk Access.

Attack Vector 1: XPC Injection:
Many apps load bundles or plugins. If an app with TCC permissions (e.g., a Video Editor with Camera access) loads a malicious dylib (via dylib hijacking or unsafe dlopen), that dylib runs inside the privileged process.

  • TCC's View: tccd sees the PID of the Video Editor. It grants access.
  • Mitigation: Hardened Runtime and Library Validation (enforced by dyld and TXM) prevent loading unsigned dylibs. However, if the host app has the com.apple.security.cs.disable-library-validation entitlement, this vector is wide open.

Attack Vector 2: The "Fake" Attributor:
Attackers attempt to spoof the XPC dictionary sent to tccd.

  • The XPC message often contains a TCCAccessRequest dictionary.
  • Historically, researchers found they could inject a fake target_token into this dictionary.
  • Current Hardening: tccd now ignores user-supplied tokens in the XPC payload for most checks. It relies almost exclusively on the kernel-supplied Audit Token from the IPC trailer, which cannot be spoofed from userland.

Attack Vector 3: Semantic Confusion (The "Open" Trap):
An attacker uses the open command or AppleEvents to coerce a privileged app to perform an action.

  • Example: Telling Terminal to execute a script.
  • Mitigation: macOS introduced "Automation" permissions (kTCCServiceAppleEvents). App A cannot send AppleEvents to App B unless explicitly authorized by the user. tccd enforces this by intercepting the AppleEvent manager's IPC.

The "Tahoe" Impact:
On Apple Silicon, the TXM plays a subtle but vital role here. tccd relies on the Code Signing flags (CS_VALID, CS_HARD, CS_KILL) to trust the client. In Tahoe, these flags are ultimately enforced by the TXM.

  • If an attacker manages to patch the kernel to set CS_VALID on a malicious binary, the SPTM will still refuse to map the pages as executable if the TXM hasn't blessed the CDHash.
  • Therefore, tccd's reliance on code signing is anchored in the hardware root of trust. A kernel compromise does not trivially allow bypassing TCC, because the attacker cannot easily forge the immutable identity of a system binary that tccd trusts.

10.0 User Session, Authentication & Data Protection

The transition from the system bootstrap phase to the interactive user session represents a critical boundary crossing. Up to this point, the system has operated primarily in the Startup domain, managed by the root launchd context. The instantiation of a user session requires the creation of a new security context—the Audit Session—and the decryption of user-specific cryptographic material anchored in the Secure Enclave.

In the Tahoe architecture, this process is not merely a check of a password hash against a file. It is a hardware-mediated ceremony involving the unwrapping of the Secure Token, the derivation of the User Keybag, and the establishment of a Kerberized identity that binds the user's biological presence (Biometrics) to their digital authority.

10.1 loginwindow & opendirectoryd

The graphical login experience is orchestrated by two primary userland daemons: loginwindow, which manages the session lifecycle and UI, and opendirectoryd, which serves as the abstraction layer for authentication and identity services. While these components are legacy codebases dating back to NeXTSTEP, their internals have been aggressively refactored to support the hardware-backed security model of Apple Silicon.

10.1.1 The Audit Session ID (ASID) and Kernel Tracking

In XNU, the concept of a "User" is tracked via the Audit Session ID (ASID). This is distinct from the UNIX UID/GID. The ASID identifies a specific login instance (e.g., the graphical console session vs. an SSH session).

The setaudit_addr Syscall:
When loginwindow successfully authenticates a user, it does not simply setuid. It invokes the setaudit_addr syscall (wrapped by libbsm).

  • Kernel Structure: This syscall populates the audit_token_t structure associated with the process.
  • Immutability: Once an ASID is set for a process, it is inherited by all children. Crucially, XNU enforces that a process cannot change its ASID once set (with very specific exceptions for sshd and loginwindow holding the com.apple.private.audit.session.create entitlement).

The Security Implications of ASID:
The ASID is the primary signal used by tccd and WindowServer to determine "Console Ownership."

  • TCC: When a process requests Camera access, tccd checks if the requesting process's ASID matches the ASID of the active graphical session. If it matches, the user is prompted. If it is a background session (e.g., SSH), the request is auto-denied or routed differently.
  • WindowServer: Only processes belonging to the active Console ASID are permitted to connect to the WindowServer and draw on the screen.

RE Focus: The security_authtrampoline:
Historically, the transition from the loginwindow context (root) to the user context involved a setuid binary called security_authtrampoline. In modern macOS, this logic is largely internalized within loginwindow's interaction with launchd.

  • loginwindow calls xpc_session_create.
  • This triggers launchd to spawn a new instance of itself (the User launchd) running as the target user.
  • This User launchd becomes the root of the user's process tree.

10.1.2 loginwindow: The Shield Window and Session State

loginwindow (located at /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow) is the session leader. It is responsible for drawing the login GUI, validating credentials, and managing the "Shield Window."

The Shield Window (Anti-Overlay):
To prevent "Clickjacking" or "Fake Login Screen" attacks where a malicious application draws a window over the login prompt to capture keystrokes, loginwindow utilizes a privileged connection to SkyLight (the WindowServer framework).

  • Window Level: The login UI is drawn at the kCGShieldingWindowLevel (or kCGCursorWindowLevel - 1). This is a Z-order reserved for the system.
  • Event Isolation: When the Shield Window is active, the WindowServer routes all keyboard and mouse events exclusively to loginwindow, regardless of what other applications might be running in the background.

The State Machine:
Reverse engineering loginwindow reveals a complex state machine driven by AppleEvents and Notificiations.

  • LoginHook / LogoutHook: While officially deprecated, the code paths for these legacy customization scripts still exist, though they are heavily sandboxed.
  • TAL (Transparent App Lifecycle): loginwindow manages the "Resume" feature (re-opening windows after reboot). This state is persisted in ~/Library/Preferences/ByHost/com.apple.loginwindow.*.plist, protected by the user's Data Protection keys.

10.1.3 opendirectoryd: The Authentication Broker

opendirectoryd is the daemon responsible for answering the question: "Is this password correct?" It is a modular daemon that loads plugins (bundles) to handle different directory services (LDAP, Active Directory, Local).

The Local Node (/Local/Default):
On a standalone Mac, authentication is handled by the Local Node. The data store for this node is located at /var/db/dslocal/nodes/Default/.

  • Users: Stored as individual Property List (.plist) files in users/.
  • Shadow Hash: The user's password hash is not stored in the plist. It is stored in a separate, restricted file structure known as the Shadow Hash, often embedded as binary data within the plist but stripped of read permissions for non-root users.

The Shadow Hash Data:
The ShadowHashData key in the user's plist contains a binary blob. In the Tahoe era, this blob is not a simple SHA-512 hash. It is a complex structure containing:

  1. SALTED-SHA512-PBKDF2: A standard hash for legacy compatibility (often disabled or randomized on FileVault-enabled systems).
  2. Secure Token Wrapper: The primary authentication secret. This is a blob encrypted by a key derived from the Secure Enclave.

The Verification Flow (ODRecordVerifyPassword):
When loginwindow passes a password to opendirectoryd:

  1. od_verify_password: The request is routed to the Local Node module.
  2. AppleSEPKeyStore Interaction: opendirectoryd cannot verify the Secure Token wrapper itself. It passes the plaintext password and the wrapped blob to the kernel extension AppleSEPKeyStore.kext.
  3. SEP Round Trip: The kernel marshals this request to the Secure Enclave via the Mailbox.
  4. Unwrapping: The SEP mixes the hardware UID with the user's password (tangling). It attempts to unwrap the blob.
  5. Verdict: If the unwrap succeeds, the SEP returns a success signal (and potentially a derived key for FileVault). If it fails, it returns an authentication error.

RE Implication:
This architecture means that offline cracking of macOS password hashes (extracted from dslocal) is effectively impossible for FileVault-enabled users on Apple Silicon. The hash is not mathematically complete without the hardware UID of the specific SEP that created it.

10.1.4 Kerberos and the Local KDC (Heimdal)

A little-known fact about modern macOS is that every machine runs a Local Key Distribution Center (LKDC). This is a stripped-down Heimdal Kerberos KDC running within opendirectoryd.

Why Kerberos on a Laptop?
Apple uses Kerberos to handle "Single Sign-On" between system services without passing the plaintext password around.

  1. Initial Login: When the user logs in, opendirectoryd obtains a Ticket Granting Ticket (TGT) from the Local KDC using the user's password.
  2. Credential Cache: This TGT is stored in the kernel-managed credential cache (accessible via the CCACHE server).
  3. Service Auth: When the user tries to unlock a System Preference pane, use Screen Sharing, or access the Keychain, the system requests a Service Ticket from the Local KDC.
  4. Validation: The service verifies the ticket. If valid, access is granted.

The "Smart Card" Enforcement:
In high-security environments (and increasingly the default in Tahoe), the Local KDC is backed by PKINIT.

  • If the user logs in with a Smart Card (or the built-in PIV token backed by the Secure Enclave), the KDC validates the x.509 certificate chain instead of a password hash.
  • The TXM plays a role here by enforcing that the Smart Card driver (running as a dext) is valid and has not been tampered with, ensuring the certificate presented to the KDC is authentic.

RE Focus: Heimdal Framework:
The Heimdal.framework in the PrivateFrameworks directory contains the logic for this. Hooking krb5_get_init_creds_password in loginwindow is a common technique for Red Teams to intercept plaintext credentials before they are hashed, though this requires bypassing SIP and the hardened runtime.

10.2 Biometric Unlock (Touch ID / Face ID / Optic ID)

Biometric authentication on Apple platforms is frequently misunderstood as a replacement for the passcode. Architecturally, it is a convenience mechanism that authorizes the Secure Enclave to perform a cryptographic operation (unwrapping a key) that would otherwise require the user's manual secret entry.

In the Tahoe architecture, the biometric stack is a high-stakes interplay between userland daemons, kernel drivers, the Secure Enclave, and—in the case of Face ID and Optic ID—the Secure Neural Engine (SNE).

10.2.1 The Daemon Hierarchy: coreauthd $\rightarrow$ biometrickitd $\rightarrow$ SEP

The userland implementation is split into two primary daemons to enforce separation of concerns: coreauthd (Policy) and biometrickitd (Mechanism).

coreauthd (The Policy Engine):
Located at /System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd, this daemon manages the Local Authentication (LA) context.

  • Context Management: When an app calls LAContext.evaluatePolicy, coreauthd creates a unique context ID (ACMContext).
  • ACL Evaluation: It parses the Access Control Lists (ACLs) attached to Keychain items. It determines what is required (e.g., "Biometry OR Passcode").
  • The ACM (Access Control Module): Inside coreauthd, the ACM is a library that mirrors the logic of the Secure Enclave. It prepares the request structures that will eventually be sent to the SEP.

biometrickitd (The Hardware Manager):
Located at /usr/libexec/biometrickitd, this daemon is responsible for the physical sensors.

  • Sensor Abstraction: It loads the specific plugin for the device's hardware (Mesa for Touch ID, Pearl for Face ID, Jade for Optic ID).
  • Hardware State: It manages the power state of the sensor and the emitter (e.g., the Flood Illuminator or IR Projector).
  • Data Relay: Crucially, biometrickitd does not process biometric data. It acts as a dumb pipe. It sets up a Direct Memory Access (DMA) channel or a shared memory buffer between the sensor hardware and the Secure Enclave.

The Handshake:

  1. coreauthd receives a request and validates the PID/ASID.
  2. coreauthd sends an XPC request to biometrickitd to "Arm" the sensor.
  3. biometrickitd issues an IOConnectCall to the kernel driver (AppleBiometricSensor.kext).
  4. The kernel driver wakes the SEP via the Mailbox and prepares the hardware interface.

10.2.2 The Hardware Path: Sensor-to-SEP Pairing

A critical security property of the biometric stack is the Hardware Pairing between the sensor and the SEP. This prevents "Evil Maid" attacks where an attacker replaces the fingerprint sensor with a malicious FPGA that replays a stored "match" signal.

The Factory Pairing:
During manufacturing, the sensor (e.g., the Touch ID button or the TrueDepth camera module) and the SEP exchange public keys. A shared secret is established and burned into the sensor's controller.

The Encrypted Channel:
When the sensor captures an image (fingerprint ridge map or IR depth map):

  1. Encryption: The sensor hardware encrypts the raw image data using the shared pairing key before it leaves the sensor module.
  2. Transport: The encrypted data travels over the SPI or MIPI bus to the Application Processor.
  3. Passthrough: The AP (kernel) writes this encrypted blob into a physical memory buffer shared with the SEP. The AP cannot decrypt this data; it sees only high-entropy noise.
  4. Decryption: The SEP reads the buffer, decrypts it using the pairing key, and processes the raw biometric data.

RE Implication:
If you hook the AppleBiometricSensor kext and dump the data buffers, you will not get a fingerprint image. You will get ciphertext. Furthermore, if you replace the sensor with a component from another iPhone (without running the Apple System Configuration tool to re-pair), the SEP will fail to decrypt the data, and biometrics will be disabled.

10.2.3 The Secure Neural Engine (SNE) & Optic ID

With the advent of Face ID and Optic ID (Vision Pro), the computational cost of biometric matching exceeded the capabilities of the general-purpose SEP core. Apple introduced the Secure Neural Engine (SNE).

Architecture:
The Neural Engine (ANE) on the SoC is partitioned.

  • Standard Mode: Accessible to userland (CoreML) for standard ML tasks.
  • Secure Mode: A dedicated slice of the ANE is reserved for the SEP.

The Optic ID Flow (Tahoe/Vision Pro):

  1. Capture: The iris cameras capture spatiotemporally modulated IR images.
  2. Transfer: The encrypted images are DMA'd to the SEP's protected memory.
  3. SNE Handoff: The SEP sanitizes the ANE (flushing previous weights/data) and loads the Optic ID neural network model.
  4. Processing: The SEP feeds the image data into the SNE. The SNE performs the feature extraction (generating the mathematical representation of the iris).
  5. Matching: The SNE returns the feature vector to the SEP. The SEP compares this vector against the enrolled templates stored in the Secure Storage Component (xART).
  6. Liveness: Simultaneously, the SEP analyzes the "spatiotemporal modulation" (the reaction of the pupil to light pulses) to ensure the subject is a living eye and not a contact lens or high-res display.

Memory Protection:
On M3/M4 chips, the memory used by the SNE during this process is protected by a dedicated Hardware Security Controller. If the AP attempts to read SNE memory while it is in Secure Mode, the transaction is blocked, and a system panic is triggered.

10.2.4 Secure Intent: The GPIO Hardline

For high-value transactions (Apple Pay, Smart Card pairing), biometric matching is insufficient. Malware could theoretically trick the user into looking at the phone (Face ID match) to authorize a background payment.

To solve this, Apple implements Secure Intent via a physical side-channel.

The Physical Button:
The power button (or side button) is wired to the Always-On Processor (AOP) and the AP, but it also has a dedicated GPIO line connected directly to the Secure Enclave.

The Logic:
When a transaction requires Secure Intent (flagged in the LAContext):

  1. The SEP performs the biometric match.
  2. If successful, the SEP holds the authorization. It does not release the token.
  3. The SEP monitors its private GPIO pin.
  4. The Window: The user must double-click the physical button within a specific time window (usually < 2 seconds) of the biometric match.
  5. Release: Only when the SEP detects the electrical signal of the double-click on its private line does it sign the payment token or release the key.

RE Focus:
This mechanism renders UI spoofing attacks ineffective for Apple Pay. Even if an attacker controls WindowServer and draws a fake "Double Click to Pay" overlay, and even if the user looks at the phone (satisfying Face ID), the attacker cannot generate the electrical signal on the SEP's GPIO pin.

10.2.5 The LAC (Local Authentication Context) and Token Binding

When authentication succeeds, the SEP does not simply return true. It returns a cryptographic token.

The ACMHandle:
coreauthd receives an opaque handle from the SEP. This handle refers to a session in the SEP's memory.

Token Binding:
When accessing a Keychain item protected by kSecAccessControlUserPresence:

  1. securityd (Keychain daemon) sends the encrypted keyblob (wrapped key) and the ACMHandle to the SEP.
  2. The SEP verifies the ACMHandle is valid and associated with a recent biometric match.
  3. The SEP uses its internal hardware key to unwrap the keyblob.
  4. The SEP returns the raw AES key to securityd (or performs the crypto operation internally if the key is non-extractable).

The "Backoff" Strategy:
The SEP enforces the retry policy in hardware.

  • Counter: Stored in xART.
  • Logic:
    • 1-2 failures: Retry allowed immediately.
    • 3+ failures: Force delay.
    • 5 failures: Biometrics disabled. Passcode required.
    • Global Reset: If the device is rebooted, or if 48 hours pass, the SEP invalidates the biometric keys in memory, forcing a passcode entry to re-derive the master keys. This logic is encoded in the sepOS and cannot be patched by the kernel.

10.3 Data Protection & FileVault

On Intel Macs, FileVault was a distinct full-disk encryption layer (CoreStorage) that operated below the filesystem. On Apple Silicon, this distinction has vanished. FileVault is simply Data Protection applied to the Volume Key.

The architecture of macOS Tahoe unifies the security model of iOS and macOS. Every file on the SSD is individually encrypted with a per-file key, wrapped by a class key, protected by the Secure Enclave. "Turning on FileVault" merely changes the protection policy of the Volume Encryption Key (VEK) from "Protected by Hardware UID" to "Protected by User Password."

This section dissects the cryptographic hierarchy, the hardware-accelerated AES path that keeps keys out of RAM, and the Sealed Key Protection (SKP) mechanism that binds data availability to the system's boot measurement.

10.3.1 Unwrapping the User Keybag: The Class Key Hierarchy

The central data structure for Data Protection is the Keybag. It is a binary property list stored on disk (typically in /private/var/db/UserKeyStash or similar locations depending on the boot volume layout), containing the wrapped class keys.

The Hierarchy:

  1. Hardware UID: The root of trust, fused into the SEP. Accessible only to the SEP's AES engine.
  2. User Password: The user's login secret.
  3. Passcode Derived Key (PDK): The SEP mixes the UID and the User Password using a proprietary KDF (Tangling).
    $$ \text{PDK} = \text{Tangle}(\text{UID}, \text{PBKDF2}(\text{Password}, \text{Salt})) $$
  4. Class Keys: The Keybag contains the keys for specific protection classes (A, B, C, D), wrapped by the PDK.

The Classes:

  • Class A (Complete Protection): Keys are evicted from SEP memory immediately upon device lock.
  • Class B (Protected Unless Open): Keys are evicted on lock, but open file handles retain an ephemeral unwrapped key in the AES engine context to allow background writes to finish.
  • Class C (First Unlock): Keys remain in SEP memory after the first successful authentication until reboot. This is the class used for the FileVault Volume Key.
  • Class D (No Protection): Keys are wrapped only by the UID. They are accessible as long as the device is booted, even without a password. Used for system daemons that must start before login.

The Unwrapping Ceremony:
When the user logs in:

  1. loginwindow passes the password to the kernel (AppleSEPKeyStore).
  2. The kernel passes the password and the Keybag blob to the SEP via Mailbox.
  3. The SEP performs the Tangle operation to derive the PDK.
  4. The SEP attempts to unwrap the Class Keys in the Keybag using the PDK.
  5. Crucially: The unwrapped Class Keys are never returned to the kernel. They remain resident in the SEP's protected SRAM. The SEP returns a "Handle" (Key ID) to the kernel.

10.3.2 Sealed Key Protection (SKP): Binding Data to Measurement

In the Tahoe architecture, Apple introduced Sealed Key Protection (SKP) to mitigate "Evil Maid" attacks where an attacker boots a malicious operating system (or a vulnerable older version) to brute-force the keybag.

SKP binds the availability of the keys to the Measurement of the boot chain.

The Measurement Flow:

  1. Boot Monitor: Measures the sepOS.
  2. sepOS: Measures the KernelCache and the LocalPolicy.
  3. PCR Extension: These measurements are extended into registers within the SEP (conceptually similar to TPM PCRs).

The SKP Key:
When the Volume Encryption Key (VEK) is generated (at install time), it is wrapped not just by the UID/PDK, but by a key derived from the Current System Measurement.

$$ \text{WrappedVEK} = \text{Encrypt}(\text{VEK}, \text{KDF}(\text{UID}, \text{PDK}, \text{Measurement})) $$

The Security Guarantee:
If an attacker attempts to boot a custom kernel (Permissive Security) or a downgraded kernel to attack the Data Protection implementation:

  1. The Boot Monitor/sepOS calculates a different measurement hash.
  2. The KDF derives a different wrapping key.
  3. The SEP fails to unwrap the VEK.
  4. The Data Volume remains inaccessible.

This forces the attacker to possess the user's credentials and boot a signed, measured, and authorized operating system to access the data.

10.3.3 The Hardware AES Engine & The "Wrapped Key" Path

A common misconception is that the kernel decrypts file data. In reality, the XNU kernel never sees the raw AES keys for file contents. Apple Silicon utilizes a dedicated AES DMA Engine that sits between the Memory Controller and the Storage Controller (ANS2).

The Inline Encryption Path:

  1. File I/O: When a user process reads a file, the kernel looks up the file's metadata to find the Per-File Key (wrapped by a Class Key).
  2. The Request: The kernel sends the Wrapped Per-File Key and the Class Key Handle to the SEP.
  3. SEP Translation: The SEP unwraps the Per-File Key using the resident Class Key.
  4. The Side Channel: The SEP does not return the raw key to the kernel. Instead, it programs the AES Engine's key registers directly via a dedicated hardware bus (or re-wraps the key with an ephemeral key known only to the AES Engine).
  5. DMA: The kernel initiates the DMA transfer from NAND.
  6. Decryption: The AES Engine decrypts the data on-the-fly as it flows into DRAM. The data lands in the page cache as plaintext, but the key that decrypted it effectively vanishes.

RE Implication:
Memory forensics on a running macOS kernel cannot recover the file encryption keys. They simply do not exist in kernel memory. To recover the keys, one must compromise the SEP firmware and dump its SRAM.

10.3.4 RE Focus: Analyzing the AppleSEPKeyStore Kernel Extension

The interface between the kernel and the SEP's key management logic is the AppleSEPKeyStore kext. This binary is a prime target for understanding the proprietary IPC protocol.

Key Symbols:

  • aks_unwrap_key: The primary function for unwrapping a keybag element.
  • aks_get_lock_state: Queries the SEP for the current lock state (Locked/Unlocked).
  • sep_key_store_client_handle_message: The demuxer for messages received from the SEP.

The Attack Surface:
The communication protocol involves complex Type-Length-Value (TLV) structures.

  • Handle Confusion: The kernel refers to keys by 32-bit integer handles. A potential vulnerability class involves guessing or forging a handle to trick the SEP into using a key belonging to a different security domain (e.g., using a System Key to unwrap User Data).
  • Race Conditions: The AppleSEPKeyStore maintains a shadow state of the lock status. Discrepancies between the kernel's view of the lock state and the SEP's actual state can lead to logic bugs where the kernel allows an operation that the SEP should have blocked, or vice versa.

Tahoe Hardening:
In Tahoe, AppleSEPKeyStore operations are increasingly gated by the TXM. Critical key operations (like enabling FileVault or changing the recovery key) require a GENTER transition to GL1 to verify the policy before the request is forwarded to the SEP, adding an additional layer of authorization check that cannot be bypassed by kernel patching.

11.0 Conclusion: The Attack Surface Landscape

The architectural transformation introduced with macOS Tahoe and the M3/M4 silicon generation signifies the end of the "Kernel is King" era. We have moved from a monolithic trust model, where uid=0 and tfp0 were the ultimate objectives, to a federated security model where the kernel is merely a highly privileged, yet strictly supervised, tenant within a hardware-enforced hypervisor.

For the vulnerability researcher, this necessitates a shift in methodology. Fuzzing syscalls is no longer sufficient to compromise the system's root of trust. The new frontier lies in the Boundary Crossings—the specific, hardware-mediated bridges that allow data and execution flow to traverse the isolated domains.

11.1 Summary of Boundary Crossings

The following matrix details the architectural boundaries, the mechanisms used to traverse them, and the specific attack surface exposed at each junction.

11.1.1 Userland (EL0) $\leftrightarrow$ Kernel (EL1)

The Traditional Boundary, Hardened by Silicon.

  • Transition Mechanism:
    • Entry: SVC (Supervisor Call) instruction triggering a synchronous exception to VBAR_EL1 + 0x400.
    • Exit: ERET (Exception Return) restoring PC and PSTATE from ELR_EL1 and SPSR_EL1.
  • Hardware Enforcement:
    • PAC: Entry points are signed. The kernel verifies the thread state signature (kauth_thread_state) on return.
    • PPL/SPTM: The kernel cannot modify its own text or page tables to disable SMEP/SMAP equivalents (PAN/PXN).
  • The Tahoe Shift:
    • The kernel is no longer the final arbiter of virtual memory. When a user process requests mmap(RWX), the kernel cannot simply write to the translation table. It must request the SPTM to map the page.
    • Attack Surface:
      • Logic Bugs: Standard memory corruption (UAF, Heap Overflow) in kernel extensions still yields EL1 execution.
      • PAC Bypasses: Forging pointers to survive the ERET or function pointer authentication.
      • Argument Sanitization: The kernel must sanitize user pointers before passing them to the SPTM. A "Confused Deputy" attack where the kernel is tricked into asking the SPTM to map a privileged page into user space is the new tfp0.

11.1.2 Kernel (EL1) $\leftrightarrow$ Secure Page Table Monitor (GL2)

The "Mechanism" Boundary: The New Hypervisor.

  • Transition Mechanism:
    • Entry: GENTER (Opcode 0x00201420) with Selector ID in x15.
    • Exit: GEXIT (Opcode 0x00201400).
  • Hardware Enforcement:
    • GXF: Hardware context switch of SP_EL1 $\rightarrow$ SP_GL2.
    • SPRR: Atomic switch of permission views. Kernel text becomes RO/NX; SPTM text becomes RX.
  • Data Exchange:
    • Registers: x0 - x7 carry physical addresses (PPNs) and permission bitmaps.
    • Shared Memory: None. The SPTM reads physical memory directly via its own linear map.
  • Attack Surface:
    • State Machine Confusion: The SPTM enforces a Finite State Machine (FSM) on every physical page (Frame Table). The primary attack vector is finding a sequence of retype/map/unmap calls that desynchronizes the SPTM's view of a page from the hardware's actual state (e.g., aliasing a PAGE_TABLE as XNU_DATA).
    • Input Validation: Passing invalid physical addresses or edge-case permission combinations to sptm_map.
    • Panic-as-Oracle: Since SPTM panics on failure, timing side-channels or fault injection during the GENTER window are potential vectors to infer the memory layout of GL2.

11.1.3 Kernel (EL1) $\leftrightarrow$ Trusted Execution Monitor (GL1)

The "Policy" Boundary: The Signature Oracle.

  • Transition Mechanism:
    • Entry: GENTER (Targeting the TXM Dispatch Table).
  • Hardware Enforcement:
    • SPRR: TXM memory is visible only in GL1 context.
  • Data Exchange:
    • Physical Pointers: The kernel passes the physical address of a Code Directory or CMS blob.
    • Trust Cache: The kernel requests updates to the Dynamic Trust Cache.
  • Attack Surface:
    • Parsing Complexity: The TXM must parse complex, legacy formats (ASN.1, CMS, Mach-O headers) to verify signatures. This parsing happens in the highest privilege level. A buffer overflow in the TXM's ASN.1 parser is a "God Mode" exploit.
    • TOCTOU: The kernel loads the signature into memory, passes the physical address to TXM, and then TXM verifies it. If the kernel (or a DMA device) can modify that memory during the TXM's verification window, the signature check can be bypassed.
    • Logic Downgrades: Tricking the TXM into believing the device is in "Developer Mode" or "Permissive Mode" via corrupted NVRAM or LocalPolicy parsing.

11.1.4 Kernel (EL1) $\leftrightarrow$ Secure Enclave (SEP)

The "Air Gap" Boundary: The Parallel Computer.

  • Transition Mechanism:
    • Asynchronous IPC: Mailbox Registers (Doorbell) + Shared Memory Buffers (DART-mapped).
  • Hardware Enforcement:
    • Physical Isolation: Distinct CPU core, distinct MMU.
    • Memory Protection Engine: SEP memory is encrypted/authenticated inline.
  • Data Exchange:
    • Serialized Messages: L4 IPC format (Endpoints, TLV payloads).
    • Wrapped Keys: Keys are passed as opaque blobs; raw key material never crosses this boundary.
  • Attack Surface:
    • Message Parsing: Fuzzing the sepOS endpoint handlers (e.g., biometrickitd, securekeyvault).
    • Shared Memory Races: Modifying the contents of a DART-mapped buffer after the SEP has validated the header but before it processes the payload.
    • Anti-Replay Logic: Attempting to rollback the xART storage state to force the SEP to reuse old nonces or counters.

11.1.5 Kernel (EL1) $\leftrightarrow$ Exclaves (Secure Domain)

The "Microkernel" Boundary: The RingGate.

  • Transition Mechanism:
    • RingGate: XNUProxy kext marshals data $\rightarrow$ GENTER (to Secure Kernel) $\rightarrow$ IPC to Conclave.
  • Hardware Enforcement:
    • SPTM: Enforces physical memory isolation between XNU_DOMAIN and SK_DOMAIN.
  • Data Exchange:
    • Tightbeam: A strongly-typed IDL serialization format.
  • Attack Surface:
    • Proxy Confusion: Exploiting XNUProxy to route messages to the wrong Conclave.
    • IDL Deserialization: Bugs in the Tightbeam generated code within the Exclave.
    • Resource Exhaustion: Flooding the Secure Kernel with Downcalls to starve secure workloads (DoS).

11.2 The "Intel Gap": Security Disparities between x86 and Apple Silicon

While macOS Tahoe presents a unified user experience across architectures, the underlying security reality is a tale of two operating systems. On Apple Silicon, macOS is a hypervisor-managed, hardware-attested fortress. On Intel (x86_64), it remains a traditional monolithic kernel relying on legacy protection mechanisms. This divergence has created a massive "Intel Gap"—a disparity in exploit mitigation so severe that the same vulnerability often yields a trivial root shell on Intel while resulting in a harmless panic on Apple Silicon.

For the reverse engineer, understanding this gap is essential for targeting. The Intel architecture represents the "Soft Target," lacking the silicon-enforced boundaries of the SPTM, TXM, and PAC.

11.2.1 The Absence of Lateral Privilege (Ring 0 is King)

The most profound difference lies in the privilege hierarchy.

  • Apple Silicon: As detailed in Section 4.0, the kernel (EL1) is deprivileged. It is subservient to the SPTM (GL2) and TXM (GL1). Achieving arbitrary kernel read/write (KRW) is merely the first step in a long chain of exploitation. The attacker must still bypass the hardware-enforced page table protection and code signing logic.
  • Intel x86: The kernel runs in Ring 0 (Supervisor Mode). There is no "Ring -1" hypervisor enforcing integrity on the host macOS kernel. Apple never implemented a Windows VBS/HVCI-equivalent on Intel Macs.
    • Consequence: On Intel, KRW is Game Over. If an attacker can write to kernel memory, they can disable SIP, unhook the Sandbox, patch amfid, and overwrite page tables directly. There is no hardware entity above Ring 0 to say "No."

11.2.2 Static vs. Dynamic Kernel Integrity (KTRR vs. SPTM)

Both architectures attempt to enforce Kernel Text Read-Only Region (KTRR), but the implementation differs fundamentally in flexibility and robustness.

  • Intel (Hardware KTRR): On recent Intel Macs, KTRR is implemented via proprietary memory controller registers (configured via MSR).
    • Mechanism: The firmware locks a physical range of memory as Read-Only/Executable.
    • Limitation: This is Static. Once the range is locked at boot, it cannot change. This forces the kernel to fit all immutable code into a contiguous block. It cannot protect dynamically loaded drivers (KEXTs) with the same hardware rigor. KEXTs rely on software-managed page tables (CR0.WP bit), which a compromised kernel can disable.
  • Apple Silicon (SPTM):
    • Mechanism: The SPTM manages the Frame Table.
    • Advantage: This is Dynamic. The kernel can load a new extension (AKC), link it, and then ask the SPTM to "Seal" it. The SPTM transitions those specific pages to XNU_TEXT. This allows the "Immutable Kernel" coverage to extend to late-loaded drivers, a feat impossible on the static Intel KTRR implementation.

11.2.3 The CFI Chasm: PAC vs. CET

Control Flow Integrity (CFI) is the primary defense against ROP/JOP.

  • Apple Silicon: Pointer Authentication (PAC) is ubiquitous. It protects return addresses (stack), function pointers (heap/data), and C++ vtables. It provides cryptographic diversity based on pointer context.
  • Intel x86: Intel Macs support Control-flow Enforcement Technology (CET), specifically Shadow Stacks (IBT support is limited).
    • The Gap: CET Shadow Stacks protect return addresses effectively, but they do not protect Forward-Edge transfers (function pointers) with the same granularity as PAC.
    • Data Pointers: Crucially, Intel has no equivalent to APDAKey (Data Key). An attacker on Intel can still perform Data-Oriented Programming (DOP)—swapping valid object pointers or corrupting decision-making flags—without triggering a hardware fault. On Apple Silicon, these pointers are signed; forging them requires a signing gadget.

11.2.4 The Root of Trust: T2 vs. On-Die Boot ROM

The boot chain trust anchor differs physically.

  • Intel: The Root of Trust is the Apple T2 Security Chip (on models 2018-2020).
    • The Weakness: The T2 is a discrete bridge. It verifies the boot.efi and kernelcache signature before the Intel CPU starts. However, once the Intel CPU is executing, the T2 is effectively a peripheral connected via USB/PCIe. It cannot introspect the Intel CPU's execution state. It cannot stop a runtime kernel exploit.
  • Apple Silicon: The Root of Trust is the AP Boot ROM.
    • The Strength: The security logic (SEP, PKA, Boot Monitor) is on the same die. The Secure Enclave can monitor the power and clock lines of the AP. The SPTM (running on the AP) enforces the boot measurements continuously. The trust chain is not "handed off"; it is maintained throughout the runtime lifecycle.

11.2.5 I/O Security: VT-d vs. DART

DMA attacks are a classic method to bypass CPU memory protections.

  • Intel: Uses VT-d (Intel Virtualization Technology for Directed I/O).
    • Configuration: The kernel configures the IOMMU tables.
    • Vulnerability: If the kernel is compromised, it can reconfigure VT-d to allow a malicious Thunderbolt device to overwrite kernel memory (unless strict "DMA Protection" is enabled and locked, which relies on the kernel's integrity).
  • Apple Silicon: Uses DART (Device Address Resolution Table).
    • Enforcement: As detailed in Section 7.2.2, the kernel cannot write to DART registers. Only the SPTM can map I/O memory.
    • Result: Even a compromised kernel cannot weaponize a peripheral to perform a DMA attack against the monitor or the kernel text, because the SPTM will reject the mapping request.

11.2.6 Summary Table: The Security Disparity

Feature Intel Mac (x86_64) Apple Silicon (arm64e)
Highest Privilege Ring 0 (Kernel) GL2 (SPTM)
Page Table Protection Software (CR0.WP) Hardware (SPRR + SPTM)
Kernel Integrity Static KTRR (Boot only) Dynamic KIP (Runtime Sealing)
CFI CET (Shadow Stack) PAC (Cryptographic Signatures)
Vtable Protection None (Plaintext Pointers) Signed Pointers (APDAKey)
Code Signing Kernel-enforced (AMFI.kext) Monitor-enforced (TXM)
DMA Protection Kernel-managed VT-d Monitor-managed DART
Secure Enclave Discrete (T2 Chip) Integrated (On-Die)
Exploit Consequence Persistent Rootkit Possible System Panic / Non-Persistence

Conclusion for the Researcher:
The "Intel Gap" means that legacy Intel Macs are essentially running a different, far more vulnerable operating system, despite sharing the macOS version number. Exploits that require complex, multi-stage chains on M3 (e.g., bypassing PAC, confusing SPTM, racing TXM) can often be reduced to a single Use-After-Free and a ROP chain on Intel. As Apple phases out Intel support, the "easy mode" of macOS exploitation is rapidly vanishing.

The trajectory of macOS security architecture is not asymptotic; it is directional. Apple is not merely patching vulnerabilities in XNU; they are actively architecting its obsolescence as a security boundary. The "Tahoe" architecture provides the silicon primitives (SPTM, TXM, GL2) required to execute a long-term strategy of Architectural Attrition.

The future of macOS exploitation lies in understanding two concurrent trends: the ossification of the XNU kernel into a static, immutable appliance, and the migration of high-value logic into the opaque, hardware-isolated world of Exclaves.

11.3.1 The Deprecation of kmod_load: The Static Kernel

For decades, the ability to load Kernel Extensions (KEXTs) was a defining feature of macOS. It was also its Achilles' heel. KEXTs run at EL1, share the kernel's address space, and historically lacked the rigorous code review applied to the core kernel.

The mechanism for this—the kmod_load syscall (and the associated kmod_control traps)—represents a massive attack surface. It requires the kernel to possess a runtime linker (kld), capable of resolving symbols, applying relocations, and modifying executable memory.

The DriverKit End-Game:
Apple has systematically introduced userland replacements for kernel drivers: USBDriverKit, HIDDriverKit, AudioDriverKit, and NetworkingDriverKit.

  • Current State: In Tahoe, third-party KEXTs are deprecated. The userland tool kmutil manages the policy, but the actual loading still relies on the kernel's ability to link code. Loading a legacy KEXT now requires reducing system security (disabling SIP/Secure Boot) and interacting with the TXM via LocalPolicy to explicitly authorize the hash.

Future State: The Death of the Runtime Linker:
We are approaching a point where the kernel will effectively lose the ability to load dynamic code entirely in "Full Security" mode. The goal is to remove the kmod_load logic from the kernel entirely.

  • The "Sealed" Kernel: The Boot Kernel Collection (BKC) (loaded by iBoot) and the Auxiliary Kernel Collection (AKC) (loaded early by kernelmanagerd) will be the only permitted executable kernel code.
  • Pre-Linked Immutability: By moving all linking to build-time (kernelcache generation) or boot-time (iBoot verification), Apple can strip the dynamic linker logic (kld) from the runtime kernel. If the kernel doesn't know how to link a Mach-O, it cannot load a rootkit.
  • SPTM Enforcement: The SPTM already enforces that XNU_TEXT is immutable. The logical next step is for the SPTM to reject any sptm_retype request that attempts to create new XNU_TEXT pages after the initial boot sealing phase is complete.

RE Implication:
The era of the "Rootkit" is ending. If you cannot introduce new code into EL1 via kmod_load, and you cannot modify existing code due to KTRR/SPTM, persistence in the kernel becomes impossible. Attackers will be forced to live entirely within data-only attacks (DOP) or move their persistence to userland (which is easier to detect) or firmware (which is harder to achieve).

11.3.2 Exclave Expansion: Eating the Monolith

If XNU is the "Insecure World," Exclaves are the "Secure World." Currently, Exclaves are used for high-sensitivity, low-complexity tasks (Privacy Indicators, Passkeys). However, the architecture is designed to scale. Apple is effectively strangling the monolithic kernel by slowly migrating critical subsystems out of EL1 and into Exclaves.

Candidates for Migration:

  1. The Network Stack (skywalk):
    Apple has already introduced skywalk, a userland networking subsystem. The logical evolution is to move the TCP/IP stack and packet filtering logic into an Exclave.
    • Benefit: A remote code execution vulnerability in the Wi-Fi firmware or the TCP stack would compromise an isolated Exclave, not the entire kernel. The SPTM would prevent the compromised network stack from touching system memory.
  2. Filesystem Encryption (APFS):
    Currently, AppleSEPKeyStore handles key wrapping, but the bulk encryption happens via the AES Engine managed by the kernel. Moving the filesystem driver's cryptographic logic to an Exclave would ensure that even a kernel compromise cannot exfiltrate file keys, as the keys would exist only within the Exclave's memory domain.
  3. Audio and Media Processing:
    To protect DRM content and prevent microphone eavesdropping, the entire CoreAudio engine could be moved to a "Media Conclave."

The "Dark Matter" OS:
As more logic moves to Exclaves, a significant portion of the OS execution flow becomes invisible to standard introspection tools.

  • No DTrace: You cannot DTrace an Exclave.
  • No kdebug: Kernel tracing will show a "black hole" where the request enters XNUProxy and vanishes until the result returns.
  • Opaque State: The memory of an Exclave is physically unmappable by the kernel. A kernel memory dump (coredump) will contain gaps where the Exclave memory resides.

11.3.3 The "Hollow Kernel" Hypothesis

Extrapolating these trends leads to the Hollow Kernel Hypothesis.

In this future architecture, XNU (EL1) is demoted to a Compatibility Shim. Its primary role is to:

  1. Provide POSIX system call semantics for legacy userland applications.
  2. Manage coarse-grained scheduling of CPU resources.
  3. Act as a message bus (via XNUProxy) between userland applications and the real system logic running in Exclaves.

The Security Inversion:
In the traditional model, the Kernel protects the User. In the Hollow Kernel model, the Hardware (SPTM/TXM) protects the System from the Kernel.

  • The kernel is treated as untrusted code.
  • The TCB (Trusted Computing Base) shrinks from "The entire Kernel" to "The SPTM, TXM, and specific Exclaves."
  • A kernel compromise becomes a "Local DoS" or "Privacy Violation" rather than a "System Compromise."

11.3.4 The Visibility Gap: The End of Passive Analysis

For the reverse engineer, this shift is catastrophic for visibility.

  • Tightbeam IDL: The interface between XNU and Exclaves is defined by Tightbeam. Unlike MIG, which was relatively static, Tightbeam protocols can evolve rapidly. Reverse engineering the system will require constantly reconstructing these serialization formats.
  • The "Intel Gap" Closure: As Apple phases out Intel support completely, they will likely remove the legacy code paths in XNU that supported the "un-isolated" model. This will make the kernel source code (if still released) increasingly divergent from the binary reality running on M-series chips.
  • Hardware-Locked Debugging: Debugging an Exclave likely requires "Red" (Development) fused silicon. Researchers working on retail "Green" (Production) hardware will be effectively locked out of analyzing the internal logic of these secure subsystems, forced to treat them as black boxes and fuzz their inputs via XNUProxy.

Final Thought:
macOS is no longer just a Unix system. It is a distributed system running on a single die, governed by a hypervisor that doesn't exist in software. The kernel is dead; long live the Monitor.

Posted on
Written by int