mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-09-02 02:34:29 +00:00
kernel: jb: Add container-manager exec-upcall force-success (iOS 27 wallpaper/temporary-sandbox fix)
The 26.4 vphone600 kernel resolves each process's containers at exec via a synchronous MIG upcall to containermanagerd over the container-manager host special port (HOST_CONTAINERD_PORT). iOS 27 DELETED this kernel-side upcall (the stock 27 kernel has no HOST_CONTAINERD_PORT / CM_KERN_* protocol — container resolution moved out of the kernel), so 27's containermanagerd no longer implements the reply server. On the 26.4 kernel running a 27.0 userland the upcall therefore fails (MACH_SEND_INVALID_DEST) for every platform app -> they are autoboxed into the restrictive `temporary-sandbox` profile, which denies mach-lookup com.apple.backboard.display.services -> Campo (the wallpaper renderer) crash-loops (no wallpaper), plus intelligencetasksd / feedbackd. Re-registering the host special port is a confirmed dead end: the 26.4 kernel then SENDS the MIG request and BLOCKS for a reply 27 cannot produce -> early-boot deadlock (SpringBoard never comes up). Fix (patchContainerManagerUpcall): flip the `cbz w0,<success>` guard in _hook_cred_label_update_execve (taken when the upcall returns 0) to an unconditional `b <success>`, so a failed upcall takes the success path instead of autobox/kill. Anchored structurally on the "failed to upcall to containermanagerd" string xref (-> the cbz immediately before the string-load adrp, preceded by the upcall bl, unique backward branch); replacement b from the Keystone-backed ARM64Encoder. No-op-in-effect for version-matched userlands (there the upcall succeeds, so the original cbz already branches to <success>). - new: sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchContainerUpcall.swift - KernelJBPatcher.findAll: call patchContainerManagerUpcall after patchExecSecurityPolicyKill - cfw_install.sh: document the port-25 launchd-cache approach as a dead end (no active code) - research/0_binary_patch_comparison.md: add row JB-02d Validated on-device (iPhone17,3 27.0 24A5380h + cloudOS 26.4 c0ecdb4b, JB): iOS 27 wallpaper renders, Campo/SpringBoard/backboardd stable, clean boot (no freeze, no panic). Patcher-level (patch-component --component kernel-jb on the pristine 26.4 kernelcache): finds 0x1AB0654 cbz w0 -> b 0x1ab0564, emits record container_manager_upcall_force_success, byte-identical to the on-device-validated host-poke. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_013pk5tsoBeuu3jhkmnRFtic
This commit is contained in:
committed by
zqxwce
co-authored by
Claude Opus 4.8
parent
9c1227f89b
commit
84f4887056
@@ -127,6 +127,7 @@
|
||||
| JB-01 | A | `patch_amfi_cdhash_in_trustcache` | `AMFIIsCDHashInTrustCache` | Always return true + store hash | Y |
|
||||
| JB-02 | A | `patch_amfi_execve_kill_path` | AMFI execve kill return site | Convert shared kill return from deny to allow (superseded by C21; standalone only) | N |
|
||||
| JB-02b| C | `patch_exec_security_policy_kill` | XNU exec `imgp->ip_mac_return` gate (kern_exec, `os_reason_create(OS_REASON_EXEC, EXEC_EXIT_REASON_SECURITY_POLICY)` site) | Flip `cbz wN, <skip>` → unconditional `b <skip>` so the exec-time MAC-verdict `SECURITY_POLICY` kill is unreachable. **Needed to run a userland NEWER than the kernel (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` → core daemons (backboardd/cfprefsd/containermanagerd/…) die at exec (`namespace 9 / code 0x8`) → boot deadlock. Validated on 27.0/26.4: 0 SECURITY_POLICY kills, daemons launch, networking + SSH come up. No-op-in-effect for version-matched userlands (ip_mac_return == 0 there, so the original cbz already skips). | Y |
|
||||
| JB-02d| C | `patch_container_manager_upcall` | sandbox `_hook_cred_label_update_execve` container-manager upcall guard: the `cbz w0,<success>` after `bl <container_manager_get_process_containers>`, on the `"failed to upcall to containermanagerd for a platform app"` fall-through | Flip `cbz w0,<success>` → unconditional `b <success>` so a FAILED exec-time container-manager upcall takes the success path instead of autobox/kill. **Needed to run iOS 27.0 on the 26.4 kernel:** iOS 27 DELETED the kernel-side containermanagerd upcall — the stock 27 kernel has no `HOST_CONTAINERD_PORT` / `CM_KERN_*` protocol (container resolution moved out of the kernel), so 27's containermanagerd no longer implements the reply server. On the 26.4 kernel the exec upcall therefore fails (`MACH_SEND_INVALID_DEST`) for every 27 platform app → they are autoboxed into the restrictive `temporary-sandbox` profile, which denies e.g. `mach-lookup com.apple.backboard.display.services` → Campo (the wallpaper renderer) crash-loops (no wallpaper) + intelligencetasksd/feedbackd. Re-registering the container-manager host special port is NOT viable: the 26.4 kernel then SENDS the synchronous `CM_KERN` MIG request and BLOCKS for a reply 27 cannot produce → early-boot deadlock (verified: boot hangs, never reaches SpringBoard). Anchor is structural (string xref to `"failed to upcall to containermanagerd"` → the `cbz w0` immediately preceding the string-load `adrp`, itself immediately preceded by the upcall `bl`; backward branch to the success continuation; unique). Replacement `b` from the Keystone-backed `ARM64Encoder`. **VALIDATED on-device (2026-07-15, `17,3_27.0_24A5380h` + cloudOS 26.4 `c0ecdb4b…`, JB): iOS 27 wallpaper renders, Campo/SpringBoard/backboardd stable, boot clean (no freeze, no panic).** No-op-in-effect for version-matched userlands (there the upcall succeeds → the original cbz already branches to `<success>`). | Y |
|
||||
| JB-03 | C | `patch_cred_label_update_execve` | `_cred_label_update_execve` | Reworked C21-v3: C21-v1 already boots; v3 keeps split late exits and additionally ORs success-only helper bits `0xC` after clearing `0x3F00`; still disabled pending boot validation | N |
|
||||
| JB-04 | C | `patch_hook_cred_label_update_execve` | sandbox `mpo_cred_label_update_execve` wrapper (`ops[18]` -> `sub_FFFFFE00093BDB64`) | Faithful upstream C23 trampoline: copy `VSUID`/`VSGID` owner state into pending cred, set `P_SUGID`, then branch back to wrapper | Y |
|
||||
| JB-05 | C | `patch_kcall10` | `sysent[439]` (`SYS_kas_info` replacement) | Rebuilt ABI-correct kcall cave: `target + 7 args -> uint64 x0`; re-enabled after focused dry-run validation | Y |
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// KernelJBPatchContainerUpcall.swift — JB kernel patch: force the sandbox exec-time
|
||||
// container-manager upcall onto its success path (neutralize the autobox / kill).
|
||||
//
|
||||
// At exec, the sandbox hook `_hook_cred_label_update_execve` resolves each process's
|
||||
// containers by a SYNCHRONOUS MIG upcall to containermanagerd over the container-manager
|
||||
// host special port:
|
||||
//
|
||||
// w0 = container_manager_get_process_containers(...) // 0 = success
|
||||
// if (w0 != 0) { // upcall failed
|
||||
// os_log("... failed to upcall to containermanagerd for a platform app");
|
||||
// ... // KILL the process, or autobox it into the restrictive
|
||||
// // `temporary-sandbox` profile
|
||||
// }
|
||||
// // success path: use the returned container-derived sandbox
|
||||
//
|
||||
// iOS 27 DELETED this kernel-side upcall entirely (the stock iOS 27 kernel has no
|
||||
// HOST_CONTAINERD_PORT / CM_KERN_* protocol — container resolution moved out of the
|
||||
// kernel), so 27's containermanagerd no longer implements the reply server. On the 26.4
|
||||
// vphone600 kernel running a 27.0 userland, the upcall therefore fails
|
||||
// (MACH_SEND_INVALID_DEST) for every platform app → they are autoboxed into
|
||||
// `temporary-sandbox`, which denies e.g. `mach-lookup com.apple.backboard.display.services`
|
||||
// → Campo (the wallpaper renderer) crash-loops (no wallpaper), plus intelligencetasksd /
|
||||
// feedbackd. Re-registering the host special port is NOT an option: it makes the kernel
|
||||
// SEND the MIG request and BLOCK for a reply 27 cannot produce → early-boot deadlock.
|
||||
//
|
||||
// Fix: flip the `cbz w0, <success>` guard (taken when the upcall returns 0) to an
|
||||
// unconditional `b <success>`, so a failed upcall takes the same path as a successful one
|
||||
// instead of autobox/kill. Safe for version-matched userlands: there the upcall succeeds
|
||||
// (w0 == 0), so the original cbz already branches to <success> — the unconditional b is
|
||||
// behaviourally identical (no-op in effect). Validated on-device: iOS 27.0 wallpaper
|
||||
// renders, Campo/SpringBoard/backboardd stable, boot clean (no freeze, no panic).
|
||||
//
|
||||
// Anchor (structural, no hardcoded offsets/bytes): the os_log format string
|
||||
// "failed to upcall to containermanagerd for a platform app" is loaded (adrp+add) on the
|
||||
// failure fall-through; the guard is the `cbz w0, <back-branch>` immediately preceding
|
||||
// that load, itself immediately preceded by the `bl <container upcall>`. The branch is
|
||||
// backward (the success continuation precedes the call site). Replacement bytes come from
|
||||
// the Keystone-backed ARM64Encoder.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension KernelJBPatcher {
|
||||
@discardableResult
|
||||
func patchContainerManagerUpcall() -> Bool {
|
||||
log("\n[JB] container-manager exec upcall: cbz w0 -> b (force success; skip autobox/temporary-sandbox)")
|
||||
|
||||
guard let strOff = buffer.findString("failed to upcall to containermanagerd") else {
|
||||
log(" [-] 'failed to upcall to containermanagerd' string not found")
|
||||
return false
|
||||
}
|
||||
|
||||
// The string ref (adrp+add loading the format string) sits on the failure
|
||||
// fall-through. The guard we flip is the cbz two instructions before the adrp:
|
||||
// bl <container upcall> ; adrpOff - 8
|
||||
// cbz w0, <success> ; adrpOff - 4 <- patch to `b <success>`
|
||||
// adrp xN, "failed to..." ; adrpOff <- the string ref
|
||||
var hits: [(cbzOff: Int, target: Int)] = []
|
||||
for ref in findStringRefs(strOff) {
|
||||
let adrpOff = ref.adrpOff
|
||||
let cbzOff = adrpOff - 4
|
||||
let blOff = adrpOff - 8
|
||||
guard blOff >= 0 else { continue }
|
||||
guard let cbz = disasAt(cbzOff), cbz.mnemonic == "cbz",
|
||||
cbz.operandString.hasPrefix("w0"), // upcall status is a 32-bit w0
|
||||
let bl = disasAt(blOff), bl.mnemonic == "bl" // the container upcall call
|
||||
else { continue }
|
||||
|
||||
// Decode the cbz forward/backward 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
|
||||
// The success continuation precedes the upcall call site (backward branch),
|
||||
// and must land in executable code.
|
||||
if target < cbzOff, codeRanges.contains(where: { target >= $0.start && target < $0.end }) {
|
||||
hits.append((cbzOff, target))
|
||||
}
|
||||
}
|
||||
|
||||
guard hits.count == 1 else {
|
||||
log(" [-] container-manager upcall guard not found uniquely (found \(hits.count))")
|
||||
return false
|
||||
}
|
||||
|
||||
let (cbzOff, target) = hits[0]
|
||||
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: "container_manager_upcall_force_success",
|
||||
virtualAddress: va,
|
||||
description: "cbz w0 -> b [force container-manager exec upcall success; skip autobox/temporary-sandbox]"
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -65,33 +65,27 @@ public final class KernelJBPatcher: KernelJBPatcherBase, Patcher {
|
||||
patchKcall10()
|
||||
patchSyscallmaskApplyToProc()
|
||||
|
||||
// Neutralize the exec-time ip_mac_return SECURITY_POLICY kill so a userland
|
||||
// newer than the kernel (iOS 27 on the 26.4 kernel) can launch: AMFI's exec
|
||||
// hooks reject the newer binaries' validation category → ip_mac_return != 0 →
|
||||
// core daemons (backboardd, cfprefsd, ...) die at exec → boot deadlock.
|
||||
// No-op-in-effect for version-matched userlands (ip_mac_return == 0 there).
|
||||
patchExecSecurityPolicyKill()
|
||||
|
||||
// iOS 27 VZ-view (host paravirt-GPU scanout) fix — kernel half of the
|
||||
// "force the kern present path" pair:
|
||||
//
|
||||
// The 26.4 kernel's paravirt GPU only scans a frame out to the host when
|
||||
// the guest presents via the IOMFB userclient SwapEnd (external method 5).
|
||||
// iOS 27 defaults the paravirt display's present to IOMFB's parallel
|
||||
// `_virt_*` path (an in-process callback that never enters the userclient),
|
||||
// so the paravirt GPU never scans out → host VZ window is black (the guest
|
||||
// still composites; visible over in-guest TrollVNC). cfw_patch_iomfb_force_kern
|
||||
// (userland half) retargets IOMFB's public `_IOMobileFramebufferSwap*`
|
||||
// trampolines to `_kern_Swap*`, forcing present back onto method 5. But
|
||||
// iOS 27's native SwapEnd struct is 0x6e0 bytes (26.x sent 0x588), and the
|
||||
// 26.4 userclient exact-checks 0x588 in TWO places, so method 5 would return
|
||||
// kIOReturnBadArgument. These two patches relax both size gates to accept
|
||||
// 27's 0x6e0 (its IOMFBSwapRec prefix matches 26.x, so the paravirt swap
|
||||
// handler reads valid fields):
|
||||
// 1. dispatch-table checkStructureInputSize 0x588 → variable
|
||||
patchIomfbSwapEndVariableSize()
|
||||
// 2. the handler's internal `cmp w2,#0x588` gate → 0x6e0
|
||||
patchIomfbSwapEndHandlerSize()
|
||||
// iOS-27-only (gated — a 26.x base skips these entirely). All target a 27
|
||||
// userland on the 26.4 kernel and are unnecessary or actively harmful on 26.x:
|
||||
// - exec ip_mac_return SECURITY_POLICY kill bypass: AMFI's exec hooks reject a
|
||||
// userland newer than the kernel (27 binaries' validation category) →
|
||||
// ip_mac_return != 0 → core daemons die at exec → boot deadlock. 26.x
|
||||
// binaries pass (ip_mac_return == 0), so it is not needed there.
|
||||
// - container-manager exec-upcall force-success: iOS 27 deleted the kernel-side
|
||||
// containermanagerd upcall, so on the 26.4 kernel it fails for every 27 app →
|
||||
// autoboxed into temporary-sandbox → Campo/intelligencetasksd/feedbackd
|
||||
// crash-loop. On 26.x the upcall succeeds, so it is not needed.
|
||||
// - IOMFB SwapEnd size gates: 27's force-kern present (cfw_patch_iomfb_force_kern)
|
||||
// sends a 0x6e0 SwapEnd struct; the 26.4 userclient exact-checks 0x588 in two
|
||||
// places, so both gates are relaxed to accept 0x6e0. HARMFUL on 26.x — it
|
||||
// sends the native 0x588, which the retargeted handler gate would then reject
|
||||
// → every framebuffer swap fails → dead display (the 26.5 regression).
|
||||
if applyIOS27 {
|
||||
patchExecSecurityPolicyKill()
|
||||
patchContainerManagerUpcall()
|
||||
patchIomfbSwapEndVariableSize() // dispatch checkStructureInputSize → variable
|
||||
patchIomfbSwapEndHandlerSize() // handler cmp w2,#0x588 → 0x6e0
|
||||
}
|
||||
|
||||
return patches
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user