Add scripts/cfw_install_jb_post.sh — an idempotent SSH-based finalizer to complete JB bootstrap on a normally-booted vphone (creates /var/jb symlink, fixes ownership, runs prep_bootstrap, creates markers, installs Sileo, and runs apt; requires sshpass). Add Makefile help, .PHONY and target cfw_install_jb_finalize to invoke the script. Remove host-side IPA signing/installing and related UI: delete VPhoneSigner, VPhoneIPAInstaller, VPhoneMenuInstall and remove signer/ipaInstaller fields and menu items/callbacks from the vphone-cli UI (also removed the DevMode enable WIP flow). Misc: minor table/formatting tweaks in AGENTS.md and research docs.
8.4 KiB
Binary Kernelcache Patch Verification Report
Date: 2026-02-27
Scope
Verify that the dynamic kernel patch finder (scripts/patchers/kernel.py) produces
the same binary result as the legacy hardcoded patch list on vphone600, then
apply the dynamic patcher to a freshly extracted vresearch101 kernelcache.
Inputs
- Original vphone600 raw kernel:
/tmp/kc_vphone600_orig.raw - vphone600 upstream hardcoded patch list:
super-tart-vphone-private/CFW/patch_fw.py - Dynamic patcher:
scripts/patchers/kernel.py - VM kernelcache image (vresearch101):
VM/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vresearch101
Method
- Apply legacy hardcoded patches to
/tmp/kc_vphone600_orig.rawusing binary replacement (32-bit writes) and save as/tmp/kc_vphone600_upstream.raw. - Run
KernelPatcher.find_all()on/tmp/kc_vphone600_orig.raw, apply all dynamic patches, and save as/tmp/kc_vphone600_dynamic.raw. - Compare the two patched binaries with
cmp -l. - Re-extract a clean vresearch101 kernelcache using
pyimg4 im4p extract, save as/tmp/kc_vresearch1_orig.raw. - Run the dynamic patcher on
/tmp/kc_vresearch1_orig.raw, save as/tmp/kc_vresearch1_dynamic.raw.
Outputs
/tmp/kc_vphone600_upstream.raw/tmp/kc_vphone600_dynamic.raw/tmp/kc_vresearch1_orig.raw/tmp/kc_vresearch1_dynamic.raw
Checksums (SHA-256)
/tmp/kc_vphone600_orig.raw:b6846048f3a60eab5f360fcc0f3dcb5198aa0476c86fb06eb42f6267cdbfcae0/tmp/kc_vphone600_upstream.raw:373e016d34ae5a2d8ba7ba96c920f4f6700dea503e3689d06a99e90ebec701c8/tmp/kc_vphone600_dynamic.raw:373e016d34ae5a2d8ba7ba96c920f4f6700dea503e3689d06a99e90ebec701c8/tmp/kc_vresearch1_orig.raw:c673c9b8226ea774d1d935427760e2e9a48200fd1daf0ef584dc88df0dccefde/tmp/kc_vresearch1_dynamic.raw:f36a78ce59c658df85ecdead56d46370a1107181689091cf798e529664f6e2b5
vphone600: Hardcoded vs Dynamic
Result: byte-identical output between hardcoded and dynamic patching.
KernelPatcherpatches found: 26- Hardcoded patches applied: 26
cmp -l /tmp/kc_vphone600_upstream.raw /tmp/kc_vphone600_dynamic.raw: no output (files identical)
Hardcoded Patch List (vphone600)
Offsets and 32-bit patch values, taken from patch_fw.py:
| # | Offset (hex) | Patch value | Purpose |
|---|---|---|---|
| 1 | 0x2476964 | 0xD503201F | _apfs_vfsop_mount root snapshot NOP |
| 2 | 0x23CFDE4 | 0xD503201F | _authapfs_seal_is_broken NOP |
| 3 | 0x00F6D960 | 0xD503201F | _bsd_init rootvp NOP |
| 4 | 0x163863C | 0x52800000 | _proc_check_launch_constraints mov w0,#0 |
| 5 | 0x1638640 | 0xD65F03C0 | _proc_check_launch_constraints ret |
| 6 | 0x12C8138 | 0xD2800020 | _PE_i_can_has_debugger mov x0,#1 |
| 7 | 0x12C813C | 0xD65F03C0 | _PE_i_can_has_debugger ret |
| 8 | 0x00FFAB98 | 0xD503201F | TXM post-validation NOP (tbnz) |
| 9 | 0x16405AC | 0x6B00001F | postValidation cmp w0,w0 |
| 10 | 0x16410BC | 0x52800020 | _check_dyld_policy_internal mov w0,#1 (1) |
| 11 | 0x16410C8 | 0x52800020 | _check_dyld_policy_internal mov w0,#1 (2) |
| 12 | 0x242011C | 0x52800000 | _apfs_graft mov w0,#0 |
| 13 | 0x2475044 | 0xEB00001F | _apfs_vfsop_mount cmp x0,x0 |
| 14 | 0x2476C00 | 0x52800000 | _apfs_mount_upgrade_checks mov w0,#0 |
| 15 | 0x248C800 | 0x52800000 | _handle_fsioc_graft mov w0,#0 |
| 16 | _handle_get_dev_by_role entitlement bypass | ||
| 17 | 0x23AC528 | 0xD2800000 | _hook_file_check_mmap mov x0,#0 |
| 18 | 0x23AC52C | 0xD65F03C0 | _hook_file_check_mmap ret |
| 19 | 0x23AAB58 | 0xD2800000 | _hook_mount_check_mount mov x0,#0 |
| 20 | 0x23AAB5C | 0xD65F03C0 | _hook_mount_check_mount ret |
| 21 | 0x23AA9A0 | 0xD2800000 | _hook_mount_check_remount mov x0,#0 |
| 22 | 0x23AA9A4 | 0xD65F03C0 | _hook_mount_check_remount ret |
| 23 | 0x23AA80C | 0xD2800000 | _hook_mount_check_umount mov x0,#0 |
| 24 | 0x23AA810 | 0xD65F03C0 | _hook_mount_check_umount ret |
| 25 | 0x23A5514 | 0xD2800000 | _hook_vnode_check_rename mov x0,#0 |
| 26 | 0x23A5518 | 0xD65F03C0 | _hook_vnode_check_rename ret |
TXM Patch Details
Dynamic patcher locates the "TXM [Error]: CodeSignature" string, finds the
following tbnz in the log/error path, and NOPs it.
vphone600 disassembly around the patch (0xFFAB98)
Before:
0x00FFAB90: mov w0, #5
0x00FFAB94: ldrb w8, [x19, #6]
0x00FFAB98: tbnz w8, #0, #0xffac80
0x00FFAB9C: ldp x29, x30, [sp, #0x100]
After:
0x00FFAB90: mov w0, #5
0x00FFAB94: ldrb w8, [x19, #6]
0x00FFAB98: nop
0x00FFAB9C: ldp x29, x30, [sp, #0x100]
vresearch101: Dynamic Patch Run
Extraction:
pyimg4 im4p extract \
-i VM/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vresearch101 \
-o /tmp/kc_vresearch1_orig.raw
Dynamic patcher results:
- Patches found/applied: 26
- TXM patch location:
0xFA6B98(NOPtbnz w8, #0, #0xfa6c80) - Patched output:
/tmp/kc_vresearch1_dynamic.raw
Conclusion
For vphone600, the dynamic patcher output is byte-identical to the legacy hardcoded patch list, indicating functional equivalence on this kernelcache. The same dynamic patcher also successfully patches the freshly extracted vresearch101 kernelcache with the expected TXM NOP and a full 26-patch set.
Targeted Re-Verification (2026-03-05)
Scope for this pass was limited to the user-requested kernel patches in
KernelPatcher.find_all() order:
- 6
patch_PE_i_can_has_debugger(mov x0,#1) - 7
patch_PE_i_can_has_debugger(ret) - 8
patch_post_validation_nop(NOP TXM post-validationtbnz) - 9
patch_post_validation_cmp(cmp w0,w0) - 10
patch_check_dyld_policy(BL -> mov w0,#1at@1)
Input kernel:
vm/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vphone600(IM4P payload decompressed in-memory; no file write during verification)
Regular and Development both use the same kernel patch entrypoint:
scripts/fw_patch.py->patch_kernelcache()->KernelPatcher.apply()scripts/fw_patch_dev.pyreusespatch_kernelcachefromfw_patch.py
Verification Results
| Item | Patch | File Offset | VA | Before | After | Status |
|---|---|---|---|---|---|---|
| 6 | _PE_i_can_has_debugger |
0x012C8138 |
0xFFFFFE00082CC138 |
adrp x8, ... |
mov x0, #1 |
OK |
| 7 | _PE_i_can_has_debugger |
0x012C813C |
0xFFFFFE00082CC13C |
cbz x0, ... |
ret |
OK |
| 8 | TXM post-validation | 0x00FFAB98 |
0xFFFFFE0007FFEB98 |
tbnz w8,#0,... |
nop |
OK |
| 9 | postValidation compare | 0x016405AC |
0xFFFFFE00086445AC |
cmp w0, #2 |
cmp w0, w0 |
OK |
| 10 (@1) | dyld policy | 0x016410BC |
0xFFFFFE00086450BC |
bl 0xFFFFFE0007FFD304 |
mov w0, #1 |
OK |
Structural Evidence (IDA + raw context)
- Patch 6/7 site is a dedicated tiny function entry at
0xFFFFFE00082CC138; patching first two instructions produces exactmov x0,#1; retstub. Call fanout observed by patcher histogram: 82 callers. - Patch 8 site is the
tbnzimmediately after the"TXM [Error]: CodeSignature"log call chain (ADRP+ADD string->BL->...->TBNZ), matching patch intent. - Patch 9 site is in AMFI code path with local pattern:
BL->CMP W0,#imm->B.NE; target instruction at0x...45ACiscmp w0,#2before replacement. - Patch 10 (@1) site is in the function containing
"com.apple.developer.swift-playgrounds-app.development-build"and matches the expectedBLfollowed by a conditional onW0; theBLat0x...50BCis replaced bymov w0,#1.
Conclusion (2026-03-05 pass)
The requested items 6/7/8/9/10 (with 10 interpreted as @1) all:
- resolve to deterministic patch sites on clean
kernelcache.research.vphone600, - replace the intended original instructions, and
- align with the expected code path in IDA.