Files
vphone-cli/VPhoneExecutable/VPhoneCommand/FirmwarePatcher/Kernel/BasePatches/Security/KernelPatchExcGuard.swift
T
LakrandClaude Opus 5.5 ae5e4531a5 Name every patch {component}-{effect}-{name}
The 117 bundled patch identifiers had grown five naming schemes
(kernel.x, jb.x, kernelcache_jb.x, txm_dev.x, bare names). Each one is now
{component}-{effect}-{name}:

- component: avpbooter, ibss, ibec, llb, txm, kernel, devicetree, dyld,
  preboot, or system-<binary> for a guest binary or file.
- effect: boot when the patch is boot-essential, exp when the standard
  preset leaves it off, cfw otherwise. A catalog test enforces this.
- name: snake_case, no hyphen, so the identifier splits from the right.

Record sites are now always <identifier>.<site>. The underscore-prefix
rule in covers(recordIdentifier:) and in the gate is gone: the new names
contain underscores, so kernel-boot-post_validation would otherwise have
covered kernel-boot-post_validation_unsigned. The 25 records that relied
on it (amfi_trustcache_1, launch_constraints_mov, sandbox_ext_N, ...) now
use a dot.

Old identifiers are not migrated. A VM whose PatchPlan or PatchSelection
names one must be patched again. The bundle becomes 2.2.0 and Launchpad
requires 2.2.0, so it never meets an old identifier from a bundle.

Launchpad's patch table shows Component, Effect and Name columns in place
of Identifier and Patch Set; the set moves to the detail line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:20:20 +09:00

106 lines
4.9 KiB
Swift

// KernelPatchExcGuard.swift — Disable EXC_GUARD for Mach port guard violations.
//
// Research kernels enforce Mach port guard violations as fatal EXC_GUARD exceptions.
// Any app calling task_swap_exception_ports() on a guarded port is killed with
// EXC_GUARD. This commonly affects apps that install custom exception handlers.
// Production iOS kernels do not enforce these fatally.
//
// The enforcement path is: guard check → thread_guard_violation() → AST delivery.
// thread_guard_violation stores violation info in the thread struct and triggers an
// AST that delivers the fatal EXC_GUARD exception when the thread returns to userspace.
//
// Patch strategy: find thread_guard_violation via anchor chain and replace its first
// instruction with RET so it returns immediately without recording or delivering
// the violation. This disables ALL Mach port guard violations (acceptable for
// research VMs where guard enforcement is not needed).
//
// Anchor chain:
// 1. "com.apple.security.only-one-exception-port" string
// 2. → ADRP+ADD code ref in set_exception_behavior_allowed() [may be dead code]
// 3. → scan function body for BL to set_exception_behavior_violation()
// 4. → in that target, find BL to thread_guard_violation()
// 5. → patch thread_guard_violation prologue to RET
import Foundation
import VPhonePatchKit
extension KernelPatcher {
/// Disable Mach port guard violation enforcement (EXC_GUARD).
///
/// Patches `thread_guard_violation` to return immediately, preventing
/// all guard violations from being delivered as fatal exceptions.
@discardableResult
func patchExcGuardBehavior() -> Bool {
log("\n[26] exc_guard: disable thread_guard_violation")
// Step 1: locate the anchor string.
guard let strOff = buffer.findString("com.apple.security.only-one-exception-port") else {
log(" [-] anchor string not found")
return false
}
// Step 2: find ADRP+ADD code reference (inside set_exception_behavior_allowed).
let refs = findStringRefs(strOff)
guard let (_, addOff) = refs.first else {
log(" [-] no code ref to anchor string")
return false
}
// Step 3: scan the surrounding function for BL instructions to find
// set_exception_behavior_violation. It's the BL whose target starts with
// PACIBSP and contains a TBZ/TBNZ within the first ~10 instructions
// (the thid_should_crash check), followed by a BL (to thread_guard_violation).
// Scan a wide window around the string ref (the function may be ~600 bytes)
let scanStart = max(0, addOff - 200)
let scanEnd = min(buffer.count - 4, addOff + 400)
for off in stride(from: scanStart, to: scanEnd, by: 4) {
guard let target = decodeBL(at: off) else { continue }
guard target > 0, target + 40 <= buffer.count else { continue }
// Check if target starts with PACIBSP
guard buffer.readU32(at: target) == ARM64.pacibspU32 else { continue }
// Check if target contains TBZ/TBNZ within first 20 instructions
// followed by a BL (pattern of set_exception_behavior_violation)
var hasTbCheck = false
var innerBLTarget: Int? = nil
for delta in stride(from: 4, through: 20 * 4, by: 4) {
let ioff = target + delta
guard ioff + 4 <= buffer.count else { break }
let iraw = buffer.readU32(at: ioff)
// TBZ/TBNZ: [30:25] = 01101x
if (iraw & 0x7E00_0000) == 0x3600_0000 {
hasTbCheck = true
}
// After TBZ, look for BL
if hasTbCheck, ARM64Inst.isBL(iraw) {
let iimm26 = iraw & 0x03FF_FFFF
let isigned = Int32(bitPattern: iimm26 << 6) >> 6
let bt = ioff + Int(isigned) * 4
if bt > 0, bt + 4 <= buffer.count,
buffer.readU32(at: bt) == ARM64.pacibspU32
{
innerBLTarget = bt
}
break
}
}
if let inner = innerBLTarget {
log(" [*] set_exception_behavior_violation at foff 0x\(String(format: "%X", target))")
log(" [*] thread_guard_violation at foff 0x\(String(format: "%X", inner))")
// Step 4: patch thread_guard_violation → RET
let va = fileOffsetToVA(inner)
emit(
inner,
ARM64.ret,
patchID: "kernel-boot-thread_guard_violation",
virtualAddress: va,
description: "PACIBSP→RET (disable guard violation delivery)",
)
return true
}
}
log(" [-] thread_guard_violation not found via anchor chain")
return false
}
}