Files
vphone-cli/sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchExecPolicyKill.swift
T
zqxwceandClaude Opus 4.8 679d3d0476 kernel: jb: Bypass exec ip_mac_return SECURITY_POLICY kill for newer userlands
Running iOS 27.0 userland on the 26.4 vphone600 kernel, every core platform
daemon (backboardd, cfprefsd, containermanagerd, locationd, CommCenter, ...)
died 3-5ms after xpcproxy spawn with exit reason namespace 9 / code 0x8
(OS_REASON_EXEC / EXEC_EXIT_REASON_SECURITY_POLICY). launchd throttled the
respawns and the boot deadlocked (all CPUs idle) before SpringBoard — no UI,
no networking. Root cause: AMFI's exec MAC hooks reject the 27.0 binaries'
code-sign validation category on the 26.4 kernel, setting imgp->ip_mac_return,
and XNU's exec path (kern_exec.c) SIGKILLs on `if (imgp->ip_mac_return != 0)`.

patch_exec_security_policy_kill flips the `cbz wN, <skip>` guard preceding the
os_reason_create(9,8) call to an unconditional `b <skip>`, making the kill block
unreachable — downstream of AMFI/TXM, so it covers the validation-category
reject regardless of which hook set the verdict. Anchored structurally (movz
w0,#9 ; movz w1,#8 preceded by ldr wN,[xM,#imm] ; cbz wN,<fwd>; W-register cbz
distinguishes the ip_mac_return site from the subsystem-root sibling). No-op in
effect for version-matched userlands (ip_mac_return == 0, so the cbz already
skips). Wired into the JB Group C dispatcher.

Validated on iPhone17,3_27.0_24A5380h + cloudOS 26.4 (c0ecdb4b): 0 SECURITY_POLICY
kills, core daemons launch, networking + SSH (dropbear :22222) come up. Remaining
gate: sandbox denies backboardd the IOMobileFramebuffer/IOSurface user clients,
so SpringBoard traps in FBSDisplayMonitor init (no display) -> UI not up yet.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_013pk5tsoBeuu3jhkmnRFtic
2026-07-20 16:20:34 +03:00

102 lines
4.2 KiB
Swift

// KernelJBPatchExecPolicyKill.swift — JB kernel patch: neutralize the exec-time
// MAC-verdict (ip_mac_return) security-policy kill.
//
// After the MAC exec hooks run, XNU's exec path (kern_exec.c) checks:
//
// if (imgp->ip_mac_return != 0) {
// ... os_reason_create(OS_REASON_EXEC, EXEC_EXIT_REASON_SECURITY_POLICY);
// error = imgp->ip_mac_return;
// goto done; // SIGKILL the new process at exec
// }
//
// When running a userland NEWER than the vphone600 kernel (e.g. iOS 27.0 on the
// 26.4 kernel), AMFI's exec hooks reject the newer binaries' code-sign validation
// category, setting ip_mac_return != 0. Every core platform daemon (backboardd,
// cfprefsd, containermanagerd, ...) then dies at exec with
// EXEC_EXIT_REASON_SECURITY_POLICY (namespace 9 / code 0x8), launchd throttles the
// respawns, and the boot deadlocks (all CPUs idle) before SpringBoard/UI.
//
// Flip the `cbz wN, <skip>` guard immediately preceding the reason-create call to
// an unconditional `b <skip>`, so the kill block is unreachable. Safe for
// version-matched userlands too: there ip_mac_return is 0, so the original cbz
// already branches to <skip> — the unconditional b is behaviourally identical.
//
// Anchor (structural, no hardcoded offsets): the
// `os_reason_create(OS_REASON_EXEC=9, EXEC_EXIT_REASON_SECURITY_POLICY=8)` call —
// two adjacent `movz w0,#9 ; movz w1,#8` — preceded by
// `ldr wN,[xM,#imm] ; cbz wN, <forward>`. The ip_mac_return site uses a W-register
// cbz (distinguishing it from the sibling subsystem-root reject site, which cbz's
// an X register).
import Foundation
extension KernelJBPatcher {
@discardableResult
func patchExecSecurityPolicyKill() -> Bool {
log("\n[JB] exec ip_mac_return SECURITY_POLICY kill: cbz -> b (allow)")
guard let (ks, ke) = kernTextRange else {
log(" [-] no kernel text range")
return false
}
let movzW0_9: UInt32 = 0x5280_0120 // movz w0, #9 (OS_REASON_EXEC)
let movzW1_8: UInt32 = 0x5280_0101 // movz w1, #8 (EXEC_EXIT_REASON_SECURITY_POLICY)
var hits: [Int] = []
var off = ks
while off + 8 <= ke {
if buffer.readU32(at: off) == movzW0_9, buffer.readU32(at: off + 4) == movzW1_8 {
let cbzOff = off - 4
let ldrOff = off - 8
if cbzOff >= ks,
let cbz = disasAt(cbzOff), cbz.mnemonic == "cbz",
let ldr = disasAt(ldrOff), ldr.mnemonic == "ldr",
// W-register cbz == the ip_mac_return site (not the X-register
// subsystem-root sibling).
cbz.operandString.hasPrefix("w"),
ldr.operandString.hasPrefix("w")
{
// Decode the cbz's forward branch target (imm19 << 2).
let word = buffer.readU32(at: cbzOff)
let imm19 = Int((word >> 5) & 0x7FFFF)
let signed = imm19 >= (1 << 18) ? imm19 - (1 << 19) : imm19
let target = cbzOff + signed * 4
// Must be a forward branch that skips the reason-create/kill block.
if target > off + 8 {
hits.append(cbzOff)
}
}
}
off += 4
}
guard hits.count == 1 else {
log(" [-] exec ip_mac_return kill guard not found uniquely (found \(hits.count))")
return false
}
let cbzOff = hits[0]
// Re-decode the target for the emitted branch.
let word = buffer.readU32(at: cbzOff)
let imm19 = Int((word >> 5) & 0x7FFFF)
let signed = imm19 >= (1 << 18) ? imm19 - (1 << 19) : imm19
let target = cbzOff + signed * 4
guard let bBytes = ARM64Encoder.encodeB(from: cbzOff, to: target) else {
log(" [-] failed to encode B to 0x\(String(target, radix: 16))")
return false
}
let va = fileOffsetToVA(cbzOff)
emit(
cbzOff,
bBytes,
patchID: "exec_security_policy_kill",
virtualAddress: va,
description: "cbz -> b [exec ip_mac_return SECURITY_POLICY kill bypass]"
)
return true
}
}