cfw: extend IOMobileFramebuffer SwapEnd display fix to iOS 18.x

iOS 18.6.2 userland on the 26.1 vphone600 kernel hits the same
IOMobileFramebuffer SwapEnd ABI mismatch as 26.0/26.0.1: userland sends
a smaller external-method-5 SwapEnd state than the 0x560 the userclient
expects, so SwapEnd returns kIOReturnBadArgument and the host VZ display
stays black (the guest still renders — the Apple logo is visible over
VNC, just not in the vphone-cli view). 18.6.2 sends 0x514 (26.0 sent
0x548).

cfw_patch_iomfb_swapend is already semantic + idempotent (it discovers
the source immediate and rewrites it to 0x560), so no patcher change is
needed — only the install-time gate. Extend the gate in cfw_install.sh
and cfw_install_dev.sh to fire when ProductVersion starts with 18. as
well as 26.0.

Scoped to those versions only: 26.1/26.3/26.4/26.5 match neither branch
of the gate and are unaffected. Document the widened scope in
research/0_binary_patch_comparison.md (patch row 9).

Part of ongoing iPhone17,3 18.6.2 + cloudOS 26.1 bring-up. Validated on
17,3_18.6.2_22G100: the Apple logo now renders in the vphone-cli view.
Full boot to the UI additionally needs two kernelcache fixes (keystore
sel-135 force-success and the mach-port EXC_GUARD disable) that are
validated at runtime but not yet folded into KernelPatcher — those will
be gated to iOS 18 bases so the working 26.x variants stay untouched.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
zqxwce
2026-07-13 15:40:44 +03:00
committed by zqxwce
co-authored by Claude Fable 5
parent aa39b7194d
commit a67e0f3939
3 changed files with 19 additions and 13 deletions
+1 -1
View File
@@ -172,7 +172,7 @@ do NOT execute these).
| 6 | Weak dylib load injection | `launchd` | Load short alias `/b` (copy of `launchdhook.dylib`) at launch. On by default; set `DISABLE_LAUNCHD_HOOK=1` to skip because this pid-1 hook path is boot-critical and has produced boot-analysis failures | - | Y | Y |
| 7 | cstring byte 5 mangle `'h' → 'X'` (`"kern.hv_vmm_present"``"kern.Xv_vmm_present"`) + per-page slot-hash re-attestation, BLACKLIST semantics — **EXP only** | DSC dylibs | Companion to EXP kernel rename (`KernelEXPPatcher.patchHvVmmRename`). The mangle is applied to every DSC dylib EXCEPT those in `DONT_PATCH_INSTALL_NAMES` (sign-in / device-likeness consumers, ~15 entries). Patched dylibs query `kern.Xv_vmm_present` and get the truthful 1 (graphics / accel passthrough). Blacklisted dylibs keep the original cstring, hit ENOENT on the renamed kernel, cache 0, lie about VM presence. On `codeSigningMonitor == 2` hardware the byte-mangle alone causes `CODESIGNING/Invalid Page` SIGKILL because TXM enforces per-page hashes; the re-attestation pass recomputes the SHA-256 slot in the chunk's `CS_CodeDirectory` for every modified 16 KiB page. See `scripts/patchers/cfw_dsc_codesign.py` and `cfw_patch_hv_vmm_dsc.py`. | - | - | - |
| 8 | (removed — was: standalone-binary mangle in 6 rootfs Mach-Os via SSH) | n/a | Removed in the blacklist-flip redesign. With the EXP kernel rename in place, the 6 rootfs binaries (MobileActivationMigrator, CheckerBoard, StoreKitUISceneService, storekitd, appstored, CorePrescriptionService) get the desired "cache 0 / not in a VM" behavior for free: they keep their original cstring, hit ENOENT on the renamed kernel sysctl, defensive `cbnz w0, skip` leaves the cached byte at BSS-zero. No SSH-time standalone patch needed. | - | - | - |
| 9 | `mov w3,#0x548` -> `mov w3,#0x560` in `_kern_SwapEnd`**26.0 and 26.0.1 only** | DSC `IOMobileFramebuffer` | Fixes 26.0/26.0.1 GUI black-screen with the available PCC vphone600 userclient: 26.0 and 26.0.1 send a 0x548-byte external-method-5 SwapEnd state, while the userclient expects the 26.1-era 0x560 layout and otherwise returns `kIOReturnBadArgument`. The patch changes only this immediate in the installed 26.0 or 26.0.1 dyld cache and re-attests the modified DSC page. Validated after JB host install on `17,3_26.0_23A341` and `17,3_26.0.1_23A355`; 26.1 remains unmodified by this patch. | Y | Y | Y |
| 9 | `mov w3,#<size>` -> `mov w3,#0x560` in `_kern_SwapEnd`**26.0/26.0.1 and 18.x** | DSC `IOMobileFramebuffer` | Fixes host VZ GUI black-screen with the available PCC vphone600 userclient: userland sends a smaller external-method-5 SwapEnd state than the 26.1-era 0x560 the userclient expects, so SwapEnd returns `kIOReturnBadArgument` and the host display stays black (guest still renders — the Apple logo is visible over VNC, just not in the vphone-cli view). Source sizes observed: 26.0/26.0.1 = 0x548, 18.6.2 = 0x514. The patcher is semantic (anchors on `mov w1,#5` -> `mov w3,#imm` -> `mov x4,#0`/`mov x5,#0` -> `bl` inside `_kern_SwapEnd`) and idempotent — rewrites the size to 0x560 regardless of source and re-attests the modified DSC page. Install gate fires when `ProductVersion` starts with `26.0` or `18.`. Validated after host install on `17,3_26.0_23A341`, `17,3_26.0.1_23A355`, and `17,3_18.6.2_22G100` (Apple logo now renders in the vphone-cli view); 26.1 remains unmodified. | Y | Y | Y |
### Installed Components
+9 -6
View File
@@ -283,13 +283,16 @@ else
echo " [+] Cryptex installed"
fi
# iOS 26.0 and 26.0.1 IOMobileFramebuffer send the older 0x548-byte SwapEnd
# payload, but the PCC vphone600 userclient rejects anything below the
# 26.1-era 0x560 layout. Patch only that immediate in the installed 26.0 or 26.0.1
# DSC; do not replace frameworks or normalize GPU metadata.
# Some userland versions send an IOMobileFramebuffer SwapEnd state smaller than
# the 26.1-era 0x560 the PCC vphone600 userclient expects, so SwapEnd returns
# kIOReturnBadArgument and the host VZ display stays black (guest still renders;
# visible over VNC). Known: 26.0/26.0.1 send 0x548, 18.x sends 0x514 (18.6.2).
# Patch only that immediate in the installed DSC; do not replace frameworks or
# normalize GPU metadata. The patcher is semantic + idempotent (rewrites the
# SwapEnd size to 0x560, no-op if already 0x560).
IOS_VERSION=$(/usr/bin/plutil -extract ProductVersion raw -o - "$MNT1/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null || true)
if [[ "$IOS_VERSION" == 26.0* ]]; then
echo " [*] Patching 26.0/26.0.1 IOMobileFramebuffer SwapEnd payload size..."
if [[ "$IOS_VERSION" == 26.0* || "$IOS_VERSION" == 18.* ]]; then
echo " [*] Patching IOMobileFramebuffer SwapEnd payload size (iOS $IOS_VERSION -> 0x560)..."
DSC_DIR="$MNT1/System/Cryptexes/OS/System/Library/Caches/com.apple.dyld"
[[ -d "$DSC_DIR" ]] || die "dyld cache dir missing: $DSC_DIR"
"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-iomfb-swapend "$DSC_DIR"
+9 -6
View File
@@ -300,13 +300,16 @@ safe_detach "$MNT_APPOS"
echo " [+] Cryptex installed"
# iOS 26.0 and 26.0.1 IOMobileFramebuffer send the older 0x548-byte SwapEnd
# payload, but the PCC vphone600 userclient rejects anything below the
# 26.1-era 0x560 layout. Patch only that immediate in the installed 26.0 or 26.0.1
# DSC; do not replace frameworks or normalize GPU metadata.
# Some userland versions send an IOMobileFramebuffer SwapEnd state smaller than
# the 26.1-era 0x560 the PCC vphone600 userclient expects, so SwapEnd returns
# kIOReturnBadArgument and the host VZ display stays black (guest still renders;
# visible over VNC). Known: 26.0/26.0.1 send 0x548, 18.x sends 0x514 (18.6.2).
# Patch only that immediate in the installed DSC; do not replace frameworks or
# normalize GPU metadata. The patcher is semantic + idempotent (rewrites the
# SwapEnd size to 0x560, no-op if already 0x560).
IOS_VERSION=$(/usr/bin/plutil -extract ProductVersion raw -o - "$MNT1/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null || true)
if [[ "$IOS_VERSION" == 26.0* ]]; then
echo " [*] Patching 26.0/26.0.1 IOMobileFramebuffer SwapEnd payload size..."
if [[ "$IOS_VERSION" == 26.0* || "$IOS_VERSION" == 18.* ]]; then
echo " [*] Patching IOMobileFramebuffer SwapEnd payload size (iOS $IOS_VERSION -> 0x560)..."
DSC_DIR="$MNT1/System/Cryptexes/OS/System/Library/Caches/com.apple.dyld"
[[ -d "$DSC_DIR" ]] || die "dyld cache dir missing: $DSC_DIR"
"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-iomfb-swapend "$DSC_DIR"