mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-09-05 17:14:28 +00:00
cfw: dsc: Add FORCE_DSC_MAXSLIDE opt-in to zero maxSlide on non-27 bases
patch-dsc-maxslide self-gates to a no-op when the shared cache fits the
vphone600 26.x kernel's 6 GiB region, which 26.x/18.x bases always do, so
it only fires on 27. Add a --force flag that bypasses the fits-check and
zeroes maxSlide unconditionally (still idempotent), and a
FORCE_DSC_MAXSLIDE=1 env opt-in in cfw_install.sh that runs it on non-27
bases. Default off; 27 behavior unchanged.
Also fix the installer env-threading in cfw_install_host.sh: a
${VAR:+NAME=val} word produced by expansion is not parsed as a shell
assignment (zsh runs it as a command), so route the assignments through
env. This makes FORCE_DSC_MAXSLIDE reach the installer and repairs the
same latent bug for SPOOF_BUILD.
Verified on a 26.4 JB VM: FORCE_DSC_MAXSLIDE=1 yields on-disk maxSlide=0
and a live shared-cache slide of 0x0 (dyld maps the cache at its
preferred base 0x180000000 in rpcserver_ios), versus the nonzero slide a
stock 26.4 boot picks.
Docs + research/0_binary_patch_comparison.md updated (README and the ja/ko/zh
translations).
Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
committed by
zqxwce
co-authored by
Claude Fable 5
parent
fac586a95d
commit
c9ad3c7519
@@ -43,12 +43,13 @@ Commands:
|
||||
Pairs with the KernelJBPatchIomfbSwap kernel patches (accept 27's 0x6e0
|
||||
SwapEnd struct).
|
||||
|
||||
patch-dsc-maxslide <chunks_dir> [--dry-run]
|
||||
patch-dsc-maxslide <chunks_dir> [--dry-run] [--force]
|
||||
Zero the dyld_cache_header maxSlide when the userland cache would overflow
|
||||
the vphone600 26.x kernel's 6 GiB shared region (cache span + maxSlide >
|
||||
0x180000000, e.g. iOS 27.0). Lets the cache map at slide 0 so launchd's dyld
|
||||
can map libSystem. Self-gating (no-op if it already fits); no re-attest needed
|
||||
(header field, not a cs_validate'd code page).
|
||||
(header field, not a cs_validate'd code page). --force zeroes maxSlide even
|
||||
when the cache fits (non-27 opt-in).
|
||||
|
||||
patch-lsd-embedded-reg <chunks_dir> [--dry-run]
|
||||
Force lsd's -[_LSDModifyClient clientIsEntitledForEmbeddedRegistrationOperations]
|
||||
@@ -229,10 +230,11 @@ def main():
|
||||
|
||||
elif cmd == "patch-dsc-maxslide":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: patch_cfw.py patch-dsc-maxslide <chunks_dir> [--dry-run]")
|
||||
print("Usage: patch_cfw.py patch-dsc-maxslide <chunks_dir> [--dry-run] [--force]")
|
||||
sys.exit(1)
|
||||
dry_run = "--dry-run" in sys.argv[3:]
|
||||
patch_dsc_maxslide(sys.argv[2], dry_run=dry_run)
|
||||
force = "--force" in sys.argv[3:]
|
||||
patch_dsc_maxslide(sys.argv[2], dry_run=dry_run, force=force)
|
||||
sys.exit(0)
|
||||
|
||||
elif cmd == "patch-lsd-embedded-reg":
|
||||
|
||||
@@ -22,7 +22,8 @@ setup, NOT a cs_validate'd dylib code page — so, unlike cfw_patch_iomfb_swapen
|
||||
page re-attestation is required (confirmed empirically: a live-poked cache with
|
||||
maxSlide=0 booted with "dyld cache mapped system-wide", 0 panics).
|
||||
|
||||
Self-gating: no-op unless span + maxSlide overflows the region.
|
||||
Self-gating: no-op unless span + maxSlide overflows the region. force=True
|
||||
(CLI --force) zeroes maxSlide unconditionally, for non-27 bases that opt in.
|
||||
|
||||
dyld_cache_header offsets (little-endian u64, stable across recent iOS):
|
||||
sharedRegionStart @0xE0, sharedRegionSize @0xE8, maxSlide @0xF0
|
||||
@@ -42,7 +43,7 @@ OFF_MAX_SLIDE = 0xF0
|
||||
KERNEL_SHARED_REGION_SIZE = 0x180000000
|
||||
|
||||
|
||||
def patch_dsc_maxslide(chunks_dir, *, kernel_region_size=KERNEL_SHARED_REGION_SIZE, dry_run=False):
|
||||
def patch_dsc_maxslide(chunks_dir, *, kernel_region_size=KERNEL_SHARED_REGION_SIZE, dry_run=False, force=False):
|
||||
main = os.path.join(chunks_dir, MAIN_CHUNK)
|
||||
if not os.path.isfile(main):
|
||||
raise FileNotFoundError(f"main DSC chunk not found: {main}")
|
||||
@@ -56,17 +57,23 @@ def patch_dsc_maxslide(chunks_dir, *, kernel_region_size=KERNEL_SHARED_REGION_SI
|
||||
maxslide = struct.unpack_from("<Q", hdr, OFF_MAX_SLIDE)[0]
|
||||
print(f" [.] {MAIN_CHUNK}: start=0x{srstart:X} size=0x{srsize:X} maxSlide=0x{maxslide:X}")
|
||||
|
||||
if srsize + maxslide <= kernel_region_size:
|
||||
fits = srsize + maxslide <= kernel_region_size
|
||||
if fits and not force:
|
||||
print(f" [=] fits: span+maxSlide 0x{srsize + maxslide:X} <= "
|
||||
f"region 0x{kernel_region_size:X}; no change")
|
||||
return 0
|
||||
if maxslide == 0:
|
||||
print(" [=] maxSlide already 0; no change")
|
||||
return 0
|
||||
|
||||
# Overflow: set maxSlide to 0 so the cache maps at slide 0 within the region.
|
||||
# Set maxSlide to 0 so the cache maps at slide 0 within the region.
|
||||
new_maxslide = 0
|
||||
reason = (f"forced: span+maxSlide 0x{srsize + maxslide:X} fits region "
|
||||
f"0x{kernel_region_size:X} but --force set" if fits
|
||||
else f"overflow: span+maxSlide 0x{srsize + maxslide:X} > "
|
||||
f"region 0x{kernel_region_size:X}")
|
||||
action = "would set" if dry_run else "set"
|
||||
print(f" [+] overflow: span+maxSlide 0x{srsize + maxslide:X} > "
|
||||
f"region 0x{kernel_region_size:X}; {action} maxSlide "
|
||||
f"0x{maxslide:X} -> 0x{new_maxslide:X}")
|
||||
print(f" [+] {reason}; {action} maxSlide 0x{maxslide:X} -> 0x{new_maxslide:X}")
|
||||
if not dry_run:
|
||||
f.seek(OFF_MAX_SLIDE)
|
||||
f.write(struct.pack("<Q", new_maxslide))
|
||||
@@ -107,6 +114,13 @@ def _self_test():
|
||||
with open(c, "rb") as f:
|
||||
f.seek(OFF_MAX_SLIDE)
|
||||
assert struct.unpack("<Q", f.read(8))[0] == 0x20000000
|
||||
# fits + force: opt-in zeroes maxSlide even though it fits
|
||||
assert patch_dsc_maxslide(d, force=True) == 1
|
||||
with open(c, "rb") as f:
|
||||
f.seek(OFF_MAX_SLIDE)
|
||||
assert struct.unpack("<Q", f.read(8))[0] == 0
|
||||
# force + already 0: idempotent no-op
|
||||
assert patch_dsc_maxslide(d, force=True) == 0
|
||||
print("self-test OK")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user