IOMFB kern SwapEnd Patch Differences: iOS 26.x vs iOS 27.x Explained
iOS 26.x requires only the JB-26 variable-size dispatch patch, while iOS 27.x needs three coordinated patches—JB-26, JB-27 handler-size modification, and a force-kern trampoline redirect—to handle the larger 0x6e0-byte IOMFBSwapRec structure.
Virtualization of iOS through projects like Lakr233/vphone-cli requires careful binary patching of the IOMobileFramebuffer (IOMFB) SwapEnd user-client to bridge struct size mismatches between guest userland and host kernels. The primary keyword IOMFB kern SwapEnd patches iOS 26 vs 27 highlights a fundamental architectural divergence in how these versions handle framebuffer presentation, necessitating different patching strategies for each major release.
The Root Cause: IOMFBSwapRec Size Discrepancy
The IOMFB SwapEnd user-client serves as the gateway through which the guest iOS VM presents its framebuffer to the host. The kernel and userland must agree on the size of the IOMFBSwapRec structure, but this size changed between major versions:
- iOS 26.x: Native structure size of 0x588 bytes
- iOS 27.x: Native structure size of 0x6e0 bytes
This discrepancy triggers two distinct kernel-side gates that must be bypassed or modified for successful virtualization.
iOS 26.x: Single Dispatch Gate Patch (JB-26)
For iOS 26.x, only one size verification gate requires modification. The IOExternalMethodDispatch.checkStructureInputSize field in the dispatch table enforces the 0x588-byte expectation.
According to the source in sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchIomfbSwap.swift, the patchIomfbSwapEndVariableSize() method (designated JB-26) rewrites this field to kIOUCVariableStructureSize (0xffffffff). This allows any struct size greater than or equal to 0x588, accommodating both native 26.x clients and future structures without further kernel modification.
No handler-level patching is required for iOS 26.x because the kernel expects the 0x588 size that the guest provides.
iOS 27.x: Three-Layer Patching Strategy
iOS 27.x introduces additional complexity through a new "virt" display path and a stricter handler-level size check. Three coordinated patches are required:
1. Variable-Size Dispatch Gate (JB-26)
As with iOS 26.x, the dispatch table gate must be relaxed. The same patchIomfbSwapEndVariableSize() function sets checkStructureInputSize to 0xffffffff, making the dispatch entry size-agnostic.
2. Handler Size Gate (JB-27)
iOS 27.x introduces a secondary gate within the handler itself. The kernel code contains a hardcoded comparison: cmp w2, #0x588, which branches to an error path if the size does not match exactly.
The patchIomfbSwapEndHandlerSize() method (designated JB-27), implemented in KernelJBPatchIomfbSwap.swift, rewrites this immediate value from 0x588 to 0x6e0. This allows the handler to accept the larger native structure that iOS 27 userland transmits.
3. Force-Kern Trampoline Redirect
Critically, iOS 27 introduced a new _virt_Swap* trampoline path that bypasses the SwapEnd user-client entirely. Even after relaxing both size gates, the kernel never reaches the handler because the display flow uses the "virt" implementations instead of the "_kern_*" methods.
The patchIomfbForceKern patch, implemented in scripts/patchers/cfw_patch_iomfb_force_kern.py, retargets the public _IOMobileFramebufferSwap* trampolines to jump directly to the _kern_* implementations. This forces the iOS 27 guest to call the user-client's method-5 path (SwapEnd) instead of the virt path, making the previous patches effective.
Source Code Implementation
The patch implementations are distributed across the vphone-cli repository:
sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchIomfbSwap.swift: Contains bothpatchIomfbSwapEndVariableSize()for JB-26 andpatchIomfbSwapEndHandlerSize()for JB-27.scripts/patchers/cfw_patch_iomfb_swapend.py: Exposes the size-gate patches to the command-line interface, allowing per-target size adjustments.scripts/patchers/cfw_patch_iomfb_force_kern.py: Provides the trampoline retargeting logic required exclusively for iOS 27 display handling.research/0_binary_patch_comparison.md: Documents the patch IDs (JB-26, JB-27, force-kern) and their strategic rationale.
Applying the Patches
Use the Swift API for programmatic patching:
let patcher = KernelJBPatcher(...)
// Required for both iOS 26.x and 27.x
patcher.patchIomfbSwapEndVariableSize()
// Required only for iOS 27.x
patcher.patchIomfbSwapEndHandlerSize()
Or use the Python CLI wrappers for dyld shared cache patching:
# iOS 26.x or 27.x - relax the dispatch size check (JB-26)
python3 scripts/patchers/cfw.py patch-iomfb-swapend "$DSC_DIR" --target-size 0xFFFFFFFF
# iOS 27.x only - rewrite handler compare (JB-27) and force kern path
python3 scripts/patchers/cfw.py patch-iomfb-swapend "$DSC_DIR" --target-size 0x6e0
python3 scripts/patchers/cfw.py patch-iomfb-force-kern "$DSC_DIR"
Summary
- iOS 26.x: Requires only
patchIomfbSwapEndVariableSize(JB-26) to set the dispatch gate to variable size (0xffffffff). - iOS 27.x: Requires three patches: the variable-size dispatch gate (JB-26), the
patchIomfbSwapEndHandlerSize(JB-27) to update the handler'scmpinstruction from0x588to0x6e0, and thepatchIomfbForceKerntrampoline redirect to bypass the new virt path. - The force-kern patch is essential for iOS 27 because the kernel otherwise bypasses the SwapEnd user-client entirely through the
_virt_Swap*trampolines. - All patches are implemented in
KernelJBPatchIomfbSwap.swift(Swift) and exposed viacfw_patch_iomfb_swapend.pyandcfw_patch_iomfb_force_kern.py(Python).
Frequently Asked Questions
Why does iOS 27 require a force-kern patch when iOS 26 does not?
iOS 27 introduced new _virt_Swap* trampolines that route display operations around the SwapEnd user-client entirely. Without patchIomfbForceKern, the kernel uses this virt path and never reaches the SwapEnd handler, rendering the size-gate patches irrelevant. iOS 26.x lacks this alternative path, so the user-client is always invoked naturally.
What are the exact IOMFBSwapRec structure sizes for each version?
iOS 26.x uses a native structure size of 0x588 bytes, while iOS 27.x expanded this to 0x6e0 bytes. The handler in iOS 27 still contains the old comparison value (0x588) hardcoded in its assembly, necessitating the JB-27 patch to update this immediate value.
Where are these patches implemented in the vphone-cli codebase?
Both size-gate patches reside in sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchIomfbSwap.swift. The force-kern trampoline patch is implemented in scripts/patchers/cfw_patch_iomfb_force_kern.py, and the command-line interface for applying these patches is provided by scripts/patchers/cfw_patch_iomfb_swapend.py.
Can I skip the handler-size patch on iOS 27 if I only use the force-kern redirect?
No. The force-kern redirect ensures the kernel reaches the SwapEnd handler, but the handler itself enforces an exact size check via cmp w2, #0x588. Without patchIomfbSwapEndHandlerSize rewriting this to 0x6e0, the handler rejects the larger iOS 27 structure and returns an error, preventing framebuffer presentation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →