Blog

Symbol index.

Every identifier these posts stop to explain, and the paragraph where they explain it. Built from the posts themselves, so it cannot drift from them.

561 symbols, drawn from every post on this blog.

B

/bin/ls

/bin/ls is a good first target, one of Apple’s own platform binaries:

Apple internals #3: The iOS code-signing pipeline → Hands-on: dumping the policy off a real binary
/bin/sh

A known address for any data we reference, such as the /bin/sh string we want rdi to point at.

Sigreturn-oriented programming → The abuse
__builtin_xnu_type_signature

Each allocation site in XNU now compiles to a view: a record in the __DATA_CONST,__kalloc_type section naming the type, carrying its size, and carrying a signature computed at compile time by __builtin_xnu_type_signature.

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
bison

You will also need flex, bison, libelf-dev, and libssl-dev to compile the kernel later.

Two ways into ring 0: system calls and kernel modules → Preparing the environment
bits

The last field, bits, is the pointer to everything else in the class: its method lists, ivar layout, properties and protocols.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
blanket

That last case is what Brandon Azad’s blanket (CVE-2018-4280) did on iOS: a Mach-service bug chained through reachable services to ReportCrash, which was unsandboxed, ran as root, and held task_for_pid-allow, so the confused deputy hande…

Apple internals #4: The iOS sandbox → What the profile tells an attacker
blr

pacibsp signs the link register with key IB against the stack pointer before it is spilled, retab authenticates it against the same stack pointer before returning, and the plain blr became blraaz: authenticate with key IA and a zero modi…

Apple internals #8: Pointer authentication → Hands-on: watching a pointer get signed
blraa

Those fused forms are where most of the signing actually happens: retab authenticates the link register and returns, braa and blraa authenticate and branch, ldraa authenticates and loads.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
blraaz

A trailing z means the modifier is zero: paciza, autiza, braaz, blraaz.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
BNCH

During a restore or update, the device sends Apple’s signing service (the Tatsu Signing Server, TSS) the list of images it wants to install plus two device-specific values: the ECID, a serial number unique to that SoC, and the ApNonce, a…

Apple internals #1: The iOS chain of trust → Personalization and the signing window
BNCN

It carries the boot nonce, tagged BNCN.

Apple internals #1: The iOS chain of trust → Image4: the container everything is signed in
bootstrap_look_up

Twenty-odd names, each one a service this profile may resolve into a send right; every other name on the system is unreachable, and bootstrap_look_up, the call that turns a service name into a port, returns nothing.

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
braa

Those fused forms are where most of the signing actually happens: retab authenticates the link register and returns, braa and blraa authenticate and branch, ldraa authenticates and loads.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
braaz

A trailing z means the modifier is zero: paciza, autiza, braaz, blraaz.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
buf

It copies LEN bytes from the user pointer s into our kernel buffer buf and returns zero on success.

Two ways into ring 0: system calls and kernel modules → The write operation
bytes

The read primitive comes from the same trick with a different shape: a forged NSData, whose layout is { isa, length, bytes, deallocator } with the deallocator left NULL, hexdumps whatever memory its bytes field points at when it is sent…

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa

D

/dev

Some of them also expose communication interfaces, often character devices under /dev, the way the KVM module exposes /dev/kvm.

Two ways into ring 0: system calls and kernel modules → Path 2: the kernel module
/dev/kvm

Some of them also expose communication interfaces, often character devices under /dev, the way the KVM module exposes /dev/kvm.

Two ways into ring 0: system calls and kernel modules → Path 2: the kernel module
__DATA

Before the page-table monitors existed, a loadable trust cache lived in ordinary writable __DATA kernel memory, and an exploit with kernel read/write appended its own binaries’ cdhashes to it.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
delta_create_ctx_t

Tommy Muir (Muirey03), CVE-2022-32832 write-up and proof-of-concept, the source for the case study: selector 49, methodDeltaCreateFinalize, the delta_create_ctx_t double-free, the root precondition, and the IOLockLock/IOLockUnlock fix in…

Apple internals #5: IOKit up close
deltaCreateTeardown

deltaCreateTeardown frees the context and the properties hanging off it.

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
deny

action is allow or deny.

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
desc_count

This invalid config can only happen from mach_msg2_trap() since desc_count is passed as its own trap argument.”

Apple internals #6: Mach messages, MIG and XPC → State in 2026
dest

dest: where the compressed data is written.

Recovering payloads from PE resources → Compressing the payload
destLen

destLen: where the compressed size in bytes is written.

Recovering payloads from PE resources → Compressing the payload
DGST

The Manifest images list is the set of components this ticket vouches for; with -v, each is shown alongside the digest (DGST) the loader will require the real image to match.

Apple internals #1: The iOS chain of trust → Hands-on: from IPSW to kernelcache
diagnosticd

diagnosticd is a convenient pick: it runs as root, and once asked to monitor a process it sends a status message several times a second.

Apple internals #6: Mach messages, MIG and XPC → CVE-2023-32405, start to finish
directory

If the internal directory option doesn’t exist (meaning no -d was used at launch, which is the default behavior), the program iterates over every letter from A to Z and tries to recursively encrypt every drive mounted on the system: A:,…

How I broke Rhysida ransomware encryption → Walking the file system
directory_modifier

The path is stored in a directory_modifier variable, whose value then gets written into the program’s internal options.

How I broke Rhysida ransomware encryption → Entry point and initialization
dispatch_async

SMJobBless ends up calling routine 1004, and smd runs that routine’s body through dispatch_async, on a queue that is not the XPC event handler.

Apple internals #6: Mach messages, MIG and XPC → CVE-2023-32405, start to finish
DM_CRYPT

If your disk is encrypted with cryptsetup, enable DM_CRYPT (make menuconfig, “Crypt target support”) or you will not be able to unlock it after booting the new kernel.

Two ways into ring 0: system calls and kernel modules → Preparing the environment
dmesg

dmesg shows the string our init function printed:

Two ways into ring 0: system calls and kernel modules → Compiling and loading
dst_size

It allocates a clear buffer for the source data and a compressed buffer for the output, with dst_size receiving the final compressed length.

Recovering payloads from PE resources → Compressing the payload
dup

This is the ordinary spray, and the object list should look familiar by now: psemnode from sem_open(), fileproc from dup(), kqworkloop from kqueue_workloop_ctl().

Apple internals #7: The zone allocator up close → From a PUAF to read and write
dyld

Everything here is public: Apple’s open-source objc4 and dyld, published research, and a Mac running a stock, unmodified macOS.

Apple internals #10: The Objective-C runtime and the shared cache
dyld_shared_cache_util

Apple’s own dyld_shared_cache_util is the other name you will see in older write-ups, and it is not installed on a stock macOS 26.4.1, so it is a build-from-source step rather than a command you have.

Apple internals #10: The Objective-C runtime and the shared cache → The libraries are not on disk

E

/etc/default/grub

If yours does not, expose the GRUB menu by editing /etc/default/grub so you can pick the entry, then run sudo update-grub.

Two ways into ring 0: system calls and kernel modules → Compiling and booting
__exit

Reading top to bottom: we include the kernel headers we need; the MODULE_* macros attach metadata (license, author, description); two functions tagged __init and __exit run when the module is loaded and unloaded; and module_init / module…

Two ways into ring 0: system calls and kernel modules → A minimal module
early_random

XNU takes a task’s jop_pid, the diversifier behind the A keys, from its shared region, so every process on the same shared cache gets the same value, while rop_pid, behind the B keys, is drawn per task from early_random().

Apple internals #8: Pointer authentication → Five keys and a modifier
eax

It loads eax with 0x77 (the 32-bit sigreturn number) on its own and fires the interrupt, so you do not even need a separate step to set the syscall number.

Sigreturn-oriented programming → Finding the pieces in practice
ECID

The ECID is the personalization described earlier: run this on a manifest from your own phone and the ECID it prints is that phone’s.

Apple internals #1: The iOS chain of trust → Hands-on: from IPSW to kernelcache
endpointsecurityd

smd, endpointsecurityd and kernelmanagerd import xpc_dictionary_get_audit_token, the per-message call.

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
enter_lockdown_mode

ppl.c and txm.c both provide register_code_signature, verify_code_signature, associate_jit_region, toggle_developer_mode and enter_lockdown_mode: the same operations, with the enforcement in a different place on each generation of hardware.

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
errno

modifier tweaks the outcome: report, a specific errno to return on deny, send-signal to kill on violation, and no-sandbox, which lets a child run unconfined.

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
EXCLAVES_BOOT_STAGE_NONE

Both values are named in osfmk/mach/exclaves.h: EXCLAVES_STATUS_NOT_SUPPORTED is 0xFF, and EXCLAVES_BOOT_STAGE_NONE is ~0u, which the sysctl prints as a signed -1.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
EXCLAVES_STATUS_NOT_SUPPORTED

Both values are named in osfmk/mach/exclaves.h: EXCLAVES_STATUS_NOT_SUPPORTED is 0xFF, and EXCLAVES_BOOT_STAGE_NONE is ~0u, which the sysctl prints as a signed -1.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
exclude_directories

Some folders are skipped by the encryptor, via an array of directory paths named exclude_directories that the isDirectoryExcluded function checks against.

How I broke Rhysida ransomware encryption → Walking the file system
exclude_extensions

In isFileExcluded we see filtering on file extensions, driven by an integer array exclude_extensions that holds the extensions to skip.

How I broke Rhysida ransomware encryption → Encrypting files
exec

The MACF label we just met inside p_ucred is where the following article starts: the framework that decides, at every exec, what is even allowed to run, and how AMFI, code signing, and trust caches hang off it.

Apple internals #2: XNU under the hood → Where this leaves us
execve

Every time a process is created, through execve or posix_spawn, the kernel answers one question before it runs a single instruction of the new image: may these bytes execute?

Apple internals #3: The iOS code-signing pipeline
exit

Instead, we’ll use direct system calls like write() and exit().

Building the smallest ELF program
externalMethod

On the way in, the kernel packs them into an IOExternalMethodArguments structure and calls the driver’s externalMethod.

Apple internals #5: IOKit up close → The userland-to-kernel bridge
extra_rc

extra_rc

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
ExtractISA

The isa was not signed at all on the first arm64e devices: the objc4 that shipped with macOS 10.15 has neither ISA_SIGNING_KEY nor ExtractISA, and Apple’s 2019 ABI document said outright that pointer authentication could not protect it.

Apple internals #8: Pointer authentication → What Apple signs

I

/ipp/IppSrvr

So the POST to /ipp/IppSrvr has to succeed.

ActiveX controller exploitation → Emulating an IPP server
__init

Reading top to bottom: we include the kernel headers we need; the MODULE_* macros attach metadata (license, author, description); two functions tagged __init and __exit run when the module is loaded and unloaded; and module_init / module…

Two ways into ring 0: system calls and kernel modules → A minimal module
ibec

sptm and trxm are in the manifest, next to krnl, ibot and ibec, tied to this machine’s ECID and this boot’s nonce.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
ibot

sptm and trxm are in the manifest, next to krnl, ibot and ibec, tied to this machine’s ECID and this boot’s nonce.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
ida_kernelcache

IDA users can additionally run ida_kernelcache, the maintained cellebrite-labs fork of Azad’s original, to rebuild the C++ vtables and OSMetaClass hierarchies.

Apple internals #2: XNU under the hood → Hands-on: reading the structures out of the kernelcache
IDR_RCDATA0

IDR_RCDATA0: the resource name.

Recovering payloads from PE resources → Embedding it as a resource
IDR_RCDATA1

A real packer would carry them the same way it carries the payload, for instance in a second resource (IDR_RCDATA1) that the unpacker reads first.

Recovering payloads from PE resources → Decompressing in memory
ie_bits

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
IE_BITS_GEN_MASK

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
IE_BITS_TYPE_MASK

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
IE_BITS_UREFS_MASK

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
IE_NULL

ipc_entry_lookup() returns IE_NULL on a generation mismatch and the caller turns that into KERN_INVALID_NAME, rather than silently aliasing the new port.

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
ie_object

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
IKOT_HOST_PRIV

Their io_bits field carries a kobject type from a fixed set (IKOT_TASK_CONTROL, IKOT_THREAD_CONTROL, IKOT_HOST_PRIV, IKOT_IOKIT_CONNECT), and ip_kobject points at the real kernel object: a task, a thread, an IOKit user client.

Apple internals #2: XNU under the hood → Ports and port rights
IKOT_IOKIT_CONNECT

Their io_bits field carries a kobject type from a fixed set (IKOT_TASK_CONTROL, IKOT_THREAD_CONTROL, IKOT_HOST_PRIV, IKOT_IOKIT_CONNECT), and ip_kobject points at the real kernel object: a task, a thread, an IOKit user client.

Apple internals #2: XNU under the hood → Ports and port rights
IKOT_TASK_CONTROL

Their io_bits field carries a kobject type from a fixed set (IKOT_TASK_CONTROL, IKOT_THREAD_CONTROL, IKOT_HOST_PRIV, IKOT_IOKIT_CONNECT), and ip_kobject points at the real kernel object: a task, a thread, an IOKit user client.

Apple internals #2: XNU under the hood → Ports and port rights
IKOT_THREAD_CONTROL

Their io_bits field carries a kobject type from a fixed set (IKOT_TASK_CONTROL, IKOT_THREAD_CONTROL, IKOT_HOST_PRIV, IKOT_IOKIT_CONNECT), and ip_kobject points at the real kernel object: a task, a thread, an IOKit user client.

Apple internals #2: XNU under the hood → Ports and port rights
IM4P

The cache is carried in an Image4 container (an IM4P payload, the format from the boot-chain post), tagged trst for the static cache, rtsc for a restore cache, ltrs for a loadable one.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
IMGPF_NOJOP

A task that is not arm64e runs with user PAC switched off entirely: bsd/kern/kern_exec.c flags the image IMGPF_NOJOP, and bsd/kern/mach_loader.c then creates its address space with PMAP_CREATE_DISABLE_JOP.

Apple internals #8: Pointer authentication → Five keys and a modifier
IMP

A bucket is a { IMP, SEL } pair, the function pointer and the selector, in that order on arm64.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
important_check

That makes sub_1000FBD0 the gate that matters, so we rename it important_check and read it.

ActiveX controller exploitation → Reversing ienipp.ocx
init_prng

init_prng initializes the pseudo-random number generator.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
inject_trusts

Electra’s inject_trusts is the canonical example, adding the cdhashes of amfid_payload.dylib and the rest of the jailbreak’s userland.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
inout

voucher_swap, named in XNU under the hood, is the canonical instance, and its own violation is subtler: task_swap_mach_voucher() breaks the rules for an inout argument, leaking one voucher reference and consuming another it had only borr…

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
insmod

The one that matters is my_module.ko, the kernel object we load with insmod:

Two ways into ring 0: system calls and kernel modules → Compiling and loading
int

Both are a small integer, valid only inside one process, that indexes a per-process table in the kernel and names a kernel object you never touch directly.

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
io_bits

Three things go into that modifier: the address of the object, the object’s io_bits, which is the field carrying its type, and a hash of the field’s own name.

Apple internals #8: Pointer authentication → What Apple signs
io_connect_async_method

(The synchronous IOConnectCall* functions cross into the kernel as one MIG routine, io_connect_method; the IOConnectCallAsync* variants use a separate one, io_connect_async_method.

Apple internals #5: IOKit up close → The userland-to-kernel bridge
io_connect_method

(The synchronous IOConnectCall* functions cross into the kernel as one MIG routine, io_connect_method; the IOConnectCallAsync* variants use a separate one, io_connect_async_method.

Apple internals #5: IOKit up close → The userland-to-kernel bridge
io_connect_t

IOServiceOpen() → io_connect_t

Apple internals #5: IOKit up close → What IOKit is
ioclasscount

ioclasscount prints how many instances of each class exist right now.

Apple internals #5: IOKit up close → The surface, and how to see it
IOCommandGate

Not every client is racy: a driver that funnels externalMethod through an IOCommandGate is serialised by its workloop, and an IOUserClient2022 client that asks for it is serialised by the framework.

Apple internals #5: IOKit up close → The bug classes
IOConnectCallMethod

A user client is a device node you open, and IOConnectCallMethod is the call you make on it afterwards.

Apple internals #5: IOKit up close → What IOKit is
ioctl

If you come from Linux, the shape is ioctl.

Apple internals #5: IOKit up close → What IOKit is
IOExternalMethodArguments

On the way in, the kernel packs them into an IOExternalMethodArguments structure and calls the driver’s externalMethod.

Apple internals #5: IOKit up close → The userland-to-kernel bridge
IOExternalMethodDispatch

The classic form is an array of IOExternalMethodDispatch, one 0x18-byte entry per selector:

Apple internals #5: IOKit up close → Where the call lands: the dispatch table
IOLockLock

Apple fixed it by wrapping the body in IOLockLock/IOLockUnlock.

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
IOLockUnlock

Apple fixed it by wrapping the body in IOLockLock/IOLockUnlock.

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
IOMobileFramebufferUserClient

IOMobileFramebufferUserClient is the family behind several bugs used in the wild.

Apple internals #5: IOKit up close → The surface, and how to see it
ioreg

Parse your sandbox profile’s iokit-open rules and cross-check the live registry with ioreg.

Apple internals #5: IOKit up close → Picking targets
IOServiceOpen

IOServiceOpen() → io_connect_t

Apple internals #5: IOKit up close → What IOKit is
IOSurfaceRoot

IOSurfaceRoot and AppleAPFSContainer both opened, 0x00000000 being kIOReturnSuccess, so an ordinary unprivileged process is now holding a live connection to a kernel driver.

Apple internals #5: IOKit up close → The surface, and how to see it
IOSurfaceRootUserClient

ioclasscount reports an instance count bumped by the number of direct subclasses that have any instances, so a concrete class like IOSurfaceRootUserClient at 88 really is 88 live connections, while an abstract base reports how many of it…

Apple internals #5: IOKit up close → The surface, and how to see it
IOUserClient2022

IOUserClient2022 arrived in iOS 16 and by now carries most of the clients worth attacking.

Apple internals #5: IOKit up close → Where the call lands: the dispatch table
ip6_pktopts

Apple published the arithmetic using SockPuppet, Ned Williamson’s 2019 bug, as the worked example: in the boot they analysed, its victim object ip6_pktopts landed in a bucket with ten other types, and a single replacement type gives an 8…

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
ip_kobject

Their io_bits field carries a kobject type from a fixed set (IKOT_TASK_CONTROL, IKOT_THREAD_CONTROL, IKOT_HOST_PRIV, IKOT_IOKIT_CONNECT), and ip_kobject points at the real kernel object: a task, a thread, an IOKit user client.

Apple internals #2: XNU under the hood → Ports and port rights
ipc_entry

Each ipc_entry carries ie_object, a pointer to the ipc_port (PAC-signed on arm64e), and ie_bits, which packs the user-reference count in its low 16 bits (IE_BITS_UREFS_MASK, 0x0000ffff), the right type just above them (IE_BITS_TYPE_MASK,…

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
ipc_entry.h

Apple’s open-source XNU is ground truth for every structure and macro named above: osfmk/ipc/ipc_port.h, ipc_object.h, ipc_entry.h, osfmk/mach/port.h (MACH_PORT_INDEX and MACH_PORT_GEN), osfmk/kern/syscall_sw.c (the trap table), osfmk/ke…

Apple internals #2: XNU under the hood
ipc_entry_lookup

ipc_entry_lookup() returns IE_NULL on a generation mismatch and the caller turns that into KERN_INVALID_NAME, rather than silently aliasing the new port.

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
ipc_kmsg

mach_msg2 copies a user message into an ipc_kmsg and processes its typed descriptors, port-right transfers and out-of-line memory included.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
ipc_kmsg_destroy

A routine that returns success on a path where it consumed nothing leaks the right, because ipc_kmsg_free() releases no references and the port can never be freed; one that consumed a right and then hit an error path has it released twic…

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
ipc_kmsg_free

A routine that returns success on a path where it consumed nothing leaks the right, because ipc_kmsg_free() releases no references and the port can never be freed; one that consumed a right and then hit an error path has it released twic…

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
ipc_kobject_server

XNU says it outright in ipc_kobject_server():

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
ipc_object.h

Apple’s open-source XNU is ground truth for every structure and macro named above: osfmk/ipc/ipc_port.h, ipc_object.h, ipc_entry.h, osfmk/mach/port.h (MACH_PORT_INDEX and MACH_PORT_GEN), osfmk/kern/syscall_sw.c (the trap table), osfmk/ke…

Apple internals #2: XNU under the hood
ipc_port

A memory-safety bug in a niche IOKit driver or a BSD socket option corrupts the same privileged address space that holds ipc_port objects and, before those monitors, the page tables, so a BSD bug regularly ends up as a Mach primitive.

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
ipc_right_lookup_read

Decompile ipc_right_lookup_read, which resolves a port name for a read.

Apple internals #2: XNU under the hood → Hands-on: reading the structures out of the kernelcache
ipc_space

Mach port: mach_port_name_t into ipc_space

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
ipc_voucher

Brandon Azad’s voucher_swap (CVE-2019-6225) freed an ipc_voucher through a reference-counting error in MIG (the Mach Interface Generator, which auto-writes the code that unpacks Mach messages for kernel services), reallocated it as a por…

Apple internals #2: XNU under the hood → The shape of a kernel exploit
IppCreateServerRef

If IppCreateServerRef returns NULL, IppMgmtGetServerVersion2 returns zero, and zero is the success code.

ActiveX controller exploitation → Reversing nipplib.dll
IppFindAttributeInSet

IppFindAttributeInSet walks the attributes received and compares each name against the one requested.

ActiveX controller exploitation → Emulating an IPP server
IppGetDriverSettings2

IppGetDriverSettings2 is the eventual target, but there is no point reversing a function we cannot reach, so IppMgmtGetServerVersion2 comes first.

ActiveX controller exploitation → Reversing nipplib.dll
IppMgmtGetServerVersion2

It calls IppMgmtGetServerVersion2, exported by nipplib.dll, and returns zero when that function returns zero:

ActiveX controller exploitation → Reversing ienipp.ocx
ipsw

To just get one open in a disassembler, ipsw downloads and decompresses it in a single command, without fetching the whole IPSW firmware bundle:

Apple internals #2: XNU under the hood → Hands-on: reading the structures out of the kernelcache
is_

The kernel end of this is is_io_connect_method (the is_ prefix marks the MIG server routine), and tracing it needs a DTrace fbt probe, which System Integrity Protection blocks on a stock Mac.

Apple internals #5: IOKit up close → The surface, and how to see it
is_io_connect_method

The kernel end of this is is_io_connect_method (the is_ prefix marks the MIG server routine), and tracing it needs a DTrace fbt probe, which System Integrity Protection blocks on a stock Mac.

Apple internals #5: IOKit up close → The surface, and how to see it
is_table

The space holds is_table, an array of struct ipc_entry.

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
isa

An Objective-C object is a C struct whose first word is called isa, short for “is a”, because it says which class the object is an instance of.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
ISA_MASK

ISA_MASK is 0x007ffffffffffff8 and keeps the whole 52-bit field, signature included.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
ISA_MASK_NOSIG

ISA_MASK_NOSIG keeps the address alone: 0x00007ffffffffff8 on macOS, 0x0000000ffffffff8 on iOS, where the address space handed to a process is smaller.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
ISA_SIGNING_AUTH

Apple, objc4 source: runtime/isa.h for the two arm64 bitfield layouts and the ISA_MASK / ISA_MASK_NOSIG constants, runtime/objc-config.h for ISA_SIGNING_DISCRIMINATOR (0x6AE1) and the ISA_SIGNING_STRIP / ISA_SIGNING_AUTH modes, runtime/a…

Apple internals #10: The Objective-C runtime and the shared cache
ISA_SIGNING_AUTH_MODE

Which one you get is the compile-time flag ISA_SIGNING_AUTH_MODE, and this build authenticates.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
ISA_SIGNING_DISCRIMINATOR

The runtime builds a modifier out of the object’s own address and the constant 0x6AE1, which objc-config.h names ISA_SIGNING_DISCRIMINATOR, and authenticates the class pointer with it.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
ISA_SIGNING_KEY

The isa was not signed at all on the first arm64e devices: the objc4 that shipped with macOS 10.15 has neither ISA_SIGNING_KEY nor ExtractISA, and Apple’s 2019 ABI document said outright that pointer authentication could not protect it.

Apple internals #8: Pointer authentication → What Apple signs
ISA_SIGNING_STRIP

Apple, objc4 source: runtime/isa.h for the two arm64 bitfield layouts and the ISA_MASK / ISA_MASK_NOSIG constants, runtime/objc-config.h for ISA_SIGNING_DISCRIMINATOR (0x6AE1) and the ISA_SIGNING_STRIP / ISA_SIGNING_AUTH modes, runtime/a…

Apple internals #10: The Objective-C runtime and the shared cache
isDirectoryExcluded

Some folders are skipped by the encryptor, via an array of directory paths named exclude_directories that the isDirectoryExcluded function checks against.

How I broke Rhysida ransomware encryption → Walking the file system
isFileExcluded

It walks the array of files to encrypt, extracts the file path’s name, checks whether the file is actually a legitimate target with isFileExcluded, and encrypts it where appropriate with processFileEnc.

How I broke Rhysida ransomware encryption → Encrypting files
itk_space

It hangs off the task as itk_space, and it is precisely the Mach counterpart of the file-descriptor table p_fd.

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities

S

/System/Library/dyld/

This is not /System/Library/dyld/ any more; it is a cryptex, a signed disk image mounted over the system volume at boot, which is how Apple ships a new cache without touching the sealed system snapshot.

Apple internals #10: The Objective-C runtime and the shared cache → The libraries are not on disk
/System/Library/Kernels/

macOS ships one kernel per SoC as a plain Mach-O in /System/Library/Kernels/, so both designs sit on the same disk.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
sandblaster

(malus-security), “SandBlaster: Reversing the Apple Sandbox” and the sandblaster decompiler (maintained fork at cellebrite-labs); reverse-sandbox/operation_node.py documents the 8-byte node layout, the 0x00 non-terminal / 0x01 terminal t…

Apple internals #4: The iOS sandbox
sandbox_check

To get the same list on iOS you query it at runtime with sandbox_check or Levin’s sbtool against a live process, or you pull the kext out of the kernelcache and run it through SandBlaster to recover the SBPL.

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
sandbox_check_by_audit_token

The xpc_dictionary_get_audit_token line is the fix from the case study, three years on, sitting in the symbol table; sandbox_check_by_audit_token shows smd also runs its sandbox check against the message’s token rather than the connectio…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
sandboxd

Watch also for audit_token_to_pid, which configd, sandboxd and taskgated carry: it takes a token and collapses it back to a PID, discarding the pidversion that made the token worth having, so if that PID drives an authorization decision…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
sbtool

To get the same list on iOS you query it at runtime with sandbox_check or Levin’s sbtool against a live process, or you pull the kext out of the kernelcache and run it through SandBlaster to recover the SBPL.

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
SCM_RIGHTS

And like a file descriptor, a port is transferable: you hand a port to another process inside a Mach message, the same way you hand a file descriptor to another process with SCM_RIGHTS over a Unix socket.

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
secd

trustd and secd import xpc_connection_get_audit_token, which is not a bug by itself since it is correct inside an event handler; telling those two cases apart is the part that needs the disassembler.

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
self_remove_modifier

The boolean is held in a self_remove_modifier variable and likewise written into the internal options.

How I broke Rhysida ransomware encryption → Entry point and initialization
sem_open

bsd/kern/posix_sem.c allocates three types through kalloc_type() on the way through sem_open(), so a POSIX semaphore is a spray any process can drive with no privilege at all.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land

sem_unlink() immediately after sem_open() is the interesting line.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
sepi

The payload is the IM4P: a four-character tag naming the contents (krnl for the kernelcache, ibot for iBoot, sepi for the SEP firmware), a description string such as a build version, the payload bytes, and the compression scheme if there…

Apple internals #1: The iOS chain of trust → Image4: the container everything is signed in
setBG

When encryption finishes, the program updates the Windows wallpaper, replacing it with a ransom note as well, via the setBG function.

How I broke Rhysida ransomware encryption → Ransom note and end of execution
shiftcls

The layout most write-ups quote is a different one: 33 bits of shiftcls at bit 3, a six-bit magic field holding 0x1a, extra_rc on 19 bits, ISA_MASK at 0x0000000ffffffff8.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
shiftcls_and_sig

shiftcls_and_sig

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
ShowMessageBox

The control exposes a ShowMessageBox method, which is the cheapest possible test:

ActiveX controller exploitation → Finding the controller
SIGKILL

Every normal process on iOS carries the CS_HARD | CS_KILL flags, so a mismatch is fatal: the kernel kills the process on the spot with SIGKILL, the signal a process cannot catch or ignore.

Apple internals #3: The iOS code-signing pipeline → What a signature is, ending at the cdhash
SignedData

It is an in-kernel validator (packaged as CoreTrust.kext on most builds) that parses the CMS SignedData structure, builds the X.509 certificate chain (X.509 is the standard certificate format), verifies every signature in it, and confirm…

Apple internals #3: The iOS code-signing pipeline → CoreTrust: the check that moved into the kernel
SignerInfo

The same component then produced a second TrollStore-grade bug, CVE-2023-41991, a multiple-SignerInfo validation flaw that carried TrollStore 2 through iOS 15.5 to 16.6.1 and 17.0 and was fixed in 16.7 and 17.0.1.

Apple internals #3: The iOS code-signing pipeline → State in 2026
sigreturn

To see why sigreturn exists, follow what happens when a process receives a signal it has a handler for.

Sigreturn-oriented programming → How a signal leaves the kernel
SK_DOMAIN

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
smd

The one the case study’s exploit sends to smd, the macOS service-management daemon, carries a routine number, the identifier of the helper to install, and an authorization blob.

Apple internals #6: Mach messages, MIG and XPC → XPC on top
SMJobBless

smd is the service management daemon behind SMJobBless, the API that installs a privileged helper tool: a small binary shipped inside an app bundle that then runs as root, so an app can do the few things that need root without running as…

Apple internals #6: Mach messages, MIG and XPC → CVE-2023-32405, start to finish
socket

PhysPuppet is reachable from an app, so sockets are available; Smith, the bug Félix found next, is reachable from WebContent, where socket() is denied and the whole read/write had to be rebuilt from what that profile allows.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
source

source: where the data to compress is read from.

Recovering payloads from PE resources → Compressing the payload
specinfo

kfd spends its first read and write on a better read and write: it reads the file descriptor’s fg_ops->fo_kqfilter to recover the KASLR slide, then overwrites the device number in the file’s specinfo so an innocuous character device now…

Apple internals #7: The zone allocator up close → From a PUAF to read and write
sptm

An Image4 payload with its own four-character code, sptm, unencrypted and LZFSE-compressed.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
sptm_common.h

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
SPTM_DOMAIN

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
sptm_map_page

XNU at EL1 may no longer write a page-table entry at all: every mapping is a call to sptm_map_page(), which SPTM validates against a type it keeps for each physical frame and a rule set for which types XNU is allowed to map.

Apple internals #7: The zone allocator up close → State in 2026
sptm_retype

Changing a frame’s type is a second call, sptm_retype(), taking the current type, the new type, and parameters specific to the transition.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
SPTM_XNU_CODE

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
srand

srand is seeded with time(0).

How I broke Rhysida ransomware encryption → Entry point and initialization
strcpy

We know it is a buffer overflow on realm, and one strcpy among the many in this function takes realm as its source:

ActiveX controller exploitation → The vulnerable function
strip

Use strip to strip an executable from all its symbols and relocation information: strip -s smallest_elf.bin

Building the smallest ELF program → Strip symbols
strlen

EIP is intact; EBX holds 0x41414141 and the fault is inside a strlen that received it as an address.

ActiveX controller exploitation → Controlling EIP
strstr

IppGetDriverSettings2 has one more gate before any interesting code, an strstr on the URL:

ActiveX controller exploitation → The vulnerable function
sub_1000FBD0

If sub_1000FBD0 returns anything other than zero, the vulnerable call is skipped and we jump straight to the end of the function.

ActiveX controller exploitation → Reversing ienipp.ocx
sub_50022960

The second one depends on sub_50022960: if that helper returns anything other than zero, IppCreateServerRef takes the failure path, which is exactly what we want.

ActiveX controller exploitation → Making IppCreateServerRef fail
sub_5C04B514

It forwards to sub_5C04B514, which is where the logic is:

ActiveX controller exploitation → Reversing nipplib.dll
subpath

filter narrows the rule to specific arguments: path filters (literal, subpath, prefix, regex), the Mach global-name / local-name (the service name being looked up), require-entitlement, iokit-user-client-class, network socket-domain / re…

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
super

A send to super takes objc_msgSendSuper2 instead, and a method declared objc_direct is called outright with no dispatch at all, but those are the exceptions.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
superclass

ldr x10, [x16, #0x10] reads the word at offset 0x10 of the class, which is cache, the third field of objc_class after isa and superclass.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
symbolicator

ipsw kernel sym, paired with blacktop’s symbolicator signatures, rebuilds most of them and writes a JSON you apply with the matching script for Ghidra, IDA Pro, or Binary Ninja.

Apple internals #2: XNU under the hood → Hands-on: reading the structures out of the kernelcache
syscall

A way to invoke sigreturn, which means getting rax to 15 and reaching a syscall instruction.

Sigreturn-oriented programming → The abuse
SYSCALL_DEFINE2

First, we define it with the SYSCALL_DEFINE2 macro, where the 2 is the number of parameters.

Two ways into ring 0: system calls and kernel modules → Writing the call
sysent

sysent (positive numbers)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
sysinfo.dwNumberOfProcessors

A call to GetSystemInfo, then a read of sysinfo.dwNumberOfProcessors.

How I broke Rhysida ransomware encryption → Entry point and initialization
system32

We hand IDA the DLL from system32 and let it resolve.

ActiveX controller exploitation → Reversing ienipp.ocx

U

/usr/libexec/amfid

amfid (/usr/libexec/amfid) is the userland daemon behind the third row of the pipeline, the one that carries third-party code: apps signed by a developer or an enterprise rather than baked into the OS or shipped through the App Store.

Apple internals #3: The iOS code-signing pipeline → amfid and provisioning profiles
ucontext

On x86-64 the relevant call is rt_sigreturn, syscall number 15, and the frame is an rt_sigframe wrapping a ucontext.

Sigreturn-oriented programming → How a signal leaves the kernel
uncompress

The uncompress() function mirrors the compress2() we used to pack the payload in the first place.

Recovering payloads from PE resources → Decompressing in memory
undefined

null, true, false, and undefined likewise have their own small fixed encodings.

Exploiting JavaScript engines: from type confusion to code execution → How values live in memory
userName

The first is a length check on each of the four parameters, printerUri, realm, userName and password, applied at the very top of the block:

ActiveX controller exploitation → Reversing ienipp.ocx
USR01

Current clang stamps a later version, which otool -hv on the binary shows as USR01, so it runs with nothing special enabled.

Apple internals #8: Pointer authentication → What Apple signs
uthread

uthread (syscall state)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
UXN

Four bits of a page-table entry, AP[1], AP[0], UXN and PXN, stop encoding permissions directly.

Apple internals #9: SPTM, TXM and memory tagging → Changing permissions without touching the page tables

V

/var/MobileDeviceProvisioningProfiles

A provisioning profile is a CMS-signed plist, stored under /var/MobileDeviceProvisioningProfiles, that binds four things together:

Apple internals #3: The iOS code-signing pipeline → amfid and provisioning profiles
verify_code_signature

ppl.c and txm.c both provide register_code_signature, verify_code_signature, associate_jit_region, toggle_developer_mode and enter_lockdown_mode: the same operations, with the enforcement in a different place on each generation of hardware.

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
vm_copy

So take free pages back a few at a time, with vm_copy() on a purgeable region, and after each one scan every dangling page for the content you just wrote.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
vm_deallocate

vm_deallocate() over that range calls pmap_remove_options(), which reaches this, in osfmk/arm/pmap/pmap.c:

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
vm_map

vm_map, vm_object, pmap (Mach owns it)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
vm_map_copy

Two details recur in exploitation: vm_map_copy, the transient object that carries out-of-line message data, is a standard heap-spray and disclosure primitive, and pmap is what the page-table monitors (PPL, then SPTM) exist to protect.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
vm_map_enter_mem_object_helper

The second maps it with an initial size of ~0ULL, which overflows the page rounding to zero, so the recovery path in vm_map_enter_mem_object_helper() takes the size from the named entry instead, size = named_entry->size - offset, giving…

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
vm_map_entry

The kernel describes a process’s address space as a list of vm_map_entry structures, each a start address, an end address, and the object backing that range.

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
vm_object

vm_map, vm_object, pmap (Mach owns it)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
vuln

Walking the chain as the CPU sees it: vuln returns into pop rax ; ret, which loads 15 and returns into the syscall gadget.

Sigreturn-oriented programming → A worked example

K

__kalloc_type

Recovering the full type-to-zone map means reading the __kalloc_type section out of a kernelcache.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
__kernel_sigreturn

The kernel’s signal trampoline lives in the vDSO, a small shared object the kernel maps into every process, exported as __kernel_sigreturn.

Sigreturn-oriented programming → Finding the pieces in practice
kalloc

Every 64-byte kalloc() came from one kalloc.64 zone, so a freed 64-byte victim could be replaced by any 64-byte object you knew how to allocate on demand.

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
kalloc.type0.16

kalloc.type0.16 and kalloc.type2.16 hold 16-byte elements exactly like kalloc.type6.16, they are busy zones with thousands of live objects, and across the spray they moved by three and by twenty-two, downward in both cases, which is ordi…

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
kalloc.type0.96

kalloc.type0.96 went from 76 to 4076.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
kalloc.type2.16

kalloc.type0.16 and kalloc.type2.16 hold 16-byte elements exactly like kalloc.type6.16, they are busy zones with thousands of live objects, and across the spray they moved by three and by twenty-two, downward in both cases, which is ordi…

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
kalloc.type3.48

which is where a name like kalloc.type3.48 comes from: bucket 3 of the 48-byte size class.

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
kalloc.type6.16

kalloc.type6.16 went from 0 to 4000, and the zone had to grow from 16K to 64K to take them.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
kalloc_type

That is the technique kalloc_type was built to end.

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
kauth_cred_t

identity (kauth_cred_t: uid, Mandatory Access Control label)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
kern.exclaves_relaxed_requirements

kern.exclaves_relaxed_requirements sits inside the guard and is missing from the same output, which points at a kernel built without CONFIG_EXCLAVES.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
KERN_INVALID_NAME

ipc_entry_lookup() returns IE_NULL on a generation mismatch and the caller turns that into KERN_INVALID_NAME, rather than silently aliasing the new port.

Apple internals #2: XNU under the hood → ipc_space: a task’s table of capabilities
KERN_SUCCESS

Return KERN_SUCCESS and MIG assumes the routine consumed every right and every out-of-line region, so it frees the buffer and nothing else.

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
kernel_pmap

Since iOS 14 the kernel’s own task will not do: convert_port_to_map_with_flavor panics when the resolved map’s pmap is kernel_pmap, which is why a modern chain forges a fake task over a fake vm_map instead.

Apple internals #2: XNU under the hood → Ports and port rights
kernel_task

tfp0 is read “task-for-pid-zero.” task_for_pid(pid) is a Mach trap that returns a send right to that process’s task control port, and pid 0 is kernproc, the kernel’s own process, whose task is kernel_task and whose address space is kerne…

Apple internals #2: XNU under the hood → tfp0: the objective
kernelmanagerd

smd, endpointsecurityd and kernelmanagerd import xpc_dictionary_get_audit_token, the per-message call.

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
kernproc

tfp0 is read “task-for-pid-zero.” task_for_pid(pid) is a Mach trap that returns a send right to that process’s task control port, and pid 0 is kernproc, the kernel’s own process, whose task is kernel_task and whose address space is kerne…

Apple internals #2: XNU under the hood → tfp0: the objective
kfd

The exploit is public in the kfd repository, and the code below is XNU’s own, quoted from the writeup.

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
kIOReturnBadArgument

Calling selector 0 on either returns 0xe00002c2, kIOReturnBadArgument: the driver compared the single scalar we sent against what that selector declares and refused before the handler ran.

Apple internals #5: IOKit up close → The surface, and how to see it
kIOReturnNotPermitted

A caller without the right entitlement is routed to the restricted table and gets a stub returning kIOReturnNotPermitted for the sensitive selectors; IOSurfaceRootUserClient and the GPU clients work this way.

Apple internals #5: IOKit up close → The surface in 2026
kIOReturnSuccess

IOSurfaceRoot and AppleAPFSContainer both opened, 0x00000000 being kIOReturnSuccess, so an ordinary unprivileged process is now holding a live connection to a kernel driver.

Apple internals #5: IOKit up close → The surface, and how to see it
kIOReturnUnsupported

AGXAccelerator never opened at all: 0xe00002c7 is kIOReturnUnsupported, so the GPU service is in the registry but will not vend us this kind of client.

Apple internals #5: IOKit up close → The surface, and how to see it
kIOUCVariableStructureSize

kIOUCVariableStructureSize, 0xFFFFFFFF, in any of the four check fields means “variable, do not check”: the caller may send any count or size, and the handler has to validate it itself.

Apple internals #5: IOKit up close → Where the call lands: the dispatch table
kIOUserClientEntitlementsKey

The sandbox and entitlement checks happen here, in the framework rather than in the driver: a MACF hook, mac_iokit_check_open_service, is where Sandbox.kext enforces your profile’s iokit-open rules, and the framework itself tests any ent…

Apple internals #5: IOKit up close → The userland-to-kernel bridge
kmalloc

We look the process up by PID, allocate our structure with kmalloc, fill it from the task’s task_struct, walk the children list, resolve the root and working-directory paths under the appropriate locks, and copy the whole thing back to t…

Two ways into ring 0: system calls and kernel modules → Writing the call
kmem_shuffle

The distribution is shuffled with kmem_shuffle() and the hash seeded from early_random(), so a type’s zone is fixed for one boot and different on the next.

Apple internals #7: The zone allocator up close → kalloc_type, or why the same size is not enough
kobject

kref_get() / kref_put() on a kobject

Apple internals #5: IOKit up close → What IOKit is
kqueue_workloop_ctl

This is the ordinary spray, and the object list should look familiar by now: psemnode from sem_open(), fileproc from dup(), kqworkloop from kqueue_workloop_ctl().

Apple internals #7: The zone allocator up close → From a PUAF to read and write
kqworkloop

This is the ordinary spray, and the object list should look familiar by now: psemnode from sem_open(), fileproc from dup(), kqworkloop from kqueue_workloop_ctl().

Apple internals #7: The zone allocator up close → From a PUAF to read and write
kread_sem_open

In kread_sem_open the object is a psemnode, the corrupted field is pinfo, and proc_info() returns eight bytes from wherever it points, believing them to be a semaphore’s uid and gid.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
kref_get

kref_get() / kref_put() on a kobject

Apple internals #5: IOKit up close → What IOKit is
kref_put

kref_get() / kref_put() on a kobject

Apple internals #5: IOKit up close → What IOKit is
KRKW

At the end is arbitrary read and write, KRKW in kfd’s vocabulary, for kernel read and kernel write: fetch or store bytes at any kernel address you name, repeatedly, without disturbing anything else.

Apple internals #7: The zone allocator up close → From a bug to a primitive
krnl

The payload is the IM4P: a four-character tag naming the contents (krnl for the kernelcache, ibot for iBoot, sepi for the SEP firmware), a description string such as a build version, the payload bytes, and the compression scheme if there…

Apple internals #1: The iOS chain of trust → Image4: the container everything is signed in
KT_GRANULE_PAC

The type signature kalloc_type computes, the one from the last post that decides which zone a struct is allocated from, has a granule value for it: KT_GRANULE_PAC, documented in osfmk/kern/kalloc.h as “represents a pointer which is subje…

Apple internals #8: Pointer authentication → What Apple signs
KT_PRIV_ACCT

First, zprint names a type only where the kernel asked it to: a view gets its own line, like kalloc.type7.512[site.struct coalition], when it was declared with KT_PRIV_ACCT for private accounting, and otherwise its allocations accumulate…

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
kTXMKernelSelectorEnterLockdownMode

Forty distinct selector names appear in that file, from kTXMKernelSelectorRegisterCodeSignature through kTXMKernelSelectorEnterLockdownMode.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
kTXMKernelSelectorRegisterCodeSignature

Forty distinct selector names appear in that file, from kTXMKernelSelectorRegisterCodeSignature through kTXMKernelSelectorEnterLockdownMode.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
KUSER_SHARED_DATA

It is the Apple analogue of Windows’ KUSER_SHARED_DATA.

Exploiting JavaScript engines: from type confusion to code execution → The mitigations that make a working bug only the beginning
kwrite_dup

In kwrite_dup the object is a fileproc, the field is fp_guard, and change_fdguard_np() writes eight bytes there.

Apple internals #7: The zone allocator up close → From a PUAF to read and write

L

__LINKEDIT

A signed Mach-O carries an LC_CODE_SIGNATURE load command pointing at a blob in its __LINKEDIT segment.

Apple internals #3: The iOS code-signing pipeline → What a signature is, ending at the cdhash
label_get_sandbox

nsantoine, “A Worm’s Look Inside: Apple’s Sandboxing Security Measures” (2024), a modern account of cred_sb_evaluate, label_get_sandbox, operation numbering, and the platform-profile-in-kext design.

Apple internals #4: The iOS sandbox
LC_CODE_SIGNATURE

A signed Mach-O carries an LC_CODE_SIGNATURE load command pointing at a blob in its __LINKEDIT segment.

Apple internals #3: The iOS code-signing pipeline → What a signature is, ending at the cdhash
ldraa

Those fused forms are where most of the signing actually happens: retab authenticates the link register and returns, braa and blraa authenticate and branch, ldraa authenticates and loads.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
LEN

We reject null pointers, insist on exactly LEN bytes, and then pull the data across the boundary with copy_from_user, the mirror image of the copy_to_user we used in the syscall.

Two ways into ring 0: system calls and kernel modules → The write operation
level

level: compression level, 9 being the maximum.

Recovering payloads from PE resources → Compressing the payload
libCoreEntitlements

Apple had moved entitlements to DER partly to end these differentials, since DER is meant to have exactly one canonical reading, but libCoreEntitlements had three traversals that disagreed on how far a sequence extended.

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
libkern

A libkern C++ object carries a vtable pointer at offset 0, and OSMetaClass gives it the run-time type identity that C++ without RTTI does not, with OSDynamicCast walking the chain of parent metaclasses.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
libmis

It validates the binary against the provisioning profiles installed on the device, by calling MISValidateSignatureAndCopyInfo in libmis, and returns the cdhash and signer information.

Apple internals #3: The iOS code-signing pipeline → amfid and provisioning profiles
libsandbox

On macOS, libsandbox compiles SBPL to bytecode at spawn, and a dynamic profile can even run Scheme to generate its rules from the process’s entitlements.

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
libtomcrypt

This function makes several calls into the external libtomcrypt library, notably the ChaCha20 random-string generation functions, but also calls to rand, which acts directly on the value passed earlier to its seeder, srand.

How I broke Rhysida ransomware encryption → Encrypting files
literal

filter narrows the rule to specific arguments: path filters (literal, subpath, prefix, regex), the Mach global-name / local-name (the service name being looked up), require-entitlement, iokit-user-client-class, network socket-domain / re…

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
lldb

The userland end needs nothing but lldb on your own binary:

Apple internals #5: IOKit up close → The surface, and how to see it
LoadResource

Recovery uses three Windows API calls: FindResourceA, LoadResource, and LockResource.

Recovering payloads from PE resources → Finding and extracting the resource
LockResource

Recovery uses three Windows API calls: FindResourceA, LoadResource, and LockResource.

Recovering payloads from PE resources → Finding and extracting the resource
lookUpImpOrForward

On a miss the slow path runs: lookUpImpOrForward walks the class’s method lists, then its superclass’s, resolves the method, fills the cache, and only then calls.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
lsmod

List them with lsmod:

Two ways into ring 0: system calls and kernel modules → Path 2: the kernel module
ltrs

The cache is carried in an Image4 container (an IM4P payload, the format from the boot-chain post), tagged trst for the static cache, rtsc for a restore cache, ltrs for a loadable one.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist

P

__PPLDATA

A small body of code is allowed to write them, and it lives in its own segments, __PPLTEXT and __PPLDATA, reached through a trampoline that flips the permission register on entry and flips it back on exit.

Apple internals #9: SPTM, TXM and memory tagging → PPL: page tables the kernel cannot write
__PPLTEXT

A small body of code is allowed to write them, and it lives in its own segments, __PPLTEXT and __PPLDATA, reached through a trampoline that flips the permission register on entry and flips it back on exit.

Apple internals #9: SPTM, TXM and memory tagging → PPL: page tables the kernel cannot write
_platform_profile_data

The platform profile is a single mandatory base policy compiled into the kext (_platform_profile_data) and evaluated for every process, root daemons included.

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI
p_csflags

With kernel read/write, an attacker does not need a fresh signing bug: append a cdhash to a loadable trust cache, flip CS_PLATFORM_BINARY and clear CS_HARD | CS_KILL in a process’s p_csflags (its code-signing flags word), or edit the AMF…

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
p_fd

file descriptor: int into p_fd

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
p_pid

struct proc is the process from BSD’s point of view: p_pid, the file-descriptor table p_fd, a back-pointer to the task, and p_ucred, the pointer to its credentials (since the iOS 15 and macOS 12 line, reached through the read-only proc_r…

Apple internals #2: XNU under the hood → The BSD half
p_ucred

struct proc is the process from BSD’s point of view: p_pid, the file-descriptor table p_fd, a back-pointer to the task, and p_ucred, the pointer to its credentials (since the iOS 15 and macOS 12 line, reached through the read-only proc_r…

Apple internals #2: XNU under the hood → The BSD half
PAC

ARM, the Arm Architecture Reference Manual for A-profile architecture (DDI 0487), for the PAC and AUT instruction behaviour, the two-bit error code written on a failed authentication, the FEAT_PAuth2, FEAT_FPAC and FEAT_FPACCOMBINE featu…

Apple internals #8: Pointer authentication
pacga

pacga does not touch a pointer at all: it takes a value in one register and a modifier in a second, and writes a 32-bit code into the top half of a third, for signing data that is not an address.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
paciasp

Others hardcode a register, which is how the link register is handled: pacibsp signs it against the stack pointer and autibsp authenticates it, and paciasp and autiasp are the same pair under key A.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
pacibsp

Others hardcode a register, which is how the link register is handled: pacibsp signs it against the stack pointer and autibsp authenticates it, and paciasp and autiasp are the same pair under key A.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
paciza

A trailing z means the modifier is zero: paciza, autiza, braaz, blraaz.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
parseOptions

Once the thread-spawning loop is done, the program calls an internal function named parseOptions, which parses the arguments the attacker passed to the program, used to toggle certain internal options of the encryptor or switch its opera…

How I broke Rhysida ransomware encryption → Entry point and initialization
password

The first is a length check on each of the four parameters, printerUri, realm, userName and password, applied at the very top of the block:

ActiveX controller exploitation → Reversing ienipp.ocx
performJITMemcpy

To write into a JIT page the runtime must first flip the thread’s APRR register so that slot maps back to a writable permission, do the copy, and flip it back, which is exactly the window the inlined performJITMemcpy opens and closes.

Exploiting JavaScript engines: from type confusion to code execution → The mitigations that make a working bug only the beginning
pidversion

Watch also for audit_token_to_pid, which configd, sandboxd and taskgated carry: it takes a token and collapses it back to a PID, discarding the pidversion that made the token worth having, so if that PID drives an authorization decision…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
pinfo

In kread_sem_open the object is a psemnode, the corrupted field is pinfo, and proc_info() returns eight bytes from wherever it points, believing them to be a semaphore’s uid and gid.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
pmap

vm_map, vm_object, pmap (Mach owns it)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
PMAP_CREATE_DISABLE_JOP

A task that is not arm64e runs with user PAC switched off entirely: bsd/kern/kern_exec.c flags the image IMGPF_NOJOP, and bsd/kern/mach_loader.c then creates its address space with PMAP_CREATE_DISABLE_JOP.

Apple internals #8: Pointer authentication → Five keys and a modifier
pmap_cs

And it only works where that memory is writable: on a PPL device (Page Protection Layer, the pre-A15 page-table monitor) the trust cache lives in pmap_cs pages the kernel may not write, and on an SPTM device (Secure Page Table Monitor, i…

Apple internals #3: The iOS code-signing pipeline → Aside: injecting a cdhash by hand in lldb
pmap_enter_options

In XNU’s own tree the call sits at the bottom of the ordinary mapping path, pmap_enter_options into pmap_enter_pte into sptm_map_page, in osfmk/arm64/sptm/pmap/pmap.c.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
pmap_enter_pte

In XNU’s own tree the call sits at the bottom of the ordinary mapping path, pmap_enter_options into pmap_enter_pte into sptm_map_page, in osfmk/arm64/sptm/pmap/pmap.c.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
pmap_ppl_interface.c

The attack surface that remains is the list of routines the trampoline will dispatch to, and XNU’s open source publishes it: ppl_handler_table in osfmk/arm/pmap/pmap.c for the mapping side, with the entry and exit helpers next door in pm…

Apple internals #9: SPTM, TXM and memory tagging → PPL: page tables the kernel cannot write
pmap_remove_options

vm_deallocate() over that range calls pmap_remove_options(), which reaches this, in osfmk/arm/pmap/pmap.c:

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
pop

Instead of using a mov instruction, let’s use push and pop for the same result.

Building the smallest ELF program → Smallest possible code
posix_spawn

Every time a process is created, through execve or posix_spawn, the kernel answers one question before it runs a single instruction of the new image: may these bytes execute?

Apple internals #3: The iOS code-signing pipeline
POST

So the POST to /ipp/IppSrvr has to succeed.

ActiveX controller exploitation → Emulating an IPP server
ppl_handler_table

The attack surface that remains is the list of routines the trampoline will dispatch to, and XNU’s open source publishes it: ppl_handler_table in osfmk/arm/pmap/pmap.c for the mapping side, with the entry and exit helpers next door in pm…

Apple internals #9: SPTM, TXM and memory tagging → PPL: page tables the kernel cannot write
prefix

filter narrows the rule to specific arguments: path filters (literal, subpath, prefix, regex), the Mach global-name / local-name (the service name being looked up), require-entitlement, iokit-user-client-class, network socket-domain / re…

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
printerUri

The first is a length check on each of the four parameters, printerUri, realm, userName and password, applied at the very top of the block:

ActiveX controller exploitation → Reversing ienipp.ocx
printf

We don’t want external dependencies like the printf() function.

Building the smallest ELF program → Getting straight to the point
printk

For now both functions just print to the kernel log with printk.

Two ways into ring 0: system calls and kernel modules → A minimal module
prngs

The global array fed by this function, named prngs and sized to the number of processors, holds the various initialization values for the ChaCha20 random-string generator.

How I broke Rhysida ransomware encryption → Encrypting files
proc

proc (pid, credentials, file descriptors)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
proc_calc_audit_token

One function decides what goes in them, proc_calc_audit_token() in bsd/kern/kern_prot.c:

Apple internals #6: Mach messages, MIG and XPC → Who is actually calling
proc_info

In kread_sem_open the object is a psemnode, the corrupted field is pinfo, and proc_info() returns eight bytes from wherever it points, believing them to be a semaphore’s uid and gid.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
proc_ro

struct proc is the process from BSD’s point of view: p_pid, the file-descriptor table p_fd, a back-pointer to the task, and p_ucred, the pointer to its credentials (since the iOS 15 and macOS 12 line, reached through the read-only proc_r…

Apple internals #2: XNU under the hood → The BSD half
processFileEnc

It walks the array of files to encrypt, extracts the file path’s name, checks whether the file is actually a legitimate target with isFileExcluded, and encrypts it where appropriate with processFileEnc.

How I broke Rhysida ransomware encryption → Encrypting files
processFiles

Across multiple threads, one per logical processor on the system, the processFiles function is called to handle the files assigned to each thread.

How I broke Rhysida ransomware encryption → Encrypting files
PROCS

Rhysida grabs the number of logical processors on the victim’s machine and stashes it in a global, PROCS, which it uses later to parallelize the encryption.

How I broke Rhysida ransomware encryption → Entry point and initialization
pseminfo

It decrements the use count and drops the name, but the pseminfo survives as long as a descriptor refers to it, so the allocations stay alive while nothing is left behind in the global namespace.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
psemnode

psemnode is not one of the named ones.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
ptrauth_string_discriminator

The constant itself is usually derived from a name: ptrauth_string_discriminator runs SipHash-2-4 over a string and folds the result into the 16-bit range, so a field can be diversified by what it is called.

Apple internals #8: Pointer authentication → Five keys and a modifier
push

Instead of using a mov instruction, let’s use push and pop for the same result.

Building the smallest ELF program → Smallest possible code
PXN

Four bits of a page-table entry, AP[1], AP[0], UXN and PXN, stop encoding permissions directly.

Apple internals #9: SPTM, TXM and memory tagging → Changing permissions without touching the page tables
pyimg4

They run on an Apple silicon Mac with stock macOS and System Integrity Protection left on, plus the Xcode command line tools, and ipsw and pyimg4 for the firmware side.

Apple internals #0: The Apple security stack → How to read it

M

_mask

Mike Ash, Dissecting objc_msgSend on ARM64, 30 June 2017, for the same fast path one runtime generation earlier: no PAC on the isa, a _mask field of its own rather than the top bits of the cache word, and a bucket laid out { SEL, IMP }.

Apple internals #10: The Objective-C runtime and the shared cache
mac_iokit_check_open_service

The sandbox and entitlement checks happen here, in the framework rather than in the driver: a MACF hook, mac_iokit_check_open_service, is where Sandbox.kext enforces your profile’s iokit-open rules, and the framework itself tests any ent…

Apple internals #5: IOKit up close → The userland-to-kernel bridge
mac_vnode_check_signature

At exec, the kernel’s mac_vnode_check_signature entry point calls AMFI’s mpo_vnode_check_signature callback, the routine that produces the verdict.

Apple internals #3: The iOS code-signing pipeline → The verdict pipeline
mach_memory_object_memory_entry_64

The first uses a Mach Interface Generator (MIG) routine, mach_memory_object_memory_entry_64(), to create a named entry of size 2P+1; sizes are not rounded on that path.

Apple internals #7: The zone allocator up close → PhysPuppet: six steps to a dangling page-table entry
mach_msg

mach_msg() now goes through a split trap that passes the descriptor count as its own argument instead of reading it from the buffer, so impossible shapes die at the syscall boundary.

Apple internals #6: Mach messages, MIG and XPC → State in 2026
mach_msg2

mach_msg2 (iOS 16, macOS 13).

Apple internals #6: Mach messages, MIG and XPC → State in 2026
mach_msg2_trap

This invalid config can only happen from mach_msg2_trap() since desc_count is passed as its own trap argument.”

Apple internals #6: Mach messages, MIG and XPC → State in 2026
MACH_MSG_OOL_DESCRIPTOR

MACH_MSG_OOL_DESCRIPTOR

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
MACH_MSG_OOL_PORTS_DESCRIPTOR

MACH_MSG_OOL_PORTS_DESCRIPTOR

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
MACH_MSG_PORT_DESCRIPTOR

MACH_MSG_PORT_DESCRIPTOR

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
MACH_MSGH_BITS_COMPLEX

The top bit of msgh_bits, MACH_MSGH_BITS_COMPLEX (0x80000000), sorts every message into one of two kinds.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
mach_port_allocate

The ones an exploit drives constantly are the _kernelrpc_* port and memory calls (mach_port_allocate, mach_port_insert_right, mach_port_mod_refs, mach_vm_allocate) and mach_msg2, the modern consolidated message path.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
MACH_PORT_GEN

Apple’s open-source XNU is ground truth for every structure and macro named above: osfmk/ipc/ipc_port.h, ipc_object.h, ipc_entry.h, osfmk/mach/port.h (MACH_PORT_INDEX and MACH_PORT_GEN), osfmk/kern/syscall_sw.c (the trap table), osfmk/ke…

Apple internals #2: XNU under the hood
mach_port_guard

mach_port_guard lets a holder bind a context to a port so unexpected operations fault.

Apple internals #2: XNU under the hood → State in 2026
MACH_PORT_INDEX

Apple’s open-source XNU is ground truth for every structure and macro named above: osfmk/ipc/ipc_port.h, ipc_object.h, ipc_entry.h, osfmk/mach/port.h (MACH_PORT_INDEX and MACH_PORT_GEN), osfmk/kern/syscall_sw.c (the trap table), osfmk/ke…

Apple internals #2: XNU under the hood
mach_port_insert_right

The ones an exploit drives constantly are the _kernelrpc_* port and memory calls (mach_port_allocate, mach_port_insert_right, mach_port_mod_refs, mach_vm_allocate) and mach_msg2, the modern consolidated message path.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
mach_port_mod_refs

The ones an exploit drives constantly are the _kernelrpc_* port and memory calls (mach_port_allocate, mach_port_insert_right, mach_port_mod_refs, mach_vm_allocate) and mach_msg2, the modern consolidated message path.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
mach_port_name_t

You read and write a file through an int; you talk to a service, a task, or a driver through a mach_port_name_t.

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
mach_portal

Ian Beer, “task_t considered harmful” (Project Zero), is the capability-confusion case study, and the mach_portal / async_wake writeups established the port-spray playbook.

Apple internals #2: XNU under the hood
MACH_RCV_TRAILER_AUDIT

Ask with MACH_RCV_TRAILER_AUDIT and it ends in an audit_token_t, which is eight unsigned integers and no field names.

Apple internals #6: Mach messages, MIG and XPC → Who is actually calling
mach_trap_table

mach_trap_table (negative numbers)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
mach_vm_allocate

The ones an exploit drives constantly are the _kernelrpc_* port and memory calls (mach_port_allocate, mach_port_insert_right, mach_port_mod_refs, mach_vm_allocate) and mach_msg2, the modern consolidated message path.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
mach_vm_read

Hold that and you can call mach_vm_read and mach_vm_write against that memory.

Apple internals #2: XNU under the hood → Ports and port rights
mach_vm_write

Hold that and you can call mach_vm_read and mach_vm_write against that memory.

Apple internals #2: XNU under the hood → Ports and port rights
MachServices

A daemon declares its names under MachServices in its launchd plist, creates a port, keeps the receive right, and hands launchd a send right; a client that looks the name up gets a copy of that send right back.

Apple internals #6: Mach messages, MIG and XPC → XPC on top
magic

The layout most write-ups quote is a different one: 33 bits of shiftcls at bit 3, a six-bit magic field holding 0x1a, extra_rc on 19 bits, ISA_MASK at 0x0000000ffffffff8.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
main

Execution drops into the encryptor through main, which sets everything up before any of the victim’s files get touched.

How I broke Rhysida ransomware encryption → Entry point and initialization
MAP_JIT

userland MAP_JIT

Apple internals #9: SPTM, TXM and memory tagging → Changing permissions without touching the page tables
mediaserverd

The per-process profile is container for third-party apps (every App Store app gets the identical container profile; what differentiates them is Apple-signed entitlements plus the per-app parameters bound in at spawn, HOME, the bundle id…

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI
memcpy

The zero-modifier case is where that defence is absent, and Clang’s documentation is blunt about it: the implementation “uses the exact same signing schema for all C function pointers, even for functions of substantially different type”,…

Apple internals #8: Pointer authentication → Where the attacks live
methodDeltaCreateFinalize

Tommy Muir (Muirey03), CVE-2022-32832 write-up and proof-of-concept, the source for the case study: selector 49, methodDeltaCreateFinalize, the delta_create_ctx_t double-free, the root precondition, and the IOLockLock/IOLockUnlock fix in…

Apple internals #5: IOKit up close
methodDeltaCreatePrepare

It does nothing until methodDeltaCreatePrepare (selector 36) has left a context behind, which needs an unmounted volume, which normally means creating one with methodVolumeCreate (selector 0), which needs root.

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
methodVolumeCreate

It does nothing until methodDeltaCreatePrepare (selector 36) has left a context behind, which needs an unmounted volume, which normally means creating one with methodVolumeCreate (selector 0), which needs root.

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
MISValidateSignatureAndCopyInfo

It validates the binary against the provisioning profiles installed on the device, by calling MISValidateSignatureAndCopyInfo in libmis, and returns the cdhash and signer information.

Apple internals #3: The iOS code-signing pipeline → amfid and provisioning profiles
mmap

mmap, mprotect (BSD uses it)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
modifierForSEL

The IMP stored in a bucket is signed with the IB key (instruction pointers, key B) and a modifier of bucket_base ^ sel ^ cls, per modifierForSEL in objc-runtime-new.h.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
module_exit

Reading top to bottom: we include the kernel headers we need; the MODULE_* macros attach metadata (license, author, description); two functions tagged __init and __exit run when the module is loaded and unloaded; and module_init / module…

Two ways into ring 0: system calls and kernel modules → A minimal module
module_init

Reading top to bottom: we include the kernel headers we need; the MODULE_* macros attach metadata (license, author, description); two functions tagged __init and __exit run when the module is loaded and unloaded; and module_init / module…

Two ways into ring 0: system calls and kernel modules → A minimal module
mov

Instead of using a mov instruction, let’s use push and pop for the same result.

Building the smallest ELF program → Smallest possible code
movk

w0 is the selector the caller asked for and it goes into x16; movk puts 3 into bits 48 to 55 of the same register, which Apple’s sptm_common.h gives to the domain field, and domain 3 is SK_DOMAIN; eight arguments are loaded from a struct…

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
mpo_vnode_check_signature

At exec, the kernel’s mac_vnode_check_signature entry point calls AMFI’s mpo_vnode_check_signature callback, the routine that produces the verdict.

Apple internals #3: The iOS code-signing pipeline → The verdict pipeline
mprotect

mmap, mprotect (BSD uses it)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
msgh_bits

msgh_bits is the field doing the security-relevant work.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
msgh_id

msgh_remote_port is the destination, msgh_local_port the reply port, msgh_size the whole packet, msgh_voucher_port a send right to a voucher, which does no work in what follows, and msgh_id a number the receiver interprets however it likes.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
msgh_local_port

msgh_remote_port is the destination, msgh_local_port the reply port, msgh_size the whole packet, msgh_voucher_port a send right to a voucher, which does no work in what follows, and msgh_id a number the receiver interprets however it likes.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
msgh_remote_port

msgh_remote_port is the destination, msgh_local_port the reply port, msgh_size the whole packet, msgh_voucher_port a send right to a voucher, which does no work in what follows, and msgh_id a number the receiver interprets however it likes.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
msgh_size

msgh_remote_port is the destination, msgh_local_port the reply port, msgh_size the whole packet, msgh_voucher_port a send right to a voucher, which does no work in what follows, and msgh_id a number the receiver interprets however it likes.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
msgh_voucher_port

msgh_remote_port is the destination, msgh_local_port the reply port, msgh_size the whole packet, msgh_voucher_port a send right to a voucher, which does no work in what follows, and msgh_id a number the receiver interprets however it likes.

Apple internals #6: Mach messages, MIG and XPC → The message, and what it carries
my_module.ko

The one that matters is my_module.ko, the kernel object we load with insmod:

Two ways into ring 0: system calls and kernel modules → Compiling and loading

X

_xpc_connection_set_creds

libxpc requests the audit trailer on every message, and _xpc_connection_set_creds copies that token onto the connection, overwriting what was there.

Apple internals #6: Mach messages, MIG and XPC → The confused deputy
x16

Disassemble the kernel and sort every gate by what it writes into x16 before the transition:

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
XNU_DEFAULT

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_DOMAIN

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_HIB_DOMAIN

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_PAGE_TABLE

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_ROZONE

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_TAG_STORAGE

The paper’s frame-type table has the matching entry, XNU_TAG_STORAGE, owned by the SPTM domain rather than XNU’s.

Apple internals #9: SPTM, TXM and memory tagging → Memory tagging, always on
XNU_USER_EXEC

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
XNU_USER_JIT

SPTM decides whether to honour the request using a type it keeps for every managed physical frame, named after what the frame is for: XNU_DEFAULT for ordinary kernel memory, XNU_PAGE_TABLE, XNU_USER_EXEC, XNU_USER_JIT, XNU_ROZONE for the…

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
xpacd

The alternative, in the same macro in arm64-asm.h, is a single xpacd that discards the signature without checking it.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
xpaci

xpaci strips a code without checking it, for tooling that only wants the bare address.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
xpc_connection_get_audit_token

xpc_connection_get_audit_token()

Apple internals #6: Mach messages, MIG and XPC → The confused deputy
xpc_connection_get_pid

xpc_connection_get_pid()

Apple internals #6: Mach messages, MIG and XPC → The confused deputy
xpc_create_from_plist

iOS parsed the entitlements blob with three different plist parsers, OSUnserializeXML in the kernel, CFPropertyListCreateWithData in amfid, and libxpc’s xpc_create_from_plist, and Siguza found a comment construct they read differently: o…

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
xpc_dictionary_get_audit_token

xpc_dictionary_get_audit_token()

Apple internals #6: Mach messages, MIG and XPC → The confused deputy
xxd

Use xxd to see a file’s hexadecimal data: xxd smallest_elf.bin

Building the smallest ELF program → Too much empty space
xzone

Typed allocators, met earlier in the series as kalloc_type in iOS 15, with a userland counterpart, xzone malloc, since iOS 17.

Apple internals #9: SPTM, TXM and memory tagging → Memory tagging, always on

A

a864282fea5a536510ae86c77ce46f7827687783628e4f2ceb5bf2c41b8cd3c6

SHA-256: a864282fea5a536510ae86c77ce46f7827687783628e4f2ceb5bf2c41b8cd3c6

How I broke Rhysida ransomware encryption → TL;DR
AAAA

And attaching a debugger requires a running process, so each round starts by loading a harmless payload, AAAA in every field, attaching x64dbg to Internet Explorer, and only then opening the real page.

ActiveX controller exploitation → Exploitation
AAAABBBBCCCCDDDD

When the offset is not obvious, removing characters until the crash changes is one way to find it, and a cyclic pattern such as AAAABBBBCCCCDDDD is the faster one.

ActiveX controller exploitation → Controlling EIP
addFileToQueue

It’s just a file-traversal function: it works through a queue holding the folders to visit one after another, while regular files are pulled out of the traversal and added to a global array named QUERY_FILE_POSS by the addFileToQueue fun…

How I broke Rhysida ransomware encryption → Walking the file system
addrof

addrof and fakeobj are not the goal, they are the tools for building the primitive we actually want: reading and writing any 64-bit word in the address space.

Exploiting JavaScript engines: from type confusion to code execution → From fakeobj to arbitrary read and write
alloc

Reversing an IOKit user client means walking exactly that: the metaclass, its vtable, its alloc, the class it produces.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
allow

A profile begins with (deny default), and every capability the process has is an explicit allow written against it.

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI
always_inline

Second, the JIT memcpy is marked always_inline, so there is no tidy function pointer to jump to.

Exploiting JavaScript engines: from type confusion to code execution → The mitigations that make a working bug only the beginning
amfi_get_out_of_my_way

The sanctioned form of exactly that switch is the boot argument amfi_get_out_of_my_way.

Apple internals #3: The iOS code-signing pipeline → MACF: the framework AMFI plugs into
amfid

For years the real signature validation happened in userland, in the amfid daemon we meet next.

Apple internals #3: The iOS code-signing pipeline → CoreTrust: the check that moved into the kernel
amfid_payload.dylib

Electra’s inject_trusts is the canonical example, adding the cdhashes of amfid_payload.dylib and the rest of the jailbreak’s userland.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
AMFIUnserializeXML

The fix added AMFIUnserializeXML to both AMFI and amfid and rejects the blob when its reading disagrees with the old parsers.

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
AppleAPFSContainer

IOSurfaceRoot and AppleAPFSContainer both opened, 0x00000000 being kIOReturnSuccess, so an ordinary unprivileged process is now holding a live connection to a kernel driver.

Apple internals #5: IOKit up close → The surface, and how to see it
AppleAPFSUserClient

Take iOS 15.0 for the iPhone X, load it in Ghidra, and follow the chain: the string AppleAPFSUserClient leads to the OSMetaClass constructor that registers the class, the metaclass leads through getMetaClass to the class vtable, and the…

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
AppleMobileFileIntegrity.kext

AppleMobileFileIntegrity.kext, a kernel extension (kext), is a set of mpo_* callbacks hung on MACF hook points.

Apple internals #3: The iOS code-signing pipeline → MACF: the framework AMFI plugs into
arm64

The plain arm64 build first:

Apple internals #10: The Objective-C runtime and the shared cache → Hands-on: decoding an isa by hand
arm64e

Now the arm64e build of the same source:

Apple internals #10: The Objective-C runtime and the shared cache → Hands-on: decoding an isa by hand
associate_jit_region

ppl.c and txm.c both provide register_code_signature, verify_code_signature, associate_jit_region, toggle_developer_mode and enter_lockdown_mode: the same operations, with the enforcement in a different place on each generation of hardware.

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
async_wake

Ian Beer, “task_t considered harmful” (Project Zero), is the capability-confusion case study, and the mach_portal / async_wake writeups established the port-spray playbook.

Apple internals #2: XNU under the hood
audit_token_t

Ask with MACH_RCV_TRAILER_AUDIT and it ends in an audit_token_t, which is eight unsigned integers and no field names.

Apple internals #6: Mach messages, MIG and XPC → Who is actually calling
audit_token_to_pid

Watch also for audit_token_to_pid, which configd, sandboxd and taskgated carry: it takes a token and collapses it back to a PID, discarding the pidversion that made the token worth having, so if that PID drives an authorization decision…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
AUT

FEAT_FPAC moves the fault to the AUT instruction itself, and FEAT_FPACCOMBINE extends that to the fused forms, the single instructions that authenticate and then branch or load.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
autda

The two masks now disagree, and the difference is 0x004f800000000000: the signature bits, sitting in the part of the 52-bit field the address does not use, and what autda checks.

Apple internals #10: The Objective-C runtime and the shared cache → Hands-on: decoding an isa by hand
autia

autia recomputes the code from the address it finds and the modifier it is given, compares it with the one stored in the pointer, and on success hands back a clean, dereferenceable address.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
autiasp

Others hardcode a register, which is how the link register is handled: pacibsp signs it against the stack pointer and autibsp authenticates it, and paciasp and autiasp are the same pair under key A.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
autibsp

Others hardcode a register, which is how the link register is handled: pacibsp signs it against the stack pointer and autibsp authenticates it, and paciasp and autiasp are the same pair under key A.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
autiza

A trailing z means the modifier is zero: paciza, autiza, braaz, blraaz.

Apple internals #8: Pointer authentication → Sign, authenticate, fail

C

cache

ldr x10, [x16, #0x10] reads the word at offset 0x10 of the class, which is cache, the third field of objc_class after isa and superclass.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
CFPropertyListCreateWithData

iOS parsed the entitlements blob with three different plist parsers, OSUnserializeXML in the kernel, CFPropertyListCreateWithData in amfid, and libxpc’s xpc_create_from_plist, and Siguza found a comment construct they read differently: o…

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
chacha20_prng_read

Generate a 32-byte key and a 16-byte initialization vector with chacha20_prng_read.

How I broke Rhysida ransomware encryption → Encrypting files
change_fdguard_np

In kwrite_dup the object is a fileproc, the field is fp_guard, and change_fdguard_np() writes eight bytes there.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
chc_register

register_hash, chc_register then find_hash are called in succession to set up the hash function used.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
checkStructureInputSize

the declared checkStructureInputSize

Apple internals #5: IOKit up close → What IOKit is
class_addMethod

class_rw_t is allocated when the class is first realized, and it holds what the runtime adds afterwards: categories, class_addMethod, swizzling.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
class_ro_t

class_ro_t is the clean version emitted by the compiler, read-only, and for a system class it lives inside the shared cache.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
class_rw_t

class_rw_t is allocated when the class is first realized, and it holds what the runtime adds afterwards: categories, class_addMethod, swizzling.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
clear

It allocates a clear buffer for the source data and a compressed buffer for the output, with dst_size receiving the final compressed length.

Recovering payloads from PE resources → Compressing the payload
codesign

This IPSW carries three; the system volume’s is the big one, 2,407 cdhashes, each a 20-byte value like the one codesign printed for /bin/ls in step 1, sorted for the binary search.

Apple internals #3: The iOS code-signing pipeline → Hands-on: dumping the policy off a real binary
com.apple.CoreServices.coreservicesd

It is the complete first-order attack surface of a thumbnailing bug, and the escape routes are visible in it: com.apple.tccd and com.apple.tccd.system decide whether code may reach your camera, microphone, and private files; com.apple.Se…

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
com.apple.cvmsServ

It is the complete first-order attack surface of a thumbnailing bug, and the escape routes are visible in it: com.apple.tccd and com.apple.tccd.system decide whether code may reach your camera, microphone, and private files; com.apple.Se…

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
com.apple.SecurityServer

It is the complete first-order attack surface of a thumbnailing bug, and the escape routes are visible in it: com.apple.tccd and com.apple.tccd.system decide whether code may reach your camera, microphone, and private files; com.apple.Se…

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac
com.apple.WebKit.WebContent

The per-process profile is container for third-party apps (every App Store app gets the identical container profile; what differentiates them is Apple-signed entitlements plus the per-app parameters bound in at spawn, HOME, the bundle id…

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI
complzss

Two appear in practice: LZSS, which Apple wraps in a complzss header, and LZFSE, Apple’s own compressor.

Apple internals #1: The iOS chain of trust → Image4: the container everything is signed in
compress2

Here we use zlib’s compress2() at maximum level.

Recovering payloads from PE resources → Compressing the payload
compressed

It allocates a clear buffer for the source data and a compressed buffer for the output, with dst_size receiving the final compressed length.

Recovering payloads from PE resources → Compressing the payload
compressed_binary

The program below reads a file, compresses it, and writes the result to compressed_binary.

Recovering payloads from PE resources → Compressing the payload
CONFIG_EXCLAVES

That pair does not say whether the code is in the build at all: both sysctls are registered in bsd/kern/kern_sysctl.c outside every #if CONFIG_EXCLAVES guard, and the #else arm of osfmk/kern/exclaves_boot.c returns exactly those two values.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
configd

Watch also for audit_token_to_pid, which configd, sandboxd and taskgated carry: it takes a token and collapses it back to a PID, discarding the pidversion that made the token worth having, so if that PID drives an authorization decision…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
connection_is_unauthorized

The body calls connection_is_unauthorized, trimmed from Sector 7’s decompilation:

Apple internals #6: Mach messages, MIG and XPC → CVE-2023-32405, start to finish
container

The per-process profile is container for third-party apps (every App Store app gets the identical container profile; what differentiates them is Apple-signed entitlements plus the per-app parameters bound in at spawn, HOME, the bundle id…

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI
containermanagerd

containermanagerd, itself a sandboxed daemon, creates each app’s data container at /var/mobile/Containers/Data/Application/<UUID>/, where the UUID is a random per-install identifier no other app ever learns, and records ownership out of…

Apple internals #4: The iOS sandbox → Containers: every process in its own tree
convert_port_to_map_with_flavor

Since iOS 14 the kernel’s own task will not do: convert_port_to_map_with_flavor panics when the resolved map’s pmap is kernel_pmap, which is why a modern chain forges a fake task over a fake vm_map instead.

Apple internals #2: XNU under the hood → Ports and port rights
copy_from_user

We reject null pointers, insist on exactly LEN bytes, and then pull the data across the boundary with copy_from_user, the mirror image of the copy_to_user we used in the syscall.

Two ways into ring 0: system calls and kernel modules → The write operation
copy_to_user

Second, we never write to the user pointer directly: we build the structure in kernel memory and hand it across the boundary with copy_to_user, which is the only safe way for ring 0 to write into a ring 3 buffer.

Two ways into ring 0: system calls and kernel modules → Writing the call
coreaudiod

Shape checks cannot see it, and they cannot see a routine that looks an object up by an ID from the message and then uses it as the wrong class, which is CVE-2024-54529 in coreaudiod, written up by Project Zero in January 2026.

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
CoreFoundation

Ghidra and radare2 both have cache-aware loaders, which is usually the better option, because a call from Foundation into CoreFoundation stays resolvable instead of pointing outside the file.

Apple internals #10: The Objective-C runtime and the shared cache → The libraries are not on disk
CoreTrust.kext

It is an in-kernel validator (packaged as CoreTrust.kext on most builds) that parses the CMS SignedData structure, builds the X.509 certificate chain (X.509 is the standard certificate format), verifies every signature in it, and confirm…

Apple internals #3: The iOS code-signing pipeline → CoreTrust: the check that moved into the kernel
cr_label

Each policy keeps its state in label slots on the objects the kernel tracks, and the one that matters here is cr_label, on a process’s credentials, the field we met at the end of the last post.

Apple internals #3: The iOS code-signing pipeline → MACF: the framework AMFI plugs into
cr_uid

Credentials are a kauth_cred_t, holding the familiar cr_uid and group set plus a field that matters more on iOS than the uid does: cr_label.

Apple internals #2: XNU under the hood → The BSD half
cred_sb_evaluate

nsantoine, “A Worm’s Look Inside: Apple’s Sandboxing Security Measures” (2024), a modern account of cred_sb_evaluate, label_get_sandbox, operation numbering, and the platform-profile-in-kext design.

Apple internals #4: The iOS sandbox
CS_CDHASH_LEN

The cdhash is the hash of the CodeDirectory blob itself, truncated to CS_CDHASH_LEN, 20 bytes, whatever the underlying algorithm.

Apple internals #3: The iOS code-signing pipeline → What a signature is, ending at the cdhash
CS_PLATFORM_BINARY

With kernel read/write, an attacker does not need a fresh signing bug: append a cdhash to a loadable trust cache, flip CS_PLATFORM_BINARY and clear CS_HARD | CS_KILL in a process’s p_csflags (its code-signing flags word), or edit the AMF…

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
cs_validate_page

When a page is first faulted in, the virtual-memory system hashes it and compares it to the stored slot (cs_validate_page).

Apple internals #3: The iOS code-signing pipeline → What a signature is, ending at the cdhash
ctr_encrypt

Encrypt the file block by block, each block going through a call to ctr_encrypt using the generated key and IV, which differ for every file.

How I broke Rhysida ransomware encryption → Encrypting files
ctr_setiv

Set the IV for the CTR algorithm with ctr_setiv.

How I broke Rhysida ransomware encryption → Encrypting files
ctr_start

Then initialize the cipher with that key and IV using ctr_start.

How I broke Rhysida ransomware encryption → Encrypting files
CURLE_COULDNT_RESOLVE_HOST

curl exits 6, CURLE_COULDNT_RESOLVE_HOST: the DNS query never left the sandbox.

Apple internals #4: The iOS sandbox → Hands-on: reading a profile off a stock Mac

F

fake_MISValidateSignatureAndCopyInfo

LiberiOS pointed amfid’s import of the validation function at a bad address and caught the resulting fault; Electra rebound it to a fake_MISValidateSignatureAndCopyInfo that simply returned success.

Apple internals #3: The iOS code-signing pipeline → CoreTrust: the check that moved into the kernel
fakeobj

addrof and fakeobj are not the goal, they are the tools for building the primitive we actually want: reading and writing any 64-bit word in the address space.

Exploiting JavaScript engines: from type confusion to code execution → From fakeobj to arbitrary read and write
fbt

The kernel end of this is is_io_connect_method (the is_ prefix marks the MIG server routine), and tracing it needs a DTrace fbt probe, which System Integrity Protection blocks on a stock Mac.

Apple internals #5: IOKit up close → The surface, and how to see it
FEAT_FPAC

FEAT_FPAC moves the fault to the AUT instruction itself, and FEAT_FPACCOMBINE extends that to the fused forms, the single instructions that authenticate and then branch or load.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
FEAT_FPACCOMBINE

FEAT_FPAC moves the fault to the AUT instruction itself, and FEAT_FPACCOMBINE extends that to the fused forms, the single instructions that authenticate and then branch or load.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
FEAT_PAuth2

FEAT_PAuth2 is the ARMv8.6 revision of the base feature and FEAT_FPAC is built on top of it, so a standalone autia that fails traps on the spot here, which the hands-on below runs into.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
file_operations

The fops field points at a file_operations structure, which is the heart of the interface: it tells the kernel which function to call for each operation on the device.

Two ways into ring 0: system calls and kernel modules → Registering the device
fileproc

fileproc, which has its own dedicated zone rather than a shared one, went up by just over 4000 as each semaphore took a descriptor.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
find_cipher

register_cipher then find_cipher are called in succession to set up the AES cipher mode.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
find_hash

register_hash, chc_register then find_hash are called in succession to set up the hash function used.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
FindResourceA

Recovery uses three Windows API calls: FindResourceA, LoadResource, and LockResource.

Recovering payloads from PE resources → Finding and extracting the resource
flex

You will also need flex, bison, libelf-dev, and libssl-dev to compile the kernel later.

Two ways into ring 0: system calls and kernel modules → Preparing the environment
fops

The fops field points at a file_operations structure, which is the heart of the interface: it tells the kernel which function to call for each operation on the device.

Two ways into ring 0: system calls and kernel modules → Registering the device
for

Likewise, here’s the C pseudocode for the start of main analyzed earlier, the srand call seeding the PRNG, GetSystemInfo for the processor count, the for loop that spins up the threads, and the parseOptions call:

How I broke Rhysida ransomware encryption → Entry point and initialization
fp_guard

In kwrite_dup the object is a fileproc, the field is fp_guard, and change_fdguard_np() writes eight bytes there.

Apple internals #7: The zone allocator up close → From a PUAF to read and write
futurerestore

futurerestore does both, taking a saved blob plus a forced generator.

Apple internals #1: The iOS chain of trust → Personalization and the signing window

G

gcc

We assemble the code with nasm then link the object with gcc then run it.

Building the smallest ELF program → Get rid of programming language
GENTER

On the A14 and the base M1 the flip moved into hardware: the trampoline enters GL2 with GENTER, the instruction the next section takes apart, which is why the M1 kernel in the hands-on below carries six GENTER sites.

Apple internals #9: SPTM, TXM and memory tagging → PPL: page tables the kernel cannot write
genter

w0 is the selector the caller asked for and it goes into x16; movk puts 3 into bits 48 to 55 of the same register, which Apple’s sptm_common.h gives to the domain field, and domain 3 is SK_DOMAIN; eight arguments are loaded from a struct…

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
GetDriverSettings

GetDriverSettings is there, the name the CVE gives us, along with a GetDriverSettings2 variant.

ActiveX controller exploitation → Finding the controller
GetDriverSettings2

GetDriverSettings is there, the name the CVE gives us, along with a GetDriverSettings2 variant.

ActiveX controller exploitation → Finding the controller
getMetaClass

Take iOS 15.0 for the iPhone X, load it in Ghidra, and follow the chain: the string AppleAPFSUserClient leads to the OSMetaClass constructor that registers the class, the metaclass leads through getMetaClass to the class vtable, and the…

Apple internals #5: IOKit up close → One bug, start to finish: CVE-2022-32832
GetSystemInfo

A call to GetSystemInfo, then a read of sysinfo.dwNumberOfProcessors.

How I broke Rhysida ransomware encryption → Entry point and initialization
GEXIT

They are entered with an Apple-proprietary instruction, GENTER, opcode 0x00201420, and left with GEXIT, opcode 0x00201400.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM

H

has_assoc

has_assoc

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
has_cxx_dtor

The magic field is gone from the ptrauth layout, and so is has_cxx_dtor.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
has_sidetable_rc

has_sidetable_rc

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
hello_cleanup

And we deregister the device when the module unloads, in hello_cleanup:

Two ways into ring 0: system calls and kernel modules → Registering the device
hello_init

In hello_init we fill a few fields and register it.

Two ways into ring 0: system calls and kernel modules → Registering the device
HOME

The per-process profile is container for third-party apps (every App Store app gets the identical container profile; what differentiates them is Apple-signed entitlements plus the per-app parameters bound in at spawn, HOME, the bundle id…

Apple internals #4: The iOS sandbox → The sandbox is built like AMFI

J

jop_pid

XNU takes a task’s jop_pid, the diversifier behind the A keys, from its shared region, so every process on the same shared cache gets the same value, while rop_pid, behind the B keys, is drawn per task from early_random().

Apple internals #8: Pointer authentication → Five keys and a modifier

N

nasm

We assemble the code with nasm then link the object with gcc then run it.

Building the smallest ELF program → Get rid of programming language
new

When you write [Sig new], the receiver is the class object Sig, and the runtime looks new up the way it looks up any message, in the receiver’s own class, which is the metaclass.

Apple internals #10: The Objective-C runtime and the shared cache → An object is an isa
new_pte

ttep is the physical address of the root translation table of the address space being modified, va the virtual address, new_pte the entry XNU computed.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
nipplib.5C0450B3

The second, nipplib.5C0450B3, is a large set of tests on the answer.

ActiveX controller exploitation → Emulating an IPP server
nonpointer

nonpointer

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
NSKeyedArchiver

NSXPCConnection goes one level higher, turning messages into ObjC method calls whose arguments are NSKeyedArchiver-serialized and decoded against a per-argument list of allowed classes.

Apple internals #6: Mach messages, MIG and XPC → XPC on top
NSKeyedUnarchiver

NSKeyedUnarchiver and NSSecureCoding turn attacker-supplied bytes into an object graph, choosing classes by name and filling ivars, which is why they keep appearing in iMessage and XPC chains.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
NSSecureCoding

NSKeyedUnarchiver and NSSecureCoding turn attacker-supplied bytes into an object graph, choosing classes by name and filling ivars, which is why they keep appearing in iMessage and XPC chains.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa

O

objc4

This is the layout from objc4, runtime/isa.h, for a build with pointer authentication (PAC):

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
objc_class

ldr x10, [x16, #0x10] reads the word at offset 0x10 of the class, which is cache, the third field of objc_class after isa and superclass.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
objc_debug_taggedpointer_obfuscator

Since iOS 12 the whole value is also XORed with objc_debug_taggedpointer_obfuscator, a random word drawn at launch, so that a write primitive cannot forge a chosen tagged value blind.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
objc_direct

A send to super takes objc_msgSendSuper2 instead, and a method declared objc_direct is called outright with no dispatch at all, but those are the exceptions.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
objc_msgSend

Almost every message send goes through objc_msgSend, with a fixed register contract: x0 is the receiver, x1 is the selector, the remaining arguments follow in x2 to x7.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
objc_msgSendSuper2

A send to super takes objc_msgSendSuper2 instead, and a method declared objc_direct is called outright with no dispatch at all, but those are the exceptions.

Apple internals #10: The Objective-C runtime and the shared cache → objc_msgSend, or how a selector becomes a call
objcopy

Use objcopy to remove a specific section from an ELF executable: objcopy --remove-section .data smallest_elf.bin

Building the smallest ELF program → Remove unnecessary sections
objdump

Use objdump to see the ELF’s executable section’s instructions: objdump -d smallest_elf.bin

Building the smallest ELF program → Too much code
open

It reads the source with open() and read(), then calls zlib’s compress2(), whose prototype is:

Recovering payloads from PE resources → Compressing the payload
openDirectoryNR

The program then moves on to walking the system’s files, through a parent function openDirectoryNR that takes the path of the directory to recurse into.

How I broke Rhysida ransomware encryption → Walking the file system
os_refcnt

Reference counts moved to the hardened os_refcnt framework, which panics on overflow and on over-release rather than wrapping, closing the overflow-to-use-after-free class.

Apple internals #2: XNU under the hood → State in 2026
OSDynamicCast

A libkern C++ object carries a vtable pointer at offset 0, and OSMetaClass gives it the run-time type identity that C++ without RTTI does not, with OSDynamicCast walking the chain of parent metaclasses.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
OSMetaClass

A libkern C++ object carries a vtable pointer at offset 0, and OSMetaClass gives it the run-time type identity that C++ without RTTI does not, with OSDynamicCast walking the chain of parent metaclasses.

Apple internals #10: The Objective-C runtime and the shared cache → What an attacker does with an isa
OSUnserializeXML

iOS parsed the entitlements blob with three different plist parsers, OSUnserializeXML in the kernel, CFPropertyListCreateWithData in amfid, and libxpc’s xpc_create_from_plist, and Siguza found a comment construct they read differently: o…

Apple internals #3: The iOS code-signing pipeline → The offensive angle: logic beats corruption
otool

w0 is the selector the caller asked for and it goes into x16; movk puts 3 into bits 48 to 55 of the same register, which Apple’s sptm_common.h gives to the domain field, and domain 3 is SK_DOMAIN; eight arguments are loaded from a struct…

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac

Q

QUERY_FILE_POSS

It’s just a file-traversal function: it works through a queue holding the folders to visit one after another, while regular files are pulled out of the traversal and added to a global array named QUERY_FILE_POSS by the addFileToQueue fun…

How I broke Rhysida ransomware encryption → Walking the file system

R

rand

This function makes several calls into the external libtomcrypt library, notably the ChaCha20 random-string generation functions, but also calls to rand, which acts directly on the value passed earlier to its seeder, srand.

How I broke Rhysida ransomware encryption → Encrypting files
rax

A forged frame gives us, in one step, what a long ROP chain gives us gadget by gadget: arbitrary values in rax, rdi, rsi, rdx, the rest of the general-purpose registers, and rip.

Sigreturn-oriented programming → The abuse
RCDATA

RCDATA: the resource type (raw data).

Recovering payloads from PE resources → Embedding it as a resource
rdi

A forged frame gives us, in one step, what a long ROP chain gives us gadget by gadget: arbitrary values in rax, rdi, rsi, rdx, the rest of the general-purpose registers, and rip.

Sigreturn-oriented programming → The abuse
rdx

A forged frame gives us, in one step, what a long ROP chain gives us gadget by gadget: arbitrary values in rax, rdi, rsi, rdx, the rest of the general-purpose registers, and rip.

Sigreturn-oriented programming → The abuse
read64

With read64 and write64 the engine bug is, in effect, fully cashed out.

Exploiting JavaScript engines: from type confusion to code execution → From fakeobj to arbitrary read and write
readelf

Use readelf to see the ELF’s sections: readelf -S smallest_elf.bin

Building the smallest ELF program → Too many sections
readpe

readpe (pev) to inspect PE sections.

Recovering payloads from PE resources → Prerequisites
realm

The first is a length check on each of the four parameters, printerUri, realm, userName and password, applied at the very top of the block:

ActiveX controller exploitation → Reversing ienipp.ocx
recount_enter_secure

XNU’s open source brackets its monitor entries the same way, with recount_enter_secure() before and recount_leave_secure() after, so that time spent inside the monitor is accounted separately from time spent in the kernel.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
recount_leave_secure

XNU’s open source brackets its monitor entries the same way, with recount_enter_secure() before and recount_leave_secure() after, so that time spent inside the monitor is accounted separately from time spent in the kernel.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
regex

filter narrows the rule to specific arguments: path filters (literal, subpath, prefix, regex), the Mach global-name / local-name (the service name being looked up), require-entitlement, iokit-user-client-class, network socket-domain / re…

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
register_cipher

register_cipher then find_cipher are called in succession to set up the AES cipher mode.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
register_code_signature

ppl.c and txm.c both provide register_code_signature, verify_code_signature, associate_jit_region, toggle_developer_mode and enter_lockdown_mode: the same operations, with the enforcement in a different place on each generation of hardware.

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
register_hash

register_hash, chc_register then find_hash are called in succession to set up the hash function used.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
release

retain and release are the two most frequent operations in an Objective-C process, and the fast path for both is now an atomic add on bits 56 and up of a word the object already had.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
remote

filter narrows the rule to specific arguments: path filters (literal, subpath, prefix, regex), the Mach global-name / local-name (the service name being looked up), require-entitlement, iokit-user-client-class, network socket-domain / re…

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
report

modifier tweaks the outcome: report, a specific errno to return on deny, send-signal to kill on violation, and no-sandbox, which lets a child run unconfined.

Apple internals #4: The iOS sandbox → SBPL, and the profile the kernel actually reads
ReportCrash

That last case is what Brandon Azad’s blanket (CVE-2018-4280) did on iOS: a Mach-service bug chained through reachable services to ReportCrash, which was unsandboxed, ran as root, and held task_for_pid-allow, so the confused deputy hande…

Apple internals #4: The iOS sandbox → What the profile tells an attacker
ret

A ROP chain is a stack full of addresses: overwrite the saved return address of the function you are in, and every ret afterwards pops the next entry of your list into the program counter, so the program runs your gadgets in your order.

Apple internals #8: Pointer authentication → Five keys and a modifier
retaa

Return addresses matter enough that IB is, in the documentation’s words, “almost entirely reserved for this purpose”, which is why the disassembly below shows pacibsp and retab and not paciasp and retaa.

Apple internals #8: Pointer authentication → Five keys and a modifier
retab

Those fused forms are where most of the signing actually happens: retab authenticates the link register and returns, braa and blraa authenticate and branch, ldraa authenticates and loads.

Apple internals #8: Pointer authentication → Sign, authenticate, fail
retain

The point of packing all this into one word is the retain count.

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
retype

Moritz Steffin and Jiska Classen, Modern iOS Security Features: A Deep Dive into SPTM, TXM, and Exclaves, October 2025, for the guarded-level architecture, the GENTER dispatch from XNU, the full frame-type and domain tables, the validati…

Apple internals #9: SPTM, TXM and memory tagging
rip

Then it points rip at the handler and lets it run.

Sigreturn-oriented programming → How a signal leaves the kernel
rmmod

Unload it with rmmod and watch the cleanup function fire:

Two ways into ring 0: system calls and kernel modules → Compiling and loading
rootRetainCount

Apple, objc4 source: runtime/isa.h for the two arm64 bitfield layouts and the ISA_MASK / ISA_MASK_NOSIG constants, runtime/objc-config.h for ISA_SIGNING_DISCRIMINATOR (0x6AE1) and the ISA_SIGNING_STRIP / ISA_SIGNING_AUTH modes, runtime/a…

Apple internals #10: The Objective-C runtime and the shared cache
rop_pid

XNU takes a task’s jop_pid, the diversifier behind the A keys, from its shared region, so every process on the same shared cache gets the same value, while rop_pid, behind the B keys, is drawn per task from early_random().

Apple internals #8: Pointer authentication → Five keys and a modifier
rsa_encrypt_key_ex

The rsa_encrypt_key_ex function handles this encryption of the secrets, and the resulting encrypted values are written to the end of the victim’s file.

How I broke Rhysida ransomware encryption → Encrypting files
rsa_import

rsa_import imports, among other things, a public RSA key referenced earlier in the code, along with its size.

How I broke Rhysida ransomware encryption → Initializing the encryptor’s cryptographic parameters
rsi

A forged frame gives us, in one step, what a long ROP chain gives us gadget by gadget: arbitrary values in rax, rdi, rsi, rdx, the rest of the general-purpose registers, and rip.

Sigreturn-oriented programming → The abuse
rsp

The kernel already ships a routine whose entire job is to load every general-purpose register, plus rip and rsp, from values sitting on the stack.

Sigreturn-oriented programming
rt_sigframe

On x86-64 the relevant call is rt_sigreturn, syscall number 15, and the frame is an rt_sigframe wrapping a ucontext.

Sigreturn-oriented programming → How a signal leaves the kernel
rt_sigreturn

On x86-64 the relevant call is rt_sigreturn, syscall number 15, and the frame is an rt_sigframe wrapping a ucontext.

Sigreturn-oriented programming → How a signal leaves the kernel
rtsc

The cache is carried in an Image4 container (an IM4P payload, the format from the boot-chain post), tagged trst for the static cache, rtsc for a restore cache, ltrs for a loadable one.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
rwx

A page’s rwx bits index into a per-thread APRR register that re-maps them to the effective permissions actually applied, and that mapping is what bakes in W^X: any entry that asks for write and execute comes back without the write bit.

Exploiting JavaScript engines: from type confusion to code execution → The mitigations that make a working bug only the beginning

T

t6000

Note t6000, which is the M1 Pro, in a set of monitors that Apple’s compatibility table gives to the A15 and later and the M2 and later.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
t6020

One SPTM per SoC, and t6020 is this machine’s.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
t8103

t8103 is the M1, t6000 the M1 Pro, t6020 the M2 Pro, t8142 the M5.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
t8142

t8103 is the M1, t6000 the M1 Pro, t6020 the M2 Pro, t8142 the M5.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
task

task (address space, ports, threads)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
task_struct

We look the process up by PID, allocate our structure with kmalloc, fill it from the task’s task_struct, walk the children list, resolve the root and working-directory paths under the appropriate locks, and copy the whole thing back to t…

Two ways into ring 0: system calls and kernel modules → Writing the call
task_swap_mach_voucher

voucher_swap, named in XNU under the hood, is the canonical instance, and its own violation is subtler: task_swap_mach_voucher() breaks the rules for an inout argument, leaking one voucher reference and consuming another it had only borr…

Apple internals #6: Mach messages, MIG and XPC → MIG, the stub generator
taskgated

Watch also for audit_token_to_pid, which configd, sandboxd and taskgated carry: it takes a token and collapses it back to a PID, discarding the pidversion that made the token worth having, so if that PID drives an authorization decision…

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
test

For example, -d C:\Users\test\Downloads tells the program to encrypt only the Downloads folder of the user test.

How I broke Rhysida ransomware encryption → Walking the file system
testingipp

Here I passed testingipp as a test case.

ActiveX controller exploitation → Making IppCreateServerRef fail
tfp0

tfp0, from task_for_pid(0), a send right to the kernel’s own task port, gave the kernel’s address space through the same Mach calls that work on any other process.

Apple internals #7: The zone allocator up close → From a bug to a primitive
thread

thread (the schedulable entity)

Apple internals #2: XNU under the hood → The Mach and BSD hybrid
toggle_developer_mode

ppl.c and txm.c both provide register_code_signature, verify_code_signature, associate_jit_region, toggle_developer_mode and enter_lockdown_mode: the same operations, with the enforcement in a different place on each generation of hardware.

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
trst

The cache is carried in an Image4 container (an IM4P payload, the format from the boot-chain post), tagged trst for the static cache, rtsc for a restore cache, ltrs for a loadable one.

Apple internals #3: The iOS code-signing pipeline → Trust caches: the allowlist
trustd

trustd and secd import xpc_connection_get_audit_token, which is not a bug by itself since it is correct inside an event handler; telling those two cases apart is the part that needs the disassembler.

Apple internals #6: Mach messages, MIG and XPC → Hands-on: sorting daemons by how they check you
trxm

TXM’s header reads the same way with the code trxm, 168 KB compressed to 475 KB.

Apple internals #9: SPTM, TXM and memory tagging → Hands-on: finding the monitors on your own Mac
tsschecker

While Apple still signs a build, a tool such as tsschecker can request and store its personalized ticket, and once the signing window closes that saved ticket is the only way back to that build.

Apple internals #1: The iOS chain of trust → Personalization and the signing window
ttep

ttep is the physical address of the root translation table of the address space being modified, va the virtual address, new_pte the entry XNU computed.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
TXM_DOMAIN

Steffin and Classen read them out of Apple’s own sptm_common.h, which ships in the macOS SDK: SPTM_DOMAIN, XNU_DOMAIN, TXM_DOMAIN, SK_DOMAIN, and XNU_HIB_DOMAIN for hibernation.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM
txm_enter

TXM, through txm_enter

Apple internals #9: SPTM, TXM and memory tagging → What EL1 no longer owns
txm_kernel_call

Moritz Steffin and Jiska Classen, “Modern iOS Security Features: A Deep Dive into SPTM, TXM, and Exclaves” (2025), for TXM as the code-signing monitor and the txm_kernel_call path.

Apple internals #3: The iOS code-signing pipeline
txm_kernel_call_internal

That is from bsd/kern/code_signing/txm.c, in txm_kernel_call_internal.

Apple internals #9: SPTM, TXM and memory tagging → GXF, SPTM and TXM

W

weakly_referenced

weakly_referenced

Apple internals #10: The Objective-C runtime and the shared cache → The isa is not a pointer
windres

The mingw cross-compiler and windres (the gcc-mingw-w64-x86-64 package on Ubuntu 20.04).

Recovering payloads from PE resources → Prerequisites
write64

With read64 and write64 the engine bug is, in effect, fully cashed out.

Exploiting JavaScript engines: from type confusion to code execution → From fakeobj to arbitrary read and write

Z

z_pageq_va

When zone garbage collection reclaims the physical pages under a chunk, the virtual address range stays assigned to that zone (z_pageq_va, zone_submap_is_sequestered()).

Apple internals #7: The zone allocator up close → The zone allocator
zalloc

zalloc slices pages into fixed-size elements of one kind, with dedicated zones for hot types such as ipc ports.

Apple internals #2: XNU under the hood → Virtual memory, traps, zones, and messages
zalloc_ro

Since the iOS 15 and macOS 12 line, p_ucred sits in the read-only proc_ro structure and struct ucred is allocated ZC_READONLY, through zalloc_ro rather than ordinary zalloc, so patching a credential is no longer a plain kernel write: it…

Apple internals #2: XNU under the hood → State in 2026
zalloc_ro_update_field

The label is no longer reachable either: since iOS 15.2 and macOS 12.1 (xnu-8019.61.5), struct label is allocated from a read-only zone (ZC_READONLY, in security/mac_label.c) and its slots are written through zalloc_ro_update_field(), so…

Apple internals #4: The iOS sandbox → State in 2026
ZC_READONLY

The pointer to it moved out of struct proc into struct proc_ro, which is ZC_READONLY as well (bsd/sys/proc_ro.h, bsd/kern/kern_proc.c).

Apple internals #7: The zone allocator up close → What the write is for
zone_require

zone_require() and pointer authentication on data pointers are the two he rates highest, and both limit what you do after the page is yours.

Apple internals #7: The zone allocator up close → What kalloc_type does not cover
zone_submap_is_sequestered

When zone garbage collection reclaims the physical pages under a chunk, the virtual address range stays assigned to that zone (z_pageq_va, zone_submap_is_sequestered()).

Apple internals #7: The zone allocator up close → The zone allocator
zprint

macOS ships zprint, which reads the live zone map, and sudo is enough.

Apple internals #7: The zone allocator up close → Hands-on: watching a spray land
How to read this

A symbol is listed once, under the post that does the most with it. The link goes to the section that explains it, not to the top of the article. Nothing here is written by hand: if a symbol is missing, the posts never named it in prose.