mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-04-05 04:59:05 +08:00
137 lines
4.4 KiB
Python
Executable File
137 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
testing_kernel_patch.py — Restore a saved kernel checkpoint and apply selected patches.
|
|
|
|
Usage:
|
|
python3 testing_kernel_patch.py <vm_dir> --base-patch jb patch_kcall10
|
|
python3 testing_kernel_patch.py <vm_dir> --base-patch normal patch_apfs_get_dev_by_role_entitlement
|
|
python3 testing_kernel_patch.py <vm_dir> patch_mac_mount patch_dounmount
|
|
|
|
Notes:
|
|
- `--base-patch` selects which checkpoint file to restore first:
|
|
kernelcache.research.vphone600.checkpoint.<base_patch>.backup
|
|
Fallback: legacy `.base_backup`.
|
|
- Patch names can come from either `KernelPatcher` (base) or `KernelJBPatcher` (JB).
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import sys
|
|
|
|
from fw_patch import find_file, find_restore_dir, load_firmware, save_firmware
|
|
from patchers.kernel import KernelPatcher
|
|
from patchers.kernel_jb import KernelJBPatcher
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Restore checkpoint and apply selected kernel patch methods")
|
|
parser.add_argument("vm_dir", help="VM directory (contains iPhone*_Restore)")
|
|
parser.add_argument(
|
|
"patch_names",
|
|
nargs="+",
|
|
help="Patch method names to apply (e.g. patch_kcall10)",
|
|
)
|
|
parser.add_argument(
|
|
"--base-patch",
|
|
choices=("normal", "dev", "jb"),
|
|
default=os.environ.get("BASE_PATCH") or "jb",
|
|
help="Checkpoint variant to restore first (default: jb)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def resolve_checkpoint(kernel_path: str, base_patch: str) -> str:
|
|
preferred = f"{kernel_path}.checkpoint.{base_patch}.backup"
|
|
legacy = f"{kernel_path}.base_backup"
|
|
|
|
if os.path.exists(preferred):
|
|
return preferred
|
|
if os.path.exists(legacy):
|
|
print(f"[!] preferred checkpoint missing, using legacy backup: {legacy}")
|
|
return legacy
|
|
|
|
print(f"[-] No checkpoint found.")
|
|
print(f" Missing: {preferred}")
|
|
print(f" Missing: {legacy}")
|
|
print(f" Run 'make testing_checkpoint_save BASE_PATCH={base_patch}' first.")
|
|
sys.exit(1)
|
|
|
|
|
|
def list_available_methods(base_patcher: KernelPatcher, jb_patcher: KernelJBPatcher) -> None:
|
|
names = set()
|
|
for obj in (base_patcher, jb_patcher):
|
|
for name in dir(obj):
|
|
if name.startswith("patch_") and callable(getattr(obj, name)):
|
|
names.add(name)
|
|
|
|
print(" Available patches:")
|
|
for name in sorted(names):
|
|
print(f" {name}")
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
|
|
vm_dir = os.path.abspath(args.vm_dir)
|
|
if not os.path.isdir(vm_dir):
|
|
print(f"[-] Not a directory: {vm_dir}")
|
|
sys.exit(1)
|
|
|
|
restore_dir = find_restore_dir(vm_dir)
|
|
if not restore_dir:
|
|
print(f"[-] No *Restore* directory found in {vm_dir}")
|
|
sys.exit(1)
|
|
|
|
kernel_path = find_file(restore_dir, ["kernelcache.research.vphone600"], "kernelcache")
|
|
checkpoint_path = resolve_checkpoint(kernel_path, args.base_patch)
|
|
|
|
shutil.copy2(checkpoint_path, kernel_path)
|
|
print(f"[*] Restored checkpoint: {checkpoint_path}")
|
|
print(f"[*] Target kernel: {kernel_path}")
|
|
|
|
im4p, data, was_im4p, original_raw = load_firmware(kernel_path)
|
|
fmt = "IM4P" if was_im4p else "raw"
|
|
print(f"[*] Loaded: {fmt}, {len(data)} bytes")
|
|
|
|
base_patcher = KernelPatcher(data)
|
|
jb_patcher = KernelJBPatcher(data)
|
|
|
|
selected = []
|
|
for patch_name in args.patch_names:
|
|
method = getattr(jb_patcher, patch_name, None)
|
|
if callable(method):
|
|
selected.append(("jb", patch_name, method))
|
|
continue
|
|
|
|
method = getattr(base_patcher, patch_name, None)
|
|
if callable(method):
|
|
selected.append(("base", patch_name, method))
|
|
continue
|
|
|
|
print(f"[-] Unknown patch: {patch_name}")
|
|
list_available_methods(base_patcher, jb_patcher)
|
|
sys.exit(1)
|
|
|
|
print(f"[*] Applying {len(selected)} method(s)...")
|
|
for scope, patch_name, method in selected:
|
|
print(f" - {patch_name} [{scope}]")
|
|
method()
|
|
|
|
applied = 0
|
|
for off, patch_bytes, _ in base_patcher.patches:
|
|
data[off : off + len(patch_bytes)] = patch_bytes
|
|
applied += 1
|
|
for off, patch_bytes, _ in jb_patcher.patches:
|
|
data[off : off + len(patch_bytes)] = patch_bytes
|
|
applied += 1
|
|
|
|
print(f"[+] Applied low-level patches: {applied}")
|
|
|
|
save_firmware(kernel_path, im4p, data, was_im4p, original_raw)
|
|
print(f"[+] Saved: {kernel_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|