iOS Exploiting

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

iOS Exploit Mitigations

1. Code Signing / Runtime Signature Verification

Introduced early (iPhone OS → iOS) यह मूलभूत सुरक्षा में से एक है: सभी executable code (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) को cryptographically sign किया जाना चाहिए एक certificate chain से जो Apple के trust में rooted हो। runtime पर, किसी binary को memory में load करने से पहले (या कुछ सीमाओं के पार jumps करने से पहले), सिस्टम उसकी signature चेक करता है। यदि कोड modify किया गया है (bit-flipped, patched) या unsigned है, तो load fail हो जाता है।

  • Thwarts: exploit chains में “classic payload drop + execute” चरण; arbitrary code injection; किसी मौजूदा binary को modify करके malicious logic डालना।
  • Mechanism detail:
  • Mach-O loader (और dynamic linker) code pages, segments, entitlements, team IDs और यह सुनिश्चित करता है कि signature file की contents को cover करती है।
  • JIT caches या dynamically generated code जैसे memory regions के लिए, Apple enforce करता है कि pages sign हों या special APIs (जैसे mprotect with code-sign checks) के द्वारा validate हों।
  • signature में entitlements और identifiers शामिल होते हैं; OS enforce करता है कि कुछ APIs या privileged capabilities के लिए specific entitlements चाहिए जो forge नहीं की जा सकतीं।
Example मान लें एक exploit किसी process में code execution प्राप्त करता है और heap में shellcode लिखकर उस पर jump करने की कोशिश करता है। iOS पर, उस page को executable flag चाहिए होगा **और** code-signature constraints को satisfy करना होगा। चूँकि shellcode Apple के certificate से sign नहीं है, jump fail हो जाता है या सिस्टम उस memory region को executable करने से इनकार कर देता है।

2. CoreTrust

Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust वह subsystem है जो binaries (system और user binaries सहित) की runtime signature validation करता है Apple की root certificate के खिलाफ बजाय cached userland trust stores पर निर्भर करने के।

  • Thwarts: binaries की post-install tampering, jailbreaking तकनीकें जो system libraries या user apps को swap या patch करने की कोशिश करती हैं; trusted binaries को malicious counterparts से बदल कर system को धोखा देना।
  • Mechanism detail:
  • Local trust database या certificate cache पर भरोसा करने के बजाय, CoreTrust सीधे Apple की root को fetch या refer करता है या intermediate certificates को secure chain में verify करता है।
  • यह सुनिश्चित करता है कि filesystem में मौजूदा binaries में किए गए modifications (जैसे) detect और reject हों।
  • यह entitlements, team IDs, code signing flags, और अन्य metadata को load time पर binary से जोड़ता है।
Example एक jailbreak कोशिश कर सकता है `SpringBoard` या `libsystem` को patched version से replace करके persistence पाने की। लेकिन जब OS का loader या CoreTrust चेक करता है, तो वह signature mismatch (या modified entitlements) notice करता है और execute करने से मना कर देता है।

3. Data Execution Prevention (DEP / NX / W^X)

Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP enforce करता है कि writable (data) pages non-executable हों, और executable pages non-writable हों। आप बस heap या stack region में shellcode लिखकर उसे execute नहीं कर सकते।

  • Thwarts: direct shellcode execution; classic buffer-overflow → injected shellcode पर jump करना।
  • Mechanism detail:
  • MMU / memory protection flags (page tables के माध्यम से) separation enforce करते हैं।
  • किसी भी कोशिश से writable page को executable बनाना system check trigger करता है (और या तो forbidden होता है या code-sign approval चाहिए).
  • कई मामलों में, pages को executable बनाना OS APIs के माध्यम से होता है जो अतिरिक्त constraints या checks लागू करते हैं।
Example एक overflow heap पर shellcode लिख देता है। attacker कोशिश करता है `mprotect(heap_addr, size, PROT_EXEC)` करके उसे executable बनाने की। लेकिन सिस्टम इनकार कर देता है या validate करता है कि नई page को code-sign constraints पास करने चाहिए (जो shellcode नहीं कर सकता)।

4. Address Space Layout Randomization (ASLR)

Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR key memory regions: libraries, heap, stack, आदि के base addresses को हर process लॉन्च पर randomize करता है। gadgets के addresses रन के बीच बदलते हैं।

  • Thwarts: ROP/JOP के लिए gadget addresses को hardcode करना; static exploit chains; known offsets पर blind jumping।
  • Mechanism detail:
  • हर loaded library / dynamic module एक randomized offset पर rebase होता है।
  • Stack और heap base pointers randomized होते हैं (कुछ entropy limits के भीतर)।
  • कभी-कभी अन्य regions (जैसे mmap allocations) भी randomized होते हैं।
  • information-leak mitigations के साथ मिलकर, यह attacker को runtime पर base addresses खोजने के लिए पहले किसी address या pointer को leak करना मजबूर करता है।
Example एक ROP chain को gadget की उम्मीद है `0x….lib + offset` पर। लेकिन क्योंकि `lib` हर रन पर अलग relocate होता है, hardcoded chain fail हो जाता है। एक exploit को module का base address पहले leak करना होगा, फिर gadget addresses compute करने होंगे।

5. Kernel Address Space Layout Randomization (KASLR)

Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) user ASLR के समान, KASLR boot time पर kernel text और अन्य kernel structures के base को randomize करता है।

  • Thwarts: kernel-level exploits जो kernel code या data की fixed location पर निर्भर हैं; static kernel exploits।
  • Mechanism detail:
  • हर boot पर kernel का base address randomized होता है (एक range के भीतर)।
  • Kernel data structures (जैसे task_structs, vm_map, आदि) भी relocate या offset किए जा सकते हैं।
  • Attackers को पहले kernel pointers leak करने या information disclosure vulnerabilities का उपयोग करके offsets compute करने होंगे, इससे पहले कि वे kernel structures या code hijack कर सकें।
Example एक local vulnerability kernel function pointer (उदा. `vtable` में) को corrupt करने की कोशिश करती है `KERN_BASE + offset` पर। लेकिन क्योंकि `KERN_BASE` unknown है, attacker को पहले इसे leak करना होगा (उदा. read primitive के माध्यम से) ताकि सही address compute कर सकें।

6. Kernel Patch Protection (KPP / AMCC)

Introduced in newer iOS / A-series hardware (post around iOS 15–16 era or newer chips) KPP (aka AMCC) लगातार kernel text pages की integrity (hash या checksum के द्वारा) monitor करता है। यदि यह tampering (patches, inline hooks, code modifications) detect करता है जो allowed windows के बाहर हों, तो यह kernel panic या reboot trigger करता है।

  • Thwarts: persistent kernel patching (kernel instructions modify करना), inline hooks, static function overwrites।
  • Mechanism detail:
  • एक hardware या firmware module kernel text region को monitor करता है।
  • यह periodically या on-demand pages का re-hash करता है और expected values से compare करता है।
  • यदि mismatches benign update windows के बाहर होते हैं, तो यह device को panic कर देता है (persistent malicious patch से बचने के लिए)।
  • Attackers को या तो detection windows से बचना होगा या legitimate patch paths का उपयोग करना होगा।
Example एक exploit kernel function prologue (उदा. `memcmp`) को patch करने की कोशिश करता है ताकि calls intercept हो सकें। लेकिन KPP notice करता है कि code page का hash अब expected value से match नहीं करता और kernel panic trigger कर देता है, जिससे device crash हो जाता है उससे पहले कि patch stabilize हो।

7. Kernel Text Read‐Only Region (KTRR)

Introduced in modern SoCs (post ~A12 / newer hardware) KTRR एक hardware-enforced mechanism है: एक बार kernel text early boot के दौरान lock हो जाने के बाद, वह EL1 (kernel) से code pages के लिए read-only बन जाता है, जिससे आगे के writes रोक दिए जाते हैं।

  • Thwarts: boot के बाद kernel code में किसी भी तरह के modification (उदा. patching, in-place code injection) को EL1 privilege level पर।
  • Mechanism detail:
  • Boot के दौरान (secure/bootloader stage में), memory controller (या एक secure hardware unit) physical pages जिनमें kernel text है उन्हें read-only mark करता है।
  • भले ही कोई exploit full kernel privileges हासिल कर ले, वह उन pages को write नहीं कर सकता ताकि instructions patch हों।
  • उन्हें modify करने के लिए attacker को पहले boot chain compromise करना होगा, या स्वयं KTRR को subvert करना होगा।
Example एक privilege-escalation exploit EL1 में कूद कर kernel function में trampoline लिखने की कोशिश करता है (उदा. `syscall` handler)। लेकिन क्योंकि pages KTRR द्वारा read-only locked हैं, write fail होता है (या fault trigger होता है), इसलिए patches apply नहीं होते।

8. Pointer Authentication Codes (PAC)

Introduced with ARMv8.3 (hardware), Apple beginning with A12 / iOS ~12+

  • PAC एक hardware feature है जो ARMv8.3-A में introduce हुआ ताकि pointer values (return addresses, function pointers, certain data pointers) के tampering का पता लगाया जा सके, pointer के unused high bits में एक छोटा cryptographic signature (एक “MAC”) embed करके।
  • signature (“PAC”) pointer value और एक modifier (एक context value, जैसे stack pointer या कुछ distinguishing data) के ऊपर compute की जाती है। इस तरह वही pointer value अलग-अलग contexts में अलग PAC देता है।
  • use-time पर, pointer को dereference या branch करने से पहले एक authenticate instruction PAC चेक करता है। यदि valid है, PAC strip हो जाता है और pure pointer मिलता है; यदि invalid है, pointer “poisoned” हो जाता है (या fault raise होता है)।
  • PAC बनाने/validate करने के लिए keys privileged registers (EL1, kernel) में रहते हैं और user mode से सीधे readable नहीं हैं।
  • क्योंकि बहुत सी systems में pointer के सभी 64 bits इस्तेमाल नहीं होते (उदा. 48-bit address space), upper bits “spare” होते हैं और PAC को बिना effective address बदले hold कर सकते हैं।

Architectural Basis & Key Types

  • ARMv8.3 पांच 128-bit keys introduce करता है (प्रत्येक दो 64-bit system registers के माध्यम से implement) pointer authentication के लिए।

  • APIAKey — instruction pointers के लिए (domain “I”, key A)

  • APIBKey — दूसरा instruction pointer key (domain “I”, key B)

  • APDAKey — data pointers के लिए (domain “D”, key A)

  • APDBKey — data pointers के लिए (domain “D”, key B)

  • APGAKey — “generic” key, non-pointer data या अन्य generic uses के लिए

  • ये keys privileged system registers में store होते हैं (केवल EL1/EL2 आदि में accessible), user mode से accessible नहीं।

  • PAC cryptographic function (ARM सुझाव देता है QARMA algorithm) से compute होती है जिसका इनपुट होता है:

  1. Pointer value (canonical portion)
  2. एक modifier (context value, जैसे salt)
  3. Secret key
  4. कुछ internal tweak logic यदि resulting PAC pointer के upper bits में stored value से match करता है तो authentication succeed करता है।

Instruction Families

नaming convention है: PAC / AUT / XPAC, फिर domain letters।

  • PACxx instructions एक pointer को sign करती हैं और PAC insert करती हैं
  • AUTxx instructions authenticate + strip करती हैं (validate और PAC remove)
  • XPACxx instructions बिना validate किए PAC strip करती हैं

Domains / suffixes:

MnemonicMeaning / DomainKey / DomainExample Usage in Assembly
PACIASign instruction pointer with APIAKey“I, A”PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1
PACIBSign instruction pointer with APIBKey“I, B”PACIB X2, X3
PACDASign data pointer with APDAKey“D, A”PACDA X4, X5
PACDBSign data pointer with APDBKey“D, B”PACDB X6, X7
PACG / PACGAGeneric (non-pointer) signing with APGAKey“G”PACGA X8, X9, X10 (sign X9 with modifier X10 into X8)
AUTIAAuthenticate APIA-signed instruction pointer & strip PAC“I, A”AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip
AUTIBAuthenticate APIB domain“I, B”AUTIB X2, X3
AUTDAAuthenticate APDA-signed data pointer“D, A”AUTDA X4, X5
AUTDBAuthenticate APDB-signed data pointer“D, B”AUTDB X6, X7
AUTGAAuthenticate generic / blob (APGA)“G”AUTGA X8, X9, X10 (validate generic)
XPACIStrip PAC (instruction pointer, no validation)“I”XPACI X0 — remove PAC from X0 (instruction domain)
XPACDStrip PAC (data pointer, no validation)“D”XPACD X4 — remove PAC from data pointer in X4

There are specialized / alias forms:

  • PACIASP shorthand है PACIA X30, SP के लिए (link register को SP को modifier के रूप में sign करना)
  • AUTIASP है AUTIA X30, SP (link register को SP के साथ authenticate करना)
  • Combined forms जैसे RETAA, RETAB (authenticate-and-return) या BLRAA (authenticate & branch) ARM extensions / compiler support में मौजूद हैं।
  • साथ ही zero-modifier variants भी हैं: PACIZA / PACIZB जहाँ modifier implicitly zero होता है, आदि।

Modifiers

modifier का मुख्य उद्देश्य PAC को एक विशिष्ट context से जोड़ना है ताकि एक ही address अलग contexts में sign होने पर अलग PAC बनें। यह pointers के simple reuse across frames या objects को रोकता है। यह एक तरह से hash में salt जोड़ने जैसा है।

इसलिए:

  • modifier एक context value (एक और register) है जो PAC computation में mix होता है। सामान्य choices: stack pointer (SP), frame pointer, या कोई object ID।
  • return address signing में SP को modifier के रूप में उपयोग करना सामान्य है: PAC specific stack frame से जुड़ जाती है। यदि आप LR को किसी दूसरे frame में reuse करने की कोशिश करते हैं, तो modifier बदल जाएगा, इसलिए PAC validation fail हो जाएगी।
  • एक ही pointer value अलग modifiers के साथ sign होने पर अलग PACs मिलते हैं।
  • modifier को secret होने की आवश्यकता नहीं है, पर ideally यह attacker-controlled नहीं होना चाहिए।
  • उन instructions के लिए जिनमें sign या verify करने के लिए कोई meaningful modifier नहीं है, कुछ forms zero या implicit constant का उपयोग करते हैं।

Apple / iOS / XNU Customizations & Observations

  • Apple की PAC implementation में per-boot diversifiers शामिल हैं ताकि keys या tweaks हर boot पर बदलें, जिससे boots के बीच reuse रोकता है।
  • वे cross-domain mitigations भी शामिल करते हैं ताकि user mode में sign किए गए PACs आसानी से kernel mode में reuse न हो सकें।
  • Apple M1 / Apple Silicon पर reverse engineering ने दिखाया कि वहाँ nine modifier types और key control के लिए Apple-specific system registers हैं।
  • Apple PAC को कई kernel subsystems में उपयोग करता है: return address signing, kernel data में pointer integrity, signed thread contexts, आदि।
  • Google Project Zero ने दिखाया कि एक powerful memory read/write primitive के तहत kernel में, किसी को kernel PACs (A keys के लिए) forge करने की理论 क्षमता मिली थी (A12-era devices पर), पर Apple ने उन paths को patch किया।
  • Apple के सिस्टम में कुछ keys global across kernel होते हैं, जबकि user processes को per-process key randomness मिल सकती है।

PAC Bypasses

  1. Kernel-mode PAC: theoretical vs real bypasses
  • क्योंकि kernel PAC keys और logic tightly controlled हैं (privileged registers, diversifiers, domain isolation), arbitrary signed kernel pointers forge करना बहुत कठिन है।
  • Azad का 2020 “iOS Kernel PAC, One Year Later” रिपोर्ट करता है कि iOS 12-13 में उसने कुछ partial bypasses पाए (signing gadgets, reuse of signed states, unprotected indirect branches) पर कोई full generic bypass नहीं। bazad.github.io
  • Apple’s “Dark Magic” customizations exploitable surfaces को और भी घटाते हैं (domain switching, per-key enabling bits)। i.blackhat.com
  • Apple silicon (M1/M2) पर ज्ञात एक kernel PAC bypass CVE-2023-32424 भी रिपोर्ट किया गया है Zecao Cai et al. द्वारा। i.blackhat.com
  • पर ये bypasses अक्सर बहुत specific gadgets या implementation bugs पर निर्भर करते हैं; ये general-purpose bypass नहीं होते।

इसलिए kernel PAC को आमतौर पर highly robust माना जाता है, हालाँकि यह perfect नहीं है।

  1. User-mode / runtime PAC bypass techniques

ये अधिक सामान्य हैं, और PAC के apply या runtime frameworks में imperfections का फायदा उठाते हैं। नीचे classes दी जा रही हैं, examples के साथ।

2.1 Shared Cache / A key issues

  • dyld shared cache एक बड़ा pre-linked blob है system frameworks और libraries का। क्योंकि यह बेहद साझा होता है, shared cache के अंदर function pointers “pre-signed” होते हैं और कई processes द्वारा उपयोग किए जाते हैं। Attackers इन पहले से-signed pointers को “PAC oracles” की तरह target करते हैं।
  • कुछ bypass techniques shared cache में मौजूद A-key signed pointers को extract या reuse करने की कोशिश करती हैं और उन्हें gadgets के साथ combine करती हैं।
  • “No Clicks Required” talk ने shared cache पर एक oracle बनाने के बारे में बताया ताकि relative addresses infer की जा सकें और signed pointers के साथ combine करके PAC bypass किया जा सके। saelo.github.io
  • साथ ही, userspace में shared libraries से function pointers के imports कुछ मामलों में PAC द्वारा पर्याप्त रूप से सुरक्षित नहीं पाए गए, जिससे attacker को function pointers बिना signature बदले प्राप्त करने का मौका मिला। (Project Zero bug entry) bugs.chromium.org

2.2 dlsym(3) / dynamic symbol resolution

  • एक ज्ञात bypass है dlsym() को call करना ताकि एक already signed function pointer (A-key से signed, diversifier zero के साथ) मिल सके और फिर उसका उपयोग किया जा सके। क्योंकि dlsym वैध रूप से signed pointer लौटाती है, उसका उपयोग करने से PAC forge करने की आवश्यकता टल जाती है।
  • Epsilon की ब्लॉग पोस्ट बताती है कि कुछ bypasses इसी का फायदा उठाते हैं: dlsym("someSym") एक signed pointer देता है और इसे indirect calls के लिए उपयोग किया जा सकता है। blog.epsilon-sec.com
  • Synacktiv का “iOS 18.4 — dlsym considered harmful” एक बग वर्णित करता है: iOS 18.4 पर कुछ symbols जो dlsym से resolve होते हैं वे pointers return करते हैं जो गलत तरीके से signed होते हैं (या buggy diversifiers के साथ), जिससे अनिच्छित PAC bypass संभव हुआ। Synacktiv
  • dyld में dlsym का logic यह है कि जब result->isCode होता है, वे returned pointer को __builtin_ptrauth_sign_unauthenticated(..., key_asia, 0) के साथ sign करते हैं, यानी context zero। blog.epsilon-sec.com

इस प्रकार, dlsym user-mode PAC bypasses में अक्सर vector होता है।

2.3 Other DYLD / runtime relocations

  • DYLD loader और dynamic relocation logic complex है और कभी-कभी relocations करने के लिए pages को temporarily read/write map किया जाता है, फिर उन्हें वापस read-only किया जाता है। Attackers इन windows का फायदा उठाते हैं। Synacktiv की talk “Operation Triangulation” में dynamic relocations के माध्यम से timing-based PAC bypass का वर्णन है। Synacktiv
  • DYLD pages अब SPRR / VM_FLAGS_TPRO जैसी सुरक्षा के साथ protected हैं (DYLD के लिए कुछ protection flags)। लेकिन पुराने versions में कमजोर guards थे। Synacktiv
  • WebKit exploit chains में, DYLD loader अक्सर PAC bypass का लक्ष्य रहा है। slides में बताया गया है कि कई PAC bypasses ने DYLD loader को target किया है (relocation, interposer hooks के माध्यम से)। Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • Userland exploit chains में, Objective-C runtime methods जैसे NSPredicate, NSExpression या NSInvocation का उपयोग control calls smuggle करने के लिए किया जाता है बिना स्पष्ट pointer forging के।
  • पुराने iOS में (PAC से पहले), एक exploit ने fake NSInvocation objects का उपयोग करके arbitrary selectors को controlled memory पर call करने का तरीका अपनाया था। PAC के साथ, इस तकनीक में परिवर्तन जरूरी हुए। पर SLOP (SeLector Oriented Programming) तकनीक PAC के तहत भी extend की गई। Project Zero
  • मूल SLOP तकनीक ने fake invocations बनाकर ObjC calls chain करने की अनुमति दी थी; bypass इस बात पर निर्भर करता था कि ISA या selector pointers कभी-कभी पूरी तरह PAC-protected नहीं होते थे। Project Zero
  • उन environments में जहाँ pointer authentication आंशिक रूप से apply होती है, methods / selectors / target pointers हमेशा PAC protection में नहीं होते, जिससे bypass के लिए जगह मिलती है।

Example Flow

Example Signing & Authenticating ``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret

; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer

; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target

; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label

</details>

<details>
<summary>उदाहरण</summary>
A buffer overflow स्टैक पर return address को overwrite कर देता है। attacker लक्ष्य gadget address लिखता है पर सही PAC की गणना नहीं कर पाता। जब function return करता है, CPU का `AUTIA` instruction PAC mismatch के कारण fault करता है। chain फेल हो जाती है।
Project Zero का A12 (iPhone XS) पर analysis दिखाता है कि Apple का PAC कैसे उपयोग होता है और यदि attacker के पास memory read/write primitive है तो PACs को forge करने के तरीके क्या हैं।
</details>


### 9. **Branch Target Identification (BTI)**
**Introduced with ARMv8.5 (later hardware)**
BTI एक hardware feature है जो **indirect branch targets** की जाँच करता है: जब `blr` या indirect calls/jumps execute होते हैं, तो target को एक **BTI landing pad** (`BTI j` या `BTI c`) से शुरू होना चाहिए। landing pad न होने पर gadget addresses में jump करने से exception ट्रिगर होता है।

LLVM की implementation तीन प्रकार के BTI instructions और उनके branch types के साथ mapping का उल्लेख करती है।

| BTI Variant | यह क्या अनुमति देता है (कौन से branch types) | सामान्य स्थान / उपयोग केस |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets of *call*-style indirect branches (e.g. `BLR`, or `BR` using X16/X17) | उन functions के entry पर रखा जाता है जिन्हें indirectly call किया जा सकता है |
| **BTI J** | Targets of *jump*-style branches (e.g. `BR` used for tail calls) | उन blocks के शुरुआत में रखा जाता है जिन्हें jump tables या tail-calls से पहुँचा जा सकता है |
| **BTI JC** | Acts as both C and J | इसे या तो call या jump branches target कर सकते हैं |

- Branch target enforcement के साथ compiled code में, compilers प्रत्येक मान्य indirect-branch target (function beginnings या blocks reachable by jumps) पर BTI instruction (C, J, या JC) insert करते हैं ताकि indirect branches केवल उन्हीं स्थानों पर सफल हों।
- **Direct branches / calls** (जैसे fixed-address `B`, `BL`) BTI द्वारा **restricted** नहीं होते। मान लिया जाता है कि code pages trusted हैं और attacker उन्हें बदल नहीं सकता (इसलिए direct branches सुरक्षित माने जाते हैं)।
- इसके अलावा, **RET / return** instructions सामान्यतः BTI से restricted नहीं होते क्योंकि return addresses PAC या return signing mechanisms द्वारा सुरक्षित किए जाते हैं।

#### Mechanism and enforcement

- जब CPU किसी page को “guarded / BTI-enabled” के रूप में decode करते हुए एक **indirect branch (BLR / BR)** को decode करता है, तो यह जाँचता है कि target address का पहला instruction एक मान्य BTI (C, J, या JC जो अनुमति है) है या नहीं। यदि नहीं, तो **Branch Target Exception** होता है।
- BTI instruction encoding पूर्व में NOPs के लिए reserved opcodes का reuse करने के लिए डिज़ाइन किया गया है (पहले के ARM versions में)। इसलिए BTI-enabled binaries backward-compatible रहते हैं: जिन hardware में BTI support नहीं है, उन पर वो instructions NOPs की तरह काम करते हैं।
- BTIs जोड़ने वाले compiler passes उन्हें केवल आवश्यक स्थानों पर insert करते हैं: वे functions जो indirectly call हो सकते हैं, या basic blocks जिन्हें jumps द्वारा target किया जा सकता है।
- कुछ patches और LLVM code दिखाते हैं कि BTI सभी basic blocks के लिए नहीं डाला जाता — केवल उन blocks के लिए जो potential branch targets हैं (उदाहरण के लिए switch / jump tables से)।

#### BTI + PAC synergy

PAC pointer value (source) को बचाता है — यह सुनिश्चित करता है कि indirect calls / returns की chain को tamper नहीं किया गया है।

BTI सुनिश्चित करता है कि भले ही pointer वैध हो, वह केवल सही से marked entry points को ही target करे।

संगठित रूप से, attacker को दोनों की जरूरत होती है: एक वैध pointer सही PAC के साथ और target पर BTI रखना चाहिए। इससे exploit gadgets बनाना कठिन हो जाता है।

#### Example


<details>
<summary>उदाहरण</summary>
एक exploit `0xABCDEF` पर gadget में pivot करने की कोशिश करता है जो `BTI c` से शुरू नहीं होता। CPU, जब `blr x0` execute करता है, तो target की जाँच करता है और fault कर देता है क्योंकि instruction alignment में valid landing pad नहीं है। इसलिए कई gadgets तब तक उपयोगी नहीं होते जब तक उनमें BTI prefix न हो।
</details>


### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Introduced in more recent ARMv8 extensions / iOS support (for hardened kernel)**

#### PAN (Privileged Access Never)

- **PAN** एक feature है जो **ARMv8.1-A** में introduced हुआ और यह **privileged code** (EL1 या EL2) को ऐसे memory को **read या write** करने से रोकता है जो **user-accessible (EL0)** के रूप में चिह्नित है, जब तक कि PAN explicitly disabled न हो।
- विचार यह है: भले ही kernel को trick या compromise किया जाए, वह user-space pointers को बिना पहले *PAN clear* किए arbitrary तरीके से dereference नहीं कर सकेगा, इस तरह `ret2usr` style exploits या user-controlled buffers के दुरुपयोग के जोखिम घटते हैं।
- जब PAN enabled होता है (PSTATE.PAN = 1), तो कोई भी privileged load/store instruction जो ऐसे virtual address को access करता है जो “accessible at EL0” है, वह एक **permission fault** ट्रिगर करता है।
- kernel को, जब उसे वैध रूप से user-space memory तक पहुँचनी होती है (उदा. data को user buffers से copy करने के लिए), तो उसे **अस्थायी रूप से PAN disable** करना होगा (या “unprivileged load/store” instructions का उपयोग करना होगा) ताकि वह access संभव हो सके।
- Linux on ARM64 में PAN support लगभग 2015 के आसपास जोड़ी गई: kernel patches ने feature का detection जोड़ा, और `get_user` / `put_user` आदि को उन variants से replace किया जो user memory accesses के आसपास PAN clear करते हैं।

**मुख्य सूक्ष्मता / सीमा / बग**
- जैसा कि Siguza और अन्य ने बताया, ARM की design में एक specification bug (या ambiguous व्यवहार) का अर्थ है कि **execute-only user mappings** (`--x`) शायद **PAN ट्रिगर न करें**। दूसरे शब्दों में, यदि user page executable के रूप में चिह्नित है पर read permission नहीं है, तो kernel का read प्रयास PAN को bypass कर सकता है क्योंकि architecture “accessible at EL0” को readable permission के साथ मानता है, केवल executable के साथ नहीं। इससे कुछ configurations में PAN bypass हो सकता है।
- इसलिए, यदि iOS / XNU execute-only user pages की अनुमति देता है (जैसे कुछ JIT या code-cache सेटअप में हो सकता है), तो kernel PAN enabled होने के बावजूद उनसे पढ़ सकता है। यह कुछ ARMv8+ systems में एक जाना-पहचाना subtle exploitable क्षेत्र है।

#### PXN (Privileged eXecute Never)

- **PXN** एक page table flag है (page table entries, leaf या block entries में) जो संकेत देता है कि वह page **privileged mode में executable नहीं है** (यानी जब EL1 execute कर रहा हो)।
- PXN kernel (या कोई भी privileged code) को user-space pages से jump करने या वहाँ से instructions execute करने से रोकता है भले ही control divert हो। प्रभावतः यह kernel-level control-flow redirection को user memory में जाने से रोकता है।
- PAN के साथ मिलकर, यह सुनिश्चित करता है कि:
1. Kernel डिफ़ॉल्ट रूप से user-space data को पढ़/लिख न करे (PAN)
2. Kernel user-space code को execute न कर सके (PXN)
- ARMv8 page table format में, leaf entries में `PXN` bit होता है (और unprivileged execute-never के लिए `UXN` भी होता है) जो attribute bits में होते हैं।

इसलिए भले ही kernel के पास एक corrupted function pointer हो जो user memory की ओर इशारा कर रहा हो, और वह वहाँ branch करने की कोशिश करे, तो PXN bit fault करवा देगा।

#### Memory-permission model & how PAN and PXN map to page table bits

PAN / PXN कैसे काम करते हैं यह समझने के लिए आपको ARM के translation और permission model को देखना होगा (सरलीकृत):

- प्रत्येक page या block entry में attribute fields होते हैं जिनमें **AP[2:1]** शामिल होते हैं (access permissions — read/write, privileged vs unprivileged) और execute-never restrictions के लिए **UXN / PXN** bits होते हैं।
- जब PSTATE.PAN 1 (enabled) होता है, तो hardware संशोधित semantics लागू करता है: privileged accesses उन pages पर जो “accessible by EL0” (यानी user-accessible) चिह्नित हैं, को disallow कर देता है (fault)।
- ऊपर बताए गए bug के कारण, केवल executable (read permission के बिना) रूप से चिह्नित pages कुछ implementations में “accessible by EL0” नहीं माने जा सकते, इस तरह PAN bypass हो सकता है।
- जब किसी page का PXN bit set होता है, तो भले ही instruction fetch higher privilege level से हो, execution प्रतिबंधित होता है।

#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)

एक hardened kernel design (जैसे Apple शायद उपयोग करता है) में:

- Kernel डिफ़ॉल्ट रूप से PAN enable करता है (ताकि privileged code constrained रहे)।
- उन code-paths में जिन्हें वैध रूप से user buffers पढ़ने/लिखने की जरूरत होती है (उदा. syscall buffer copy, I/O, read/write user pointer), kernel अस्थायी रूप से **PAN disable** करता है या विशेष instructions का उपयोग करता है ताकि user memory access संभव हो।
- user data access खत्म होने के बाद, kernel को PAN फिर से enable करना चाहिए।
- PXN page tables के माध्यम से enforce किया जाता है: user pages पर PXN = 1 होता है (ताकि kernel उन्हें execute न कर सके), kernel pages पर PXN नहीं होता (ताकि kernel code execute हो सके)।
- Kernel को सुनिश्चित करना चाहिए कि कोई code path execution flow को user memory regions में न ले जाए (जो PXN को bypass कर सके) — इसलिए exploit chains जो “user-controlled shellcode में jump” पर निर्भर करते हैं वे blocked होते हैं।

जैसा कि PAN bypass via execute-only pages में बताया गया है, वास्तविक सिस्टम में Apple या तो execute-only user pages को disable कर सकता है, या specification weakness के आसपास patches लागू कर सकता है।

#### Attack surfaces, bypasses, and mitigations

- **PAN bypass via execute-only pages**: जैसा ऊपर चर्चा की गई, spec में एक अंतर है: user pages जो execute-only (read perm नहीं) हैं वे कुछ implementations में “accessible at EL0” नहीं मानी जा सकतीं, इसलिए PAN kernel के reads को उन pages से block नहीं करेगा। यह attacker को execute-only sections के माध्यम से data feed करने का असामान्य रास्ता देता है।
- **Temporal window exploit**: यदि kernel आवश्यक से अधिक लंबे समय तक PAN disable कर देता है, तो एक race या malicious path उस window का उपयोग कर unintended user memory access कर सकता है।
- **Forgotten re-enable**: यदि code paths PAN को फिर से enable करना भूल जाते हैं, तो subsequent kernel operations गलत तरीके से user memory तक पहुँच सकती हैं।
- **Misconfiguration of PXN**: यदि page tables user pages पर PXN सेट नहीं करते या user code pages को गलत तरीके से map करते हैं, तो kernel को user-space code execute कराने के लिए trick किया जा सकता है।
- **Speculation / side-channels**: speculative bypasses के समकक्ष, ऐसे microarchitectural side-effects हो सकते हैं जो PAN / PXN checks का transient उल्लंघन कराते हैं (हालाँकि ऐसे attacks बहुत हद तक CPU design पर निर्भर करते हैं)।
- **Complex interactions**: उन्नत features (उदा. JIT, shared memory, just-in-time code regions) में kernel को user-mapped regions में कुछ memory accesses या execution की अनुमति देने की सूक्ष्म आवश्यकता होती है; PAN/PXN constraints के तहत इन्हें सुरक्षित तरीके से डिजाइन करना जटिल होता है।

#### Example

<details>
<summary>Code Example</summary>
यहाँ दर्शावटी pseudo-assembly sequences हैं जो दिखाते हैं कि user memory access के आसपास PAN को किस तरह enable/disable किया जाता है, और कैसे एक fault हो सकता है।
</details>

// Suppose kernel entry point, PAN is enabled (privileged code cannot access user memory by default)

; Kernel receives a syscall with user pointer in X0 ; wants to read an integer from user space mov X1, X0 ; X1 = user pointer

; disable PAN to allow privileged access to user memory MSR PSTATE.PAN, #0 ; clear PAN bit, disabling the restriction

ldr W2, [X1] ; now allowed load from user address

; re-enable PAN before doing other kernel logic MSR PSTATE.PAN, #1 ; set PAN

; … further kernel work …

; Later, suppose an exploit corrupts a pointer to a user-space code page and jumps there BR X3 ; branch to X3 (which points into user memory)

; Because the target page is marked PXN = 1 for privileged execution, ; the CPU throws an exception (fault) and rejects execution

If the kernel had **not** set PXN on that user page, then the branch might succeed — which would be insecure.

If the kernel forgets to re-enable PAN after user memory access, it opens a window where further kernel logic might accidentally read/write arbitrary user memory.

If the user pointer is into an execute-only page (user page with only execute permission, no read/write), under the PAN spec bug, `ldr W2, [X1]` might **not** fault even with PAN enabled, enabling a bypass exploit, depending on implementation.

</details>

<details>
<summary>Example</summary>
A kernel vulnerability tries to take a user-provided function pointer and call it in kernel context (i.e. `call user_buffer`). Under PAN/PXN, that operation is disallowed or faults.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI का अर्थ है कि 64-bit pointer के top byte (सबसे-significant byte) को address translation करते समय अनदेखा किया जाता है। इससे OS या hardware pointer के top byte में टैग बिट्स embed कर सकता है बिना वास्तविक address को बदलें।

- TBI का पूरा नाम है **Top Byte Ignore** (कभी-क-कभी इसे *Address Tagging* कहा जाता है)। यह एक hardware feature है (कई ARMv8+ implementations में उपलब्ध) जो **top 8 bits** (bits 63:56) को 64-bit pointer के लिए address translation / load/store / instruction fetch के दौरान अनदेखा (mask) कर देता है।
- प्रभावतः, CPU एक pointer `0xTTxxxx_xxxx_xxxx` (जहाँ `TT` = top byte) को address translation के मामले में `0x00xxxx_xxxx_xxxx` के रूप में देखता है, यानी top byte को mask कर देता है। top byte को software द्वारा **metadata / tag bits** स्टोर करने के लिए इस्तेमाल किया जा सकता है।
- इससे software को हर pointer में एक बाइट टैग embed करने के लिए “free” in-band space मिलता है बिना उस pointer के संदर्भित memory location को बदले।
- Architecture यह सुनिश्चित करता है कि loads, stores, और instruction fetch pointer के top byte को mask (यानी tag हटाकर) होने के बाद ही memory access करें।

इस प्रकार TBI “logical pointer” (pointer + tag) को memory operations के लिए उपयोग किए जाने वाले वास्तविक physical address से अलग कर देता है।

#### Why TBI: Use cases and motivation

- **Pointer tagging / metadata**: आप उस top byte में अतिरिक्त metadata स्टोर कर सकते हैं (जैसे object type, version, bounds, integrity tags)। जब आप बाद में pointer का उपयोग करते हैं, तो hardware स्तर पर tag अनदेखा हो जाता है, इसलिए memory access के लिए उसे मैन्युअली strip करने की जरूरत नहीं रहती।
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI वही बेस hardware मैकेनिज्म है जिस पर MTE बनता है। ARMv8.5 में, **Memory Tagging Extension** pointer के bits 59:56 को एक **logical tag** के रूप में उपयोग करती है और इसे memory में store किए गए **allocation tag** के साथ चेक करती है।
- **Enhanced security & integrity**: TBI को pointer authentication (PAC) या runtime checks के साथ मिलाकर, आप यह सुनिश्चित कर सकते हैं कि न केवल pointer value बल्कि tag भी सही होना चाहिए। बिना सही tag के pointer को overwrite करने वाला attacker tag mismatch पैदा करेगा।
- **Compatibility**: क्योंकि TBI वैकल्पिक है और hardware द्वारा tag bits को अनदेखा किया जाता है, मौजूदा untagged कोड सामान्य रूप से काम करना जारी रखता है। टैग बिट्स legacy कोड के लिए effectively "don’t care" बाइट बन जाते हैं।

#### Example
<details>
<summary>Example</summary>
A function pointer included a tag in its top byte (say `0xAA`). An exploit overwrites the pointer low bits but neglects the tag, so when the kernel verifies or sanitizes, the pointer fails or is rejected.
</details>

---

### 12. **Page Protection Layer (PPL)**
**Introduced in late iOS / modern hardware (iOS ~17 / Apple silicon / high-end models)** (some reports show PPL circa macOS / Apple silicon, but Apple is bringing analogous protections to iOS)

- PPL को एक **intra-kernel protection boundary** के रूप में डिजाइन किया गया है: भले ही kernel (EL1) compromized हो और उसके पास read/write क्षमताएँ हों, **वह कुछ संवेदनशील pages** (खासकर page tables, code-signing metadata, kernel code pages, entitlements, trust caches, आदि) को खुलकर modify नहीं कर पाएगा।
- यह प्रभावतः एक **“kernel के भीतर kernel”** बनाता है — एक छोटा trusted component (PPL) जिसके पास **elevated privileges** होते हैं और केवल वही protected pages को modify कर सकता है। अन्य kernel code को protected page बदलने के लिए PPL routines में कॉल करना होगा।
- यह kernel exploit के लिए attack surface घटाता है: भले ही kernel mode में पूर्ण arbitrary R/W/execute हो, exploit को critical structures बदलने के लिए या तो PPL domain में प्रवेश करना होगा या PPL को bypass करना होगा।
- नए Apple silicon (A15+ / M2+) पर, Apple कई मामलों में page-table protection के लिए PPL के स्थान पर **SPTM (Secure Page Table Monitor)** पर ट्रांज़िशन कर रहा है।

यहाँ बताया गया है कि public analysis के आधार पर PPL कैसे काम करने की संभावना है:

#### Use of APRR / permission routing (APRR = Access Permission ReRouting)

- Apple hardware एक मैकेनिज्म का उपयोग करता है जिसे **APRR (Access Permission ReRouting)** कहा जाता है, जो page table entries (PTEs) को full permission bits की बजाय छोटे indices रखने की अनुमति देता है। ये indices APRR registers के माध्यम से वास्तविक permissions के साथ map होते हैं। इसका अर्थ है कि permissions को डायनामिक रूप से per-domain remap किया जा सकता है।
- PPL APRR का उपयोग kernel context के भीतर privilege segregate करने के लिए करता है: केवल PPL domain को indices और effective permissions के बीच mapping update करने की अनुमति होती है। यानी जब non-PPL kernel code एक PTE लिखता है या permission bits flip करने की कोशिश करता है, तो APRR logic उसे disallow कर देता है (या read-only mapping लागू करता है)।
- PPL कोड स्वयं एक restricted region (उदा. `__PPLTEXT`) में चलता है जो सामान्यतः entry gates के बाहर non-executable या non-writable होता है जब तक कि entry के दौरान अस्थायी रूप से अनुमति न दी जाए। kernel sensitive operations करने के लिए PPL entry points (“PPL routines”) को कॉल करता है।

#### Gate / Entry & Exit

- जब kernel को किसी protected page (उदा. kernel code page की permissions बदलना, या page tables modify करना) में बदलाव करने की ज़रूरत होती है, तो वह एक **PPL wrapper** routine को कॉल करता है, जो validation करता है और फिर PPL domain में transition करता है। उस domain के बाहर protected pages मूलतः main kernel द्वारा read-only या non-modifiable होते हैं।
- PPL entry के दौरान, APRR mappings को adjust किया जाता है ताकि PPL region में memory pages PPL के भीतर **executable & writable** हों। exit पर उन्हें वापस read-only / non-writable कर दिया जाता है। यह सुनिश्चित करता है कि केवल अच्छी तरह audit किए गए PPL routines ही protected pages को लिख सकें।
- PPL के बाहर, उन protected pages को लिखने का प्रयास kernel code द्वारा fault (permission denied) देगा क्योंकि उस code domain के लिए APRR mapping writing की अनुमति नहीं देता।

#### Protected page categories

PPL जो पेज आम तौर पर protect करता है उनमें शामिल हैं:

- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, खासकर वे जिनमें critical logic होता है
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- अन्य high-value kernel structures जहां किसी पेज को patch करने से signature checks या credentials manipulation को bypass किया जा सकता है

मूल विचार यह है कि भले ही kernel memory पर पूरा नियंत्रण मिल जाए, attacker सीधे इन पेजों को patch या rewrite न कर सके, जब तक कि वे PPL routines को compromise न कर लें या PPL को bypass न कर लें।

#### Known Bypasses & Vulnerabilities

1. **Project Zero’s PPL bypass (stale TLB trick)**

- Project Zero द्वारा एक public writeup में stale TLB entries से जुड़ा एक bypass बताया गया है।
- विचार इस तरह है:

1. दो physical pages A और B allocate करें, उन्हें PPL pages के रूप में mark करें (ताकि वे protected हों)।
2. दो virtual addresses P और Q map करें जिनके L3 translation table pages A और B से आते हों।
3. एक thread को Q को लगातार access करने के लिए spin करें, ताकि इसकी TLB entry alive रहे।
4. `pmap_remove_options()` को कॉल करें ताकि P से mappings हटाई जाएँ; एक बग के कारण, कोड गलती से P और Q दोनों के TTEs हटा देता है, पर केवल P के लिए TLB entry invalidate करता है, जिससे Q की stale entry live रह जाती है।
5. B (page Q का table) को reuse करके arbitrary memory map करें (उदा. PPL-protected pages)। क्योंकि stale TLB entry अभी भी Q के old mapping को map करती है, वह mapping उस context के लिए वैध बनी रहती है।
6. इसके माध्यम से, attacker PPL-protected pages का writable mapping बिना PPL interface का उपयोग किए place कर सकता है।

- इस exploit के लिए physical mapping और TLB behavior पर सूक्ष्म नियंत्रण की आवश्यकता थी। यह दिखाता है कि TLB / mapping correctness पर निर्भर सुरक्षा सीमा को TLB invalidations और mapping consistency के प्रति बहुत सावधान रहना चाहिए।
- Project Zero ने टिप्पणी की कि इस तरह के bypasses subtle और दुर्लभ होते हैं, लेकिन complex systems में संभव हैं। फिर भी, वे PPL को एक मजबूत mitigation मानते हैं।

2. **Other potential hazards & constraints**

- अगर कोई kernel exploit सीधे PPL routines में प्रवेश कर सकता है (PPL wrappers कॉल करके), तो वह सीमाओं को bypass कर सकता है। इसलिए argument validation बहुत महत्वपूर्ण है।
- PPL कोड में ही bugs (उदा. arithmetic overflow, boundary checks) होने पर PPL के अंदर out-of-bounds modifications संभव हो सकते हैं। Project Zero ने देखा कि `pmap_remove_options_internal()` में ऐसा एक बग उनके bypass में exploit किया गया था।
- PPL boundary hardware enforcement (APRR, memory controller) पर निर्भर है, इसलिए यह केवल उतना ही मजबूत है जितना hardware implementation।

#### Example
<details>
<summary>Code Example</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry)  // call PPL wrapper
}

// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}

// If kernel code outside PPL does:
*pt_addr = new_entry  // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page
The kernel can do many normal operations, but only through `ppl_call_*` routines can it change protected mappings or patch code.
Example A kernel exploit tries to overwrite the entitlement table, or disable code-sign enforcement by modifying a kernel signature blob. Because that page is PPL-protected, the write is blocked unless going through the PPL interface. So even with kernel code execution, you cannot bypass code-sign constraints or modify credential data arbitrarily. On iOS 17+ certain devices use SPTM to further isolate PPL-managed pages.

PPL → SPTM / Replacements / Future

  • On Apple’s modern SoCs (A15 or later, M2 or later), Apple supports SPTM (Secure Page Table Monitor), which replaces PPL for page table protections.
  • Apple calls out in documentation: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
  • The SPTM architecture likely shifts more policy enforcement into a higher-privileged monitor outside kernel control, further reducing the trust boundary.

MTE | EMTE | MIE

Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:

  1. Tag assignment
  • When memory is allocated (e.g. in kernel or user space via secure allocators), a secret tag is assigned to that block.
  • The pointer returned to the user or kernel includes that tag in its high bits (using TBI / top byte ignore mechanisms).
  1. Tag checking on access
  • Whenever a load or store is executed using a pointer, the hardware checks that the pointer’s tag matches the memory block’s tag (allocation tag). If mismatch, it faults immediately (since synchronous).
  • Because it’s synchronous, there is no “delayed detection” window.
  1. Retagging on free / reuse
  • When memory is freed, the allocator changes the block’s tag (so older pointers with old tags no longer match).
  • A use-after-free pointer would therefore have a stale tag and mismatch when accessed.
  1. Neighbor-tag differentiation to catch overflows
  • Adjacent allocations are given distinct tags. If a buffer overflow spills into neighbor’s memory, tag mismatch causes a fault.
  • This is especially powerful in catching small overflows that cross boundary.
  1. Tag confidentiality enforcement
  • Apple must prevent tag values being leaked (because if attacker learns the tag, they could craft pointers with correct tags).
  • They include protections (microarchitectural / speculative controls) to avoid side-channel leakage of tag bits.
  1. Kernel and user-space integration
  • Apple uses EMTE not just in user-space but also in kernel / OS-critical components (to guard kernel against memory corruption).
  • The hardware/OS ensures tag rules apply even when kernel is executing on behalf of user space.
Example ``` Allocate A = 0x1000, assign tag T1 Allocate B = 0x2000, assign tag T2

// pointer P points into A with tag T1 P = (T1 << 56) | 0x1000

// Valid store *(P + offset) = value // tag T1 matches allocation → allowed

// Overflow attempt: P’ = P + size_of_A (into B region) *(P’ + delta) = value → pointer includes tag T1 but memory block has tag T2 → mismatch → fault

// Free A, allocator retags it to T3 free(A)

// Use-after-free: *(P) = value → pointer still has old tag T1, memory region is now T3 → mismatch → fault

</details>

#### Limitations & challenges

- **Intrablock overflows**: यदि overflow उसी allocation के अंदर रहता है (doesn’t cross boundary) और tag वही रहता है, तो tag mismatch इसे पकड़ नहीं पाता।
- **Tag width limitation**: tag के लिए केवल कुछ bits (e.g. 4 bits, या छोटा domain) उपलब्ध हैं—सीमित namespace।
- **Side-channel leaks**: यदि tag bits cache / speculative execution के माध्यम से leaked हो सकते हैं, तो attacker valid tags सीख सकता है और bypass कर सकता है। Apple की tag confidentiality enforcement इसे mitigate करने के लिए है।
- **Performance overhead**: हर load/store पर tag checks अतिरिक्त लागत जोड़ते हैं; Apple को hardware को optimize करना होगा ताकि overhead कम रहे।
- **Compatibility & fallback**: पुराने hardware या उन हिस्सों पर जो EMTE को support नहीं करते, fallback मौजूद होना चाहिए। Apple का दावा है कि MIE केवल उन devices पर enabled है जिनमें support है।
- **Complex allocator logic**: Allocator को tags, retagging, boundary aligning संभालना होगा और mis-tag collisions से बचना होगा। Allocator logic में bugs vulnerabilities ला सकते हैं।
- **Mixed memory / hybrid areas**: कुछ memory untagged (legacy) बनी रह सकती है, जिससे interoperability और जटिल हो जाती है।
- **Speculative / transient attacks**: कई microarchitectural protections की तरह, speculative execution या micro-op fusions समयिक रूप से checks को bypass कर सकते हैं या tag bits को leak कर सकते हैं।
- **Limited to supported regions**: Apple संभवतः EMTE को केवल चुनिंदा, high-risk क्षेत्रों (kernel, security-critical subsystems) में लागू करे, न कि सार्वत्रिक रूप से।

---

## Key enhancements / differences compared to standard MTE

Here are the improvements and changes Apple emphasizes:

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Supports synchronous and asynchronous modes. In async, tag mismatches are reported later (delayed)| Apple insists on **synchronous mode** by default—tag mismatches are caught immediately, no delay/race windows allowed.|
| **Coverage of non-tagged memory** | Accesses to non-tagged memory (e.g. globals) may bypass checks in some implementations | EMTE requires that accesses from a tagged region to non-tagged memory also validate tag knowledge, making it harder to bypass by mixing allocations.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple adds **Tag Confidentiality Enforcement**, which attempts to prevent leak of tag values (via speculative side-channels etc.).|
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Apple’s secure typed allocators (kalloc_type, xzone malloc, etc.) integrate with EMTE: when memory is allocated or freed, tags are managed at fine granularity.|
| **Always-on by default** | In many platforms, MTE is optional or off by default | Apple enables EMTE / MIE by default on supported hardware (e.g. iPhone 17 / A19) for kernel and many user processes.|

क्योंकि Apple हार्डवेयर और software stack दोनों नियंत्रित करता है, वह EMTE को कड़ाई से लागू कर सकता है, performance pitfalls से बच सकता है, और side-channel holes को बंद कर सकता है।

---

## How EMTE works in practice (Apple / MIE)

Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:

1. **Tag assignment**
- जब memory allocate होती है (e.g. kernel या user space में secure allocators के द्वारा), उस block को एक **secret tag** असाइन किया जाता है।
- user या kernel को लौटाया गया pointer उसके high bits में वह tag शामिल करता है (using TBI / top byte ignore mechanisms).

2. **Tag checking on access**
- जब भी pointer का उपयोग कर load या store execute होता है, hardware यह चेक करता है कि pointer का tag memory block के tag (allocation tag) से मेल खाता है। यदि mismatch हो, तो यह तुरंत fault करता है (since synchronous)।
- चूंकि यह synchronous है, इसलिए कोई “delayed detection” विंडो नहीं रहती।

3. **Retagging on free / reuse**
- जब memory free होती है, allocator block का tag बदल देता है (ताकि पुराने pointers जिनके tags पुराने हैं वे अब मैच न करें)।
- इस तरह use-after-free pointer के पास stale tag होगा और access पर mismatch होगा।

4. **Neighbor-tag differentiation to catch overflows**
- Adjacent allocations को अलग-अलग tags दिए जाते हैं। यदि buffer overflow पड़ोसी की memory में spill करता है, तो tag mismatch fault पैदा करेगा।
- यह खासकर छोटे overflows को पकड़ने में प्रभावशाली है जो boundary cross करते हैं।

5. **Tag confidentiality enforcement**
- Apple को tag values के leaked होने को रोकना होगा (क्योंकि अगर attacker tag सीख लेता है, तो वे सही tags वाले pointers बना सकते हैं)।
- वे protections (microarchitectural / speculative controls) शामिल करते हैं ताकि tag bits के side-channel leakage से बचा जा सके।

6. **Kernel and user-space integration**
- Apple EMTE का उपयोग सिर्फ user-space में नहीं बल्कि kernel / OS-critical components में भी करता है (kernel को memory corruption से बचाने के लिए)।
- Hardware/OS यह सुनिश्चित करता है कि tag नियम तब भी लागू हों जब kernel user space की ओर से execute कर रहा हो।

क्योंकि EMTE MIE में बना हुआ है, Apple प्रमुख attack surfaces पर EMTE को synchronous mode में इस्तेमाल करता है, इसे opt-in या debugging mode के रूप में नहीं रखता।

---

## Exception handling in XNU

When an **exception** occurs (e.g., `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, etc.), the **Mach layer** of the XNU kernel is responsible for intercepting it before it becomes a UNIX-style **signal** (like `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).

यह प्रक्रिया user space तक पहुँचने या BSD signal में convert होने से पहले कई स्तरों की exception propagation और handling शामिल करती है।

### Exception Flow (High-Level)

1.  **CPU triggers a synchronous exception** (e.g., invalid pointer dereference, PAC failure, illegal instruction, etc.).
2.  **Low-level trap handler** runs (`trap.c`, `exception.c` in XNU source).
3.  The trap handler calls **`exception_triage()`**, the core of the Mach exception handling.
4.  `exception_triage()` तय करता है कि exception को कैसे route किया जाए:

-   First to the **thread's exception port**.

-   Then to the **task's exception port**.

-   Then to the **host's exception port** (often `launchd` or `ReportCrash`).

यदि इन में से कोई भी port exception को handle नहीं करता, तो kernel कर सकता है:

-   **Convert it into a BSD signal** (for user-space processes).

-   **Panic** (for kernel-space exceptions).


### Core Function: `exception_triage()`

The function `exception_triage()` Mach exceptions को संभावित handlers की श्रृंखला में ऊपर route करता है जब तक कोई इसे handle न कर दे या यह अंततः fatal न हो जाए। यह `osfmk/kern/exception.c` में परिभाषित है।
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

सामान्य कॉल फ्लो:

exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()

यदि सभी विफल होते हैं → bsd_exception() द्वारा संभाला जाता है → SIGSEGV जैसे सिग्नल में परिवर्तित किया जाता है।

Exception Ports

प्रत्येक Mach ऑब्जेक्ट (thread, task, host) exception ports रजिस्टर कर सकता है, जहाँ exception messages भेजे जाते हैं।

ये API द्वारा परिभाषित होते हैं:

task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()

Each exception port has:

  • A mask (कौन से exceptions यह प्राप्त करना चाहता है)
  • A port name (Mach port to receive messages)
  • A behavior (how the kernel sends the message)
  • A flavor (which thread state to include)

Debuggers and Exception Handling

A debugger (e.g., LLDB) लक्ष्य task या thread पर एक exception port सेट करता है, आमतौर पर task_set_exception_ports() के माध्यम से।

When an exception occurs:

  • Mach message debugger process को भेजा जाता है।
  • Debugger निर्णय ले सकता है कि वह exception को handle करे (resume, registers modify करना, instruction skip करना) या not handle करे।
  • अगर debugger इसे handle नहीं करता, तो exception अगले स्तर पर propagate होता है (task → host).

Flow of EXC_BAD_ACCESS

  1. Thread एक invalid pointer को dereference करता है → CPU Data Abort उठाता है।

  2. Kernel trap handler कॉल करता है exception_triage(EXC_BAD_ACCESS, ...)

  3. Message भेजा जाता है:

  • Thread port → (debugger breakpoint को intercept कर सकता है).

  • अगर debugger ignore करे → Task port → (process-level handler).

  • अगर ignored रहे → Host port (usually ReportCrash).

  1. अगर कोई इसे handle नहीं करता → bsd_exception() इसे SIGSEGV में translate कर देता है।

PAC Exceptions

जब Pointer Authentication (PAC) fail करता है (signature mismatch), एक special Mach exception उठता है:

  • EXC_ARM_PAC (type)
  • Codes में विवरण हो सकते हैं (e.g., key type, pointer type).

अगर binary में flag TFRO_PAC_EXC_FATAL सेट है, तो kernel PAC failures को fatal मानता है और debugger interception को bypass कर देता है। यह attackers को debuggers का उपयोग करके PAC checks को bypass करने से रोकने के लिए है और यह platform binaries के लिए enabled होता है।

Software Breakpoints

एक software breakpoint (int3 on x86, brk on ARM64) सामान्यतः deliberate fault पैदा करके लागू किया जाता है.
Debugger इसे exception port के ज़रिये पकड़ता है:

  • instruction pointer या memory को modify करता है।
  • original instruction restore करता है।
  • execution resume करता है।

यह वही mechanism है जो आपको PAC exception “catch” करने की अनुमति देता है — सिवाय इसके कि TFRO_PAC_EXC_FATAL सेट हो, तब यह कभी debugger तक नहीं पहुँचता।

Conversion to BSD Signals

अगर कोई handler exception स्वीकार नहीं करता:

  • Kernel कॉल करता है task_exception_notify() → bsd_exception()

  • यह Mach exceptions को signals में map करता है:

Mach ExceptionSignal
EXC_BAD_ACCESSSIGSEGV or SIGBUS
EXC_BAD_INSTRUCTIONSIGILL
EXC_ARITHMETICSIGFPE
EXC_SOFTWARESIGTRAP
EXC_BREAKPOINTSIGTRAP
EXC_CRASHSIGKILL
EXC_ARM_PACSIGILL (on non-fatal)

### Key Files in XNU Source

  • osfmk/kern/exception.c → Core of exception_triage(), exception_deliver_*().
  • bsd/kern/kern_sig.c → Signal delivery logic.
  • osfmk/arm64/trap.c → Low-level trap handlers.
  • osfmk/mach/exc.h → Exception codes and structures.
  • osfmk/kern/task.c → Task exception port setup.

Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)

Kernel ने एक zone allocator (kalloc) का उपयोग किया जो fixed-size “zones” में विभाजित था। हर zone केवल एक ही size class की allocations रखता था।

From the screenshot:

Zone NameElement SizeExample Use
default.kalloc.1616 bytesVery small kernel structs, pointers.
default.kalloc.3232 bytesSmall structs, object headers.
default.kalloc.6464 bytesIPC messages, tiny kernel buffers.
default.kalloc.128128 bytesMedium objects like parts of OSObject.
default.kalloc.12801280 bytesLarge structures, IOSurface/graphics metadata.

कैसे काम करता था:

  • हर allocation request को नज़दीकी zone size पर rounded up किया जाता था। (उदाहरण के लिए, 50-byte request kalloc.64 zone में जाएगा).
  • हर zone की memory एक freelist में रखी जाती थी — kernel द्वारा freed किए गए chunks वापस उसी zone में चले जाते थे।
  • अगर आप एक 64-byte buffer को overflow करते थे, तो आप उसी zone के अगले object को overwrite कर देते थे।

यही वजह है कि heap spraying / feng shui इतना प्रभावी था: आप एक ही size class की allocations करके object के पड़ोसी का अनुमान लगा सकते थे।

The freelist

हर kalloc zone के अंदर, freed objects सीधे system को वापस नहीं जाते थे — वे freelist में चले जाते थे, जो कि उपलब्ध chunks की एक linked list थी।

  • जब कोई chunk free होता था, तो kernel उस chunk की शुरुआत में एक pointer लिखता था → उसी zone के अगले free chunk का address।

  • Zone के पास HEAD pointer होता था जो पहले free chunk को इंगित करता था।

  • Allocation हमेशा current HEAD का उपयोग करता था:

  1. Pop HEAD (वह memory caller को वापस की जाती है)।

  2. Update HEAD = HEAD->next (जो freed chunk के header में store होता है)।

  • Freeing chunks को वापस push कर देता था:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

तो freelist बस freed memory के अंदर बनाई गई एक linked list थी।

Normal state:

Zone page (64-byte chunks for example):
[ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ]

Freelist view:
HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL
(next ptrs stored at start of freed chunks)

Exploiting the freelist

क्योंकि किसी free chunk के पहले 8 बाइट्स = freelist pointer होते हैं, एक attacker इसे corrupt कर सकता है:

  1. Heap overflow into an adjacent freed chunk → overwrite its “next” pointer.

  2. Use-after-free write into a freed object → overwrite its “next” pointer.

फिर, उसी साइज के अगले allocation पर:

  • The allocator pops the corrupted chunk.
  • Follows the attacker-supplied “next” pointer.
  • Returns a pointer to arbitrary memory, enabling fake object primitives or targeted overwrite.

Visual example of freelist poisoning:

Before corruption:
HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL

After attacker overwrite of F1->next:
HEAD ──► [ F1 ]
(next) ──► 0xDEAD_BEEF_CAFE_BABE  (attacker-chosen)

Next alloc of this zone → kernel hands out memory at attacker-controlled address.

This freelist डिज़ाइन hardening से पहले exploitation को बहुत प्रभावी बनाता था: heap sprays से predictable neighbors, raw pointer freelist links, और कोई type separation न होने के कारण attackers UAF/overflow बग्स को arbitrary kernel memory control में escalate कर पाते थे।

Heap Grooming / Feng Shui

The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.

Steps:

  1. Spray allocations (fill the holes)
  • Over time, the kernel heap gets fragmented: some zones have holes where old objects were freed.
  • The attacker first makes lots of dummy allocations to fill these gaps, so the heap becomes “packed” and predictable.
  1. Force new pages
  • Once the holes are filled, the next allocations must come from new pages added to the zone.
  • Fresh pages mean objects will be clustered together, not scattered across old fragmented memory.
  • This gives the attacker much better control of neighbors.
  1. Place attacker objects
  • The attacker now sprays again, creating lots of attacker-controlled objects in those new pages.
  • These objects are predictable in size and placement (since they all belong to the same zone).
  1. Free a controlled object (make a gap)
  • The attacker deliberately frees one of their own objects.
  • This creates a “hole” in the heap, which the allocator will later reuse for the next allocation of that size.
  1. Victim object lands in the hole
  • The attacker triggers the kernel to allocate the victim object (the one they want to corrupt).
  • Since the hole is the first available slot in the freelist, the victim is placed exactly where the attacker freed their object.
  1. Overflow / UAF into victim
  • Now the attacker has attacker-controlled objects around the victim.
  • By overflowing from one of their own objects (or reusing a freed one), they can reliably overwrite the victim’s memory fields with chosen values.

Why it works:

  • Zone allocator predictability: allocations of the same size always come from the same zone.
  • Freelist behavior: new allocations reuse the most recently freed chunk first.
  • Heap sprays: attacker fills memory with predictable content and controls layout.
  • End result: attacker controls where the victim object lands and what data sits next to it.

Modern Kernel Heap (iOS 15+/A12+ SoCs)

Apple ने allocator को harden किया और heap grooming बहुत कठिन बना दिया:

1. From Classic kalloc to kalloc_type

  • Before: a single kalloc.<size> zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects.
  • Now:
  • Kernel objects are allocated from typed zones (kalloc_type).
  • Each type of object (e.g., ipc_port_t, task_t, OSString, OSData) has its own dedicated zone, even if they’re the same size.
  • The mapping between object type ↔ zone is generated from the kalloc_type system at compile time.

An attacker can no longer guarantee that controlled data (OSData) ends up adjacent to sensitive kernel objects (task_t) of the same size.

2. Slabs and Per-CPU Caches

  • The heap is divided into slabs (pages of memory carved into fixed-size chunks for that zone).
  • Each zone has a per-CPU cache to reduce contention.
  • Allocation path:
  1. Try per-CPU cache.
  2. If empty, pull from the global freelist.
  3. If freelist is empty, allocate a new slab (one or more pages).
  • Benefit: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches.

3. Randomization inside zones

  • Within a zone, freed elements are not handed back in simple FIFO/LIFO order.
  • Modern XNU uses encoded freelist pointers (safe-linking like Linux, introduced ~iOS 14).
  • Each freelist pointer is XOR-encoded with a per-zone secret cookie.
  • This prevents attackers from forging a fake freelist pointer if they gain a write primitive.
  • Some allocations are randomized in their placement within a slab, so spraying doesn’t guarantee adjacency.

4. Guarded Allocations

  • Certain critical kernel objects (e.g., credentials, task structures) are allocated in guarded zones.
  • These zones insert guard pages (unmapped memory) between slabs or use redzones around objects.
  • Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption.

5. Page Protection Layer (PPL) and SPTM

  • Even if you control a freed object, you can’t modify all of kernel memory:
  • PPL (Page Protection Layer) enforces that certain regions (e.g., code signing data, entitlements) are read-only even to the kernel itself.
  • On A15/M2+ devices, this role is replaced/enhanced by SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
  • These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures.
  • (Added / Enhanced): also, PAC (Pointer Authentication Codes) is used in the kernel to protect pointers (especially function pointers, vtables) so that forging or corrupting them becomes harder.
  • (Added / Enhanced): zones may enforce zone_require / zone enforcement, i.e. that an object freed can only be returned through its correct typed zone; invalid cross-zone frees may panic or be rejected. (Apple alludes to this in their memory safety posts)

6. Large Allocations

  • Not all allocations go through kalloc_type.
  • Very large requests (above ~16 KB) bypass typed zones and are served directly from kernel VM (kmem) via page allocations.
  • These are less predictable, but also less exploitable, since they don’t share slabs with other objects.

7. Allocation Patterns Attackers Target

Even with these protections, attackers still look for:

  • Reference count objects: if you can tamper with retain/release counters, you may cause use-after-free.
  • Objects with function pointers (vtables): corrupting one still yields control flow.
  • Shared memory objects (IOSurface, Mach ports): these are still attack targets because they bridge user ↔ kernel.

But — unlike before — you can’t just spray OSData and expect it to neighbor a task_t. You need type-specific bugs or info leaks to succeed.

Example: Allocation Flow in Modern Heap

Suppose userspace calls into IOKit to allocate an OSData object:

  1. Type lookupOSData maps to kalloc_type_osdata zone (size 64 bytes).
  2. Check per-CPU cache for free elements.
  • If found → return one.
  • If empty → go to global freelist.
  • If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
  1. Return chunk to caller.

Freelist pointer protection:

  • Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
  • Overwriting that field with attacker data won’t work unless you know the key.

Comparison Table

FeatureOld Heap (Pre-iOS 15)Modern Heap (iOS 15+ / A12+)
Allocation granularityFixed size buckets (kalloc.16, kalloc.32, etc.)Size + type-based buckets (kalloc_type)
Placement predictabilityHigh (same-size objects side by side)Low (same-type grouping + randomness)
Freelist managementRaw pointers in freed chunks (easy to corrupt)Encoded pointers (safe-linking style)
Adjacent object controlEasy via sprays/frees (feng shui predictable)Hard — typed zones separate attacker objects
Kernel data/code protectionsFew hardware protectionsPPL / SPTM protect page tables & code pages, and PAC protects pointers
Allocation reuse validationNone (freelist pointers raw)zone_require / zone enforcement
Exploit reliabilityHigh with heap spraysMuch lower, requires logic bugs or info leaks
Large allocations handlingAll small allocations managed equallyLarge ones bypass zones → handled via VM

Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)

Recent Apple OS versions (especially iOS 17+) में Apple ने एक अधिक सुरक्षित userland allocator पेश किया है, xzone malloc (XZM). यह kernel के kalloc_type का user-space समकक्ष है, जो type awareness, metadata isolation और memory tagging safeguards लागू करता है।

Goals & Design Principles

  • Type segregation / type awareness: अलग-अलग types या usage (pointer vs data) के आधार पर allocations को group करना ताकि type confusion और cross-type reuse रोका जा सके।
  • Metadata isolation: heap metadata (जैसे free lists, size/state bits) को object payloads से अलग रखना ताकि out-of-bounds writes से metadata कम प्रभावित हो।
  • Guard pages / redzones: allocations के चारों ओर unmapped pages या padding डालना ताकि overflows पकड़े जा सकें।
  • Memory tagging (EMTE / MIE): hardware tagging के साथ मिलकर use-after-free, out-of-bounds, और invalid accesses को डिटेक्ट करना।
  • Scalable performance: कम overhead बनाए रखना, fragmentation कम करना, और high allocation rates के साथ low latency बनाए रखना।

Architecture & Components

नीचे xzone allocator के मुख्य घटक हैं:

Segment Groups & Zones

  • Segment groups address space को usage कैटेगोरीज़ से partition करते हैं: जैसे data, pointer_xzones, data_large, pointer_large.
  • प्रत्येक segment group में segments (VM ranges) होते हैं जो उस category के allocations होस्ट करते हैं।
  • हर segment के साथ एक metadata slab जुड़ा होता है (अलग VM क्षेत्र) जो metadata (जैसे free/used bits, size classes) स्टोर करता है। यह out-of-line (OOL) metadata यह सुनिश्चित करता है कि metadata object payloads के साथ intermingled न हो, जिससे overflows से नुकसान कम होता है।
  • Segments को chunks (slices) में काटा जाता है जो बदले में blocks (allocation units) में विभाजित होते हैं। एक chunk एक specific size class और segment group से संबंधित होता है (यानी chunk के सभी blocks एक ही size & category शेयर करते हैं)।
  • छोटे/मध्यम allocations के लिए fixed-size chunks का उपयोग होता है; बड़े allocations अलग से map किए जा सकते हैं।

Chunks & Blocks

  • एक chunk एक क्षेत्र है (अक्सर कई pages) जो एक size class के allocations के लिए समर्पित होता है।
  • Chunk के अंदर, blocks allocation के लिए उपलब्ध slots होते हैं। Freed blocks को metadata slab द्वारा ट्रैक किया जाता है — जैसे bitmaps या out-of-line free lists के माध्यम से।
  • Chunks के बीच (या अंदर) guard slices / guard pages डाले जा सकते हैं (उदा. unmapped slices) ताकि out-of-bounds writes पकड़े जा सकें।

Type / Type ID

  • हर allocation site (या malloc, calloc, आदि कॉल) एक type identifier (malloc_type_id_t) के साथ जुड़ा होता है जो बताता है कि किस तरह का object allocate हो रहा है। वह type ID allocator को दी जाती है, जो zone/segment चुनने के लिए इसका उपयोग करती है।
  • इसलिए, भले ही दो allocations का size समान हो, वे अलग types होने पर पूरी तरह अलग zones में जा सकते हैं।
  • शुरुआती iOS 17 versiones में, सभी APIs (उदा. CFAllocator) पूरी तरह type-aware नहीं थे; Apple ने iOS 18 में कुछ कमजोरियों को address किया।

Allocation & Freeing Workflow

यहाँ xzone में allocation और deallocation का high-level flow है:

  1. malloc / calloc / realloc / typed alloc को size और type ID के साथ कॉल किया जाता है।
  2. Allocator type ID का उपयोग करके सही segment group / zone चुनता है।
  3. उस zone/segment के अंदर, वह ऐसे chunk की तलाश करता है जिसमें requested size के free blocks हों।
  • यह local caches / per-thread pools या metadata से free block lists देख सकता है।
  • अगर कोई free block नहीं मिलता, तो वह उस zone में नया chunk allocate कर सकता है।
  1. Metadata slab अपडेट होता है (free bit cleared, bookkeeping)।
  2. अगर memory tagging (EMTE) लागू है, तो returned block को एक tag दिया जाता है, और metadata को उसकी “live” state दिखाने के लिए अपडेट किया जाता है।
  3. जब free() होता है:
  • Block को metadata में freed के रूप में चिह्नित किया जाता है (OOL slab के माध्यम से)।
  • Block को reuse के लिए free list में रखा जा सकता है या pooled किया जा सकता है।
  • वैकल्पिक रूप से, block contents को cleared या poisoned किया जा सकता है ताकि data leaks या use-after-free exploitation की संभावना कम हो।
  • उस block से जुड़ा hardware tag invalidated या re-tagged किया जा सकता है।
  • अगर पूरा chunk free हो जाता है (सभी blocks free), तो allocator उस chunk को reclaim कर सकता है (unmap या OS को return) memory pressure में।

Security Features & Hardening

ये modern userland xzone में built-in defenses हैं:

FeaturePurposeNotes
Metadata decouplingPrevent overflow from corrupting metadataMetadata lives in separate VM region (metadata slab)
Guard pages / unmapped slicesCatch out-of-bounds writesHelps detect buffer overflows rather than silently corrupting adjacent blocks
Type-based segregationPrevent cross-type reuse & type confusionEven same-size allocations from different types go to different zones
Memory Tagging (EMTE / MIE)Detect invalid access, stale references, OOB, UAFxzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”)
Delayed reuse / poisoning / zapReduce chance of use-after-free exploitationFreed blocks may be poisoned, zeroed, or quarantined before reuse
Chunk reclamation / dynamic unmappingReduce memory waste and fragmentationEntire chunks may be unmapped when unused
Randomization / placement variationPrevent deterministic adjacencyBlocks in a chunk and chunk selection may have randomized aspects
Segregation of “data-only” allocationsSeparate allocations that don’t store pointersReduces attacker control over metadata or control fields

Interaction with Memory Integrity Enforcement (MIE / EMTE)

  • Apple की MIE (Memory Integrity Enforcement) वह hardware + OS framework है जो Enhanced Memory Tagging Extension (EMTE) को always-on, synchronous mode में बड़ी attack surfaces पर लाती है।
  • xzone allocator user space में MIE का एक बुनियादी हिस्सा है: xzone के माध्यम से किये गए allocations को tags मिलते हैं, और accesses hardware द्वारा चेक होते हैं।
  • MIE में allocator, tag assignment, metadata management, और tag confidentiality enforcement एकीकृत होते हैं ताकि memory errors (जैसे stale reads, OOB, UAF) तुरंत पकड़े जाएं, न कि बाद में exploit किए जाएं।

If you like, I can also generate a cheat-sheet or diagram of xzone internals for your book. Do you want me to do that next?
:contentReference[oai:20]{index=20}

(Old) Physical Use-After-Free via IOSurface

ios Physical UAF - IOSurface


Ghidra Install BinDiff

Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.

Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and isntall it even if there is a version mismatch.

Using BinDiff with Kernel versions

  1. Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be .ipsw files.
  2. Decompress until you get the bin format of the kernelcache of both .ipsw files. You have information on how to do this on:

macOS Kernel Extensions & Kernelcache

  1. Open Ghidra with ghidraRun, create a new project and load the kernelcaches.
  2. Open each kernelcache so they are automatically analyzed by Ghidra.
  3. Then, on the project Window of Ghidra, right click each kernelcache, select Export, select format Binary BinExport (v2) for BinDiff and export them.
  4. Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.

Finding the right XNU version

If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).

For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.

JSKit-Based Safari Chains and PREYHUNTER Stagers

Renderer RCE abstraction with JSKit

  • Reusable entry: Recent in-the-wild chains abused a WebKit JIT bug (patched as CVE-2023-41993) purely to gain JavaScript-level arbitrary read/write. The exploit immediately pivots into a purchased framework called JSKit, so any future Safari bug only needs to deliver the same primitive.
  • Version abstraction & PAC bypasses: JSKit bundles support for a wide range of iOS releases together with multiple, selectable Pointer Authentication Code bypass modules. The framework fingerprints the target build, selects the appropriate PAC bypass logic, and verifies every step (primitive validation, shellcode launch) before progressing.
  • Manual Mach-O mapping: JSKit parses Mach-O headers directly from memory, resolves the symbols it needs inside dyld-cached images, and can manually map additional Mach-O payloads without writing them to disk. This keeps the renderer process in-memory only and evades code-signature checks tied to filesystem artifacts.
  • Portfolio model: Debug strings such as “exploit number 7” show that the suppliers maintain multiple interchangeable WebKit exploits. Once the JS primitive matches JSKit’s interface, the rest of the chain is unchanged across campaigns.

Kernel bridge: IPC UAF -> code-sign bypass pattern

  • Kernel IPC UAF (CVE-2023-41992): The second stage, still running inside the Safari context, triggers a kernel use-after-free in IPC code, re-allocates the freed object from userland, and abuses the dangling pointers to pivot into arbitrary kernel read/write. The stage also reuses PAC bypass material previously computed by JSKit instead of re-deriving it.
  • Code-signing bypass (CVE-2023-41991): With kernel R/W available, the exploit patches the trust cache / code-signing structures so unsigned payloads execute as system. The stage then exposes a lightweight kernel R/W service to later payloads.
  • Composed pattern: This chain demonstrates a reusable recipe that defenders should expect going forward:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager

PREYHUNTER helper & watcher modules

  • Watcher anti-analysis: एक समर्पित watcher binary लगातार डिवाइस का प्रोफाइल बनाता है और जब एक रिसर्च वातावरण पाया जाता है तो kill-chain को abort कर देता है। यह security.mac.amfi.developer_mode_status, diagnosticd कंसोल की उपस्थिति, locales US या IL, jailbreak के निशान जैसे Cydia, प्रक्रियाएँ जैसे bash, tcpdump, frida, sshd, या checkrain, मोबाइल AV ऐप्स (McAfee, AvastMobileSecurity, NortonMobileSecurity), कस्टम HTTP proxy सेटिंग्स, और कस्टम root CAs की जाँच करता है। किसी भी जांच में विफल होने पर आगे की payload डिलीवरी ब्लॉक कर दी जाती है।

  • Helper surveillance hooks: helper component अन्य चरणों से /tmp/helper.sock के माध्यम से संवाद करता है, फिर DMHooker और UMHooker नामक hook सेट लोड करता है। ये hooks VOIP audio paths को टैप करते हैं (रिकॉर्डिंग /private/var/tmp/l/voip_%lu_%u_PART.m4a के अंतर्गत आती हैं), एक system-wide keylogger लागू करते हैं, बिना UI के फोटो कैप्चर करते हैं, और SpringBoard को hook करके उन क्रियाओं से सामान्यतः उठने वाले notifications को दबा देते हैं। इसलिए helper भारी implants जैसे Predator गिराने से पहले एक stealthy validation + light-surveillance लेयर के रूप में काम करता है।

  • HiddenDot indicator suppression in SpringBoard: kernel-level code injection के साथ, Predator SBSensorActivityDataProvider._handleNewDomainData: (sensor activity का aggregation point) को hook करता है। यह hook Objective-C self pointer (x0) को zero कर देता है ताकि कॉल [nil _handleNewDomainData:newData] बन जाए, जिससे camera/mic अपडेट्स गिर जाती हैं और दोनों green/orange dots दबा दिए जाते हैं।

  • Mach exception-based hooking flow (DMHooker): Hooks EXC_BREAKPOINT + exception ports के माध्यम से लागू किए जाते हैं, फिर thread_set_state रजिस्टरों को बदलता है और execution resume होता है। Return code 2 का मतलब है “continue with modified thread state.”

  • PAC-aware redirection for camera access checks: mediaserverd में, एक pattern-scan (उदा., memmem) CMCapture.framework के अंदर FigVideoCaptureSourceCreateWithSourceInfo के पास एक private routine ढूँढता है। Hook 3 लौटाता है ताकि pre-signed PAC cached return address का उपयोग करके redirect किया जा सके, जिससे PAC संतुष्ट होता है जबकि चेक बायपास हो जाता है।

  • VoIP capture pipeline in mediaserverd: AudioConverterNew और AudioConverterConvertComplexBuffer+52 को hook करके buffers को टैप किया जाता है, buffer sizes से sample rate का अनुमान लगाया जाता है, float32 PCM → int16 NEON के साथ कनवर्ट किया जाता है, 4-channel को stereo में downmix किया जाता है, और ExtAudioFileWrite() के जरिए persist किया जाता है। VoIP module स्वयं indicators को suppress नहीं करता, इसलिए operators को HiddenDot अलग से सक्षम करना होगा।

WebKit DFG Store-Barrier UAF + ANGLE PBO OOB (iOS 26.1)

Webkit Dfg Store Barrier Uaf Angle Oob

iMessage/Media Parser Zero-Click Chains

Imessage Media Parser Zero Click Coreaudio Pac Bypass

संदर्भ

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें