Refactor TXM patching and fw_patch_jb flow

Separate dev vs JB TXM patches and streamline fw_patch_jb. fw_patch_dev now calls patch_txm and uses TXMPatcher as TXMDevPatcher; txm_dev.py doc/comments clarify it is dev-only and removes the trustcache bypass from the dev finder. fw_patch_jb was reorganized: COMPONENTS and JB_COMPONENTS lists define base and JB-only patches, the previous subprocess call to run fw_patch.py was removed, and component discovery/patching is unified (with updated messages). txm_jb now only implements the JB-exclusive selector24 extension, relying on dev patches being applied separately.
This commit is contained in:
Lakr
2026-03-04 17:45:02 +08:00
parent 4692a9bee4
commit 2096f9cefe
4 changed files with 59 additions and 57 deletions

View File

@@ -17,13 +17,16 @@ from fw_patch import (
patch_ibss,
patch_kernelcache,
patch_llb,
patch_txm,
patch_component,
)
from patchers.txm_dev import TXMPatcher
from patchers.txm_dev import TXMPatcher as TXMDevPatcher
def patch_txm_dev(data):
p = TXMPatcher(data)
if not patch_txm(data):
return False
p = TXMDevPatcher(data)
n = p.apply()
print(f" [+] {n} TXM dev patches applied dynamically")
return n > 0

View File

@@ -1,36 +1,32 @@
#!/usr/bin/env python3
"""
fw_patch_jb.py — Apply jailbreak extension patches after base fw_patch.
fw_patch_jb.py — Patch boot-chain components using dev patches + JB extensions.
Usage:
python3 fw_patch_jb.py [vm_directory]
This script runs base `fw_patch.py` first, then applies additional JB-oriented
patches found dynamically.
This script extends fw_patch_dev with additional JB-oriented patches.
"""
import os
import subprocess
import sys
from fw_patch import (
find_file,
find_restore_dir,
load_firmware,
save_firmware,
patch_avpbooter,
patch_ibec,
patch_ibss,
patch_kernelcache,
patch_llb,
patch_component,
)
from fw_patch_dev import patch_txm_dev
from patchers.iboot_jb import IBootJBPatcher
from patchers.kernel_jb import KernelJBPatcher
from patchers.txm_jb import TXMJBPatcher
def patch_kernelcache_jb(data):
kp = KernelJBPatcher(data)
n = kp.apply()
print(f" [+] {n} kernel JB patches applied dynamically")
return n > 0
def patch_ibss_jb(data):
p = IBootJBPatcher(data, mode="ibss", label="Loaded iBSS")
n = p.apply()
@@ -45,7 +41,32 @@ def patch_txm_jb(data):
return n > 0
def patch_kernelcache_jb(data):
kp = KernelJBPatcher(data)
n = kp.apply()
print(f" [+] {n} kernel JB patches applied dynamically")
return n > 0
# Base components — same as fw_patch_dev (dev TXM instead of base TXM).
COMPONENTS = [
# (name, search_base_is_restore, search_patterns, patch_function, preserve_payp)
("AVPBooter", False, ["AVPBooter*.bin"], patch_avpbooter, False),
("iBSS", True, ["Firmware/dfu/iBSS.vresearch101.RELEASE.im4p"], patch_ibss, False),
("iBEC", True, ["Firmware/dfu/iBEC.vresearch101.RELEASE.im4p"], patch_ibec, False),
(
"LLB",
True,
["Firmware/all_flash/LLB.vresearch101.RELEASE.im4p"],
patch_llb,
False,
),
("TXM", True, ["Firmware/txm.iphoneos.research.im4p"], patch_txm_dev, True),
("kernelcache", True, ["kernelcache.research.vphone600"], patch_kernelcache, True),
]
# JB extension components — applied AFTER base components on the same files.
JB_COMPONENTS = [
# (name, search_base_is_restore, search_patterns, patch_function, preserve_payp)
("iBSS (JB)", True, ["Firmware/dfu/iBSS.vresearch101.RELEASE.im4p"], patch_ibss_jb, False),
("TXM (JB)", True, ["Firmware/txm.iphoneos.research.im4p"], patch_txm_jb, True),
@@ -59,24 +80,6 @@ COMPONENTS = [
]
def patch_component(path, patch_fn, name, preserve_payp):
print(f"\n{'=' * 60}")
print(f" {name}: {path}")
print(f"{'=' * 60}")
im4p, data, was_im4p, original_raw = load_firmware(path)
fmt = "IM4P" if was_im4p else "raw"
extra = f", fourcc={im4p.fourcc}" if was_im4p and im4p else ""
print(f" format: {fmt}{extra}, {len(data)} bytes")
if not patch_fn(data):
print(f" [-] FAILED: {name}")
sys.exit(1)
save_firmware(path, im4p, data, was_im4p, original_raw if preserve_payp else None)
print(f" [+] saved ({fmt})")
def main():
vm_dir = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
vm_dir = os.path.abspath(vm_dir)
@@ -85,12 +88,6 @@ def main():
print(f"[-] Not a directory: {vm_dir}")
sys.exit(1)
script_dir = os.path.dirname(os.path.abspath(__file__))
fw_patch_script = os.path.join(script_dir, "fw_patch.py")
print("[*] Running base fw_patch first ...", flush=True)
subprocess.run([sys.executable, fw_patch_script, vm_dir], check=True)
restore_dir = find_restore_dir(vm_dir)
if not restore_dir:
print(f"[-] No *Restore* directory found in {vm_dir}")
@@ -98,15 +95,22 @@ def main():
print(f"[*] VM directory: {vm_dir}")
print(f"[*] Restore directory: {restore_dir}")
print(f"[*] Applying {len(COMPONENTS)} JB extension components ...")
print(f"[*] Patching {len(COMPONENTS)} boot-chain components (jailbreak mode) ...")
for name, in_restore, patterns, patch_fn, preserve_payp in COMPONENTS:
search_base = restore_dir if in_restore else vm_dir
path = find_file(search_base, patterns, name)
patch_component(path, patch_fn, name, preserve_payp)
if JB_COMPONENTS:
print(f"\n[*] Applying {len(JB_COMPONENTS)} JB extension patches ...")
for name, in_restore, patterns, patch_fn, preserve_payp in JB_COMPONENTS:
search_base = restore_dir if in_restore else vm_dir
path = find_file(search_base, patterns, name)
patch_component(path, patch_fn, name, preserve_payp)
print(f"\n{'=' * 60}")
print(" JB extension patching complete!")
print(f" All components patched successfully (jailbreak mode)!")
print(f"{'=' * 60}")

View File

@@ -58,17 +58,13 @@ def _find_asm_pattern(data, asm_str):
class TXMPatcher:
"""Dynamic patcher for TXM images.
"""Dev-only dynamic patcher for TXM images.
Patches:
1. Trustcache binary-search BL → mov x0, #0
(in the AMFI cert verification function identified by the
unique constant 0x20446 loaded into w19)
2. Selector24 hash extraction: nop LDR X1 + nop BL
3. get-task-allow entitlement check BL → mov x0, #1
4. Selector42|29: shellcode hook + manifest flag force
5. debugger entitlement check BL → mov w0, #1
6. developer-mode guard branch → nop
Patches (dev-specific only — base trustcache bypass is in txm.py):
1. get-task-allow entitlement check BL → mov x0, #1
2. Selector42|29: shellcode hook + manifest flag force
3. debugger entitlement check BL → mov w0, #1
4. developer-mode guard branch → nop
"""
def __init__(self, data, verbose=True):
@@ -109,7 +105,6 @@ class TXMPatcher:
def find_all(self):
self.patches = []
self.patch_trustcache_bypass()
self.patch_get_task_allow_force_true()
self.patch_selector42_29_shellcode()
self.patch_debugger_entitlement_force_true()

View File

@@ -13,7 +13,11 @@ NOP = _asm("nop")
class TXMJBPatcher(TXMDevPatcher):
"""JB TXM patcher: dev TXM patches + selector24 extension."""
"""JB-only TXM patcher: selector24 CS hash-extraction bypass.
Dev patches are applied separately by txm_dev.py; this class only
adds the JB-exclusive selector24 extension.
"""
def apply(self):
self.find_all()
@@ -26,10 +30,6 @@ class TXMJBPatcher(TXMDevPatcher):
def find_all(self):
self.patches = []
self.patch_selector24_hash_extraction_nop()
self.patch_get_task_allow_force_true()
self.patch_selector42_29_shellcode()
self.patch_debugger_entitlement_force_true()
self.patch_developer_mode_bypass()
return self.patches
def patch_selector24_hash_extraction_nop(self):