#!/usr/bin/env python3 """ cfw.py — Dynamic binary patching for CFW installation on vphone600. Uses capstone for disassembly-based anchoring and keystone for instruction assembly, producing reliable, upgrade-proof patches. Called by cfw_install.sh during CFW installation. Commands: cryptex-paths Print SystemOS and AppOS DMG paths from BuildManifest. patch-seputil Patch seputil gigalocker UUID to "AA". patch-launchd-cache-loader NOP the cache validation check in launchd_cache_loader. patch-mobileactivationd Patch -[DeviceType should_hactivate] to always return true. patch-launchd-jetsam Patch launchd jetsam panic guard to avoid initproc crash loop. inject-daemons Inject bash/trollvnc into launchd.plist. inject-dylib Inject LC_LOAD_DYLIB into Mach-O binary (thin or universal). Equivalent to: optool install -c load -p -t Dependencies: pip install capstone keystone-engine """ import os import sys # When run as `python3 scripts/patchers/cfw.py`, __name__ is "__main__" and # relative imports fail. Add the parent directory to sys.path so we can import # from the patchers package using absolute imports. if __name__ == "__main__": sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from patchers.cfw_patch_seputil import patch_seputil from patchers.cfw_patch_cache_loader import patch_launchd_cache_loader from patchers.cfw_patch_mobileactivationd import patch_mobileactivationd from patchers.cfw_patch_jetsam import patch_launchd_jetsam from patchers.cfw_daemons import parse_cryptex_paths, inject_daemons else: from .cfw_patch_seputil import patch_seputil from .cfw_patch_cache_loader import patch_launchd_cache_loader from .cfw_patch_mobileactivationd import patch_mobileactivationd from .cfw_patch_jetsam import patch_launchd_jetsam from .cfw_daemons import parse_cryptex_paths, inject_daemons def main(): if len(sys.argv) < 2: print(__doc__) sys.exit(1) cmd = sys.argv[1] if cmd == "cryptex-paths": if len(sys.argv) < 3: print("Usage: patch_cfw.py cryptex-paths ") sys.exit(1) sysos, appos = parse_cryptex_paths(sys.argv[2]) print(sysos) print(appos) elif cmd == "patch-seputil": if len(sys.argv) < 3: print("Usage: patch_cfw.py patch-seputil ") sys.exit(1) if not patch_seputil(sys.argv[2]): sys.exit(1) elif cmd == "patch-launchd-cache-loader": if len(sys.argv) < 3: print("Usage: patch_cfw.py patch-launchd-cache-loader ") sys.exit(1) if not patch_launchd_cache_loader(sys.argv[2]): sys.exit(1) elif cmd == "patch-mobileactivationd": if len(sys.argv) < 3: print("Usage: patch_cfw.py patch-mobileactivationd ") sys.exit(1) if not patch_mobileactivationd(sys.argv[2]): sys.exit(1) elif cmd == "patch-launchd-jetsam": if len(sys.argv) < 3: print("Usage: patch_cfw.py patch-launchd-jetsam ") sys.exit(1) if not patch_launchd_jetsam(sys.argv[2]): sys.exit(1) elif cmd == "inject-daemons": if len(sys.argv) < 4: print("Usage: patch_cfw.py inject-daemons ") sys.exit(1) inject_daemons(sys.argv[2], sys.argv[3]) elif cmd == "inject-dylib": if len(sys.argv) < 4: print("Usage: patch_cfw.py inject-dylib ") sys.exit(1) import subprocess, shutil insert_dylib_bin = shutil.which("insert_dylib") if not insert_dylib_bin: # Check .tools/bin/ relative to project root project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) candidate = os.path.join(project_root, ".tools", "bin", "insert_dylib") if os.path.isfile(candidate) and os.access(candidate, os.X_OK): insert_dylib_bin = candidate if not insert_dylib_bin: print("[-] insert_dylib not found. Run: make setup_tools") sys.exit(1) rc = subprocess.run( [insert_dylib_bin, "--weak", "--inplace", "--all-yes", sys.argv[3], sys.argv[2]], ).returncode if rc != 0: sys.exit(rc) else: print(f"Unknown command: {cmd}") print("Commands: cryptex-paths, patch-seputil, patch-launchd-cache-loader,") print(" patch-mobileactivationd, patch-launchd-jetsam,") print(" inject-daemons, inject-dylib") sys.exit(1) if __name__ == "__main__": main()