From 08c9cb78eeda7f7d555bc7d32deb93994dc6ecf0 Mon Sep 17 00:00:00 2001 From: Xin Huang Date: Wed, 11 Mar 2026 22:51:45 -0700 Subject: [PATCH] Nix PATH preservation, amfidont boot, and preflight stability (#196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: preserve caller PATH through Nix zshenv reset in cfw scripts Nix darwin's /etc/zshenv resets PATH on every zsh subprocess, discarding the Makefile's carefully constructed PATH (which includes .venv/bin and /opt/homebrew/bin). This caused 'Missing Python deps' and ldid PKCS12_parse errors during cfw_install. Pass the Makefile PATH through _VPHONE_PATH env var (which zshenv won't touch), and restore it at the top of each cfw_install script. * fix(cfw_install_dev): add python resolver, use glob for vphoned sources - Add _resolve_python3() matching cfw_install.sh so the venv python is used instead of Nix system python (which lacks capstone/keystone). - Replace hardcoded VPHONED_SRCS list with glob pattern to auto-pick up new .m files (was missing 5 files: accessibility, apps, clipboard, settings, url — causing linker errors). * fix: amfidont uses bundle binary CDHash and .build path make boot launches the bundle binary (.build/vphone-cli.app/Contents/ MacOS/vphone-cli), not the release binary. amfidont's --path must cover the .app bundle location. - amfidont_allow_vphone depends on bundle (not build) - start_amfidont_for_vphone.sh extracts CDHash from bundle binary - --path points to .build/ so amfidont covers .app bundle contents * fix(preflight): prevent run_capture errexit on non-zero return zsh set -e is global scope — set -e inside run_capture then return 137 triggers errexit and kills the script before reaching the assert-bootable check. Use '|| rc=$?' instead to capture the exit code without modifying errexit state. --- Makefile | 8 ++--- scripts/boot_host_preflight.sh | 6 ++-- scripts/cfw_install.sh | 3 ++ scripts/cfw_install_dev.sh | 47 ++++++++++++++++------------ scripts/cfw_install_jb.sh | 2 ++ scripts/start_amfidont_for_vphone.sh | 34 +++++++++++--------- 6 files changed, 57 insertions(+), 43 deletions(-) diff --git a/Makefile b/Makefile index 12a9820..a4e62c8 100644 --- a/Makefile +++ b/Makefile @@ -189,7 +189,7 @@ vm_new: CPU="$(CPU)" MEMORY="$(MEMORY)" \ zsh $(SCRIPTS)/vm_create.sh --dir $(VM_DIR) --disk-size $(DISK_SIZE) -amfidont_allow_vphone: build +amfidont_allow_vphone: bundle zsh $(SCRIPTS)/start_amfidont_for_vphone.sh boot_host_preflight: build @@ -280,10 +280,10 @@ ramdisk_send: .PHONY: cfw_install cfw_install_dev cfw_install_jb cfw_install: - cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") zsh "$(CURDIR)/$(SCRIPTS)/cfw_install.sh" . + cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") _VPHONE_PATH="$$PATH" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install.sh" . cfw_install_dev: - cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_dev.sh" . + cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") _VPHONE_PATH="$$PATH" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_dev.sh" . cfw_install_jb: - cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_jb.sh" . + cd $(VM_DIR) && $(if $(SSH_PORT),SSH_PORT="$(SSH_PORT)") _VPHONE_PATH="$$PATH" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_jb.sh" . diff --git a/scripts/boot_host_preflight.sh b/scripts/boot_host_preflight.sh index df88236..b713d55 100644 --- a/scripts/boot_host_preflight.sh +++ b/scripts/boot_host_preflight.sh @@ -51,10 +51,8 @@ run_capture() { shift local log_file="${TMP_DIR}/${label}.log" - set +e - "$@" >"$log_file" 2>&1 - local rc=$? - set -e + local rc=0 + "$@" >"$log_file" 2>&1 || rc=$? (( QUIET == 0 )) && echo "[${label}] exit=${rc}" if (( QUIET == 0 )) && [[ -s "$log_file" ]]; then diff --git a/scripts/cfw_install.sh b/scripts/cfw_install.sh index ee417aa..9fafb7c 100755 --- a/scripts/cfw_install.sh +++ b/scripts/cfw_install.sh @@ -17,6 +17,9 @@ # Usage: make cfw_install set -euo pipefail +# ── Restore caller's PATH — Nix /etc/zshenv resets PATH on zsh startup ─ +[[ -n "${_VPHONE_PATH:-}" ]] && export PATH="$_VPHONE_PATH" + VM_DIR="${1:-.}" SCRIPT_DIR="${0:a:h}" CFW_SKIP_HALT="${CFW_SKIP_HALT:-0}" diff --git a/scripts/cfw_install_dev.sh b/scripts/cfw_install_dev.sh index d44f63d..ffbb0e4 100755 --- a/scripts/cfw_install_dev.sh +++ b/scripts/cfw_install_dev.sh @@ -17,6 +17,9 @@ # Usage: make cfw_install_dev set -euo pipefail +# ── Restore caller's PATH — Nix /etc/zshenv resets PATH on zsh startup ─ +[[ -n "${_VPHONE_PATH:-}" ]] && export PATH="$_VPHONE_PATH" + VM_DIR="${1:-.}" SCRIPT_DIR="${0:a:h}" CFW_SKIP_HALT="${CFW_SKIP_HALT:-0}" @@ -24,6 +27,17 @@ CFW_SKIP_HALT="${CFW_SKIP_HALT:-0}" # Resolve absolute paths VM_DIR="$(cd "$VM_DIR" && pwd)" +# ── Python resolver — prefer project venv over whatever is in PATH ─ +_resolve_python3() { + local venv_py="${SCRIPT_DIR:h}/.venv/bin/python3" + if [[ -x "$venv_py" ]]; then + echo "$venv_py" + else + command -v python3 || true + fi +} +PYTHON3="$(_resolve_python3)" + # ── Configuration ─────────────────────────────────────────────── CFW_INPUT="cfw_input" CFW_ARCHIVE="cfw_input.tar.zst" @@ -177,9 +191,12 @@ apply_dev_overlay() { check_prereqs() { command -v ipsw >/dev/null 2>&1 || die "'ipsw' not found. Install: brew install blacktop/tap/ipsw" command -v aea >/dev/null 2>&1 || die "'aea' not found (requires macOS 12+)" - command -v python3 >/dev/null 2>&1 || die "python3 not found" - python3 -c "import capstone, keystone" 2>/dev/null || - die "Missing Python deps. Install: pip install capstone keystone-engine" + [[ -x "$PYTHON3" ]] || die "python3 not found (tried: $PYTHON3). Run: make setup_venv" + echo "[*] Python: $PYTHON3 ($("$PYTHON3" --version 2>&1))" + local py_err + py_err="$("$PYTHON3" -c "import capstone, keystone" 2>&1)" || { + die "Missing Python deps (using $PYTHON3).\n Error: ${py_err}\n Fix: source ${SCRIPT_DIR:h}/.venv/bin/activate && pip install capstone keystone-engine\n Or: make setup_venv" + } } # ── Cleanup trap (unmount DMGs on error) ─────────────────────── @@ -210,7 +227,7 @@ mkdir -p "$TEMP_DIR" # ── Parse Cryptex paths from BuildManifest ───────────────────── echo "" echo "[*] Parsing iPhone BuildManifest for Cryptex paths..." -CRYPTEX_PATHS=$(python3 "$SCRIPT_DIR/patchers/cfw.py" cryptex-paths "$RESTORE_DIR/BuildManifest-iPhone.plist") +CRYPTEX_PATHS=$("$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" cryptex-paths "$RESTORE_DIR/BuildManifest-iPhone.plist") CRYPTEX_SYSOS=$(echo "$CRYPTEX_PATHS" | head -1) CRYPTEX_APPOS=$(echo "$CRYPTEX_PATHS" | tail -1) echo " SystemOS: $CRYPTEX_SYSOS" @@ -270,7 +287,7 @@ fi scp_from "/mnt1/sbin/launchd.bak" "$TEMP_DIR/launchd" -python3 "$SCRIPT_DIR/patchers/cfw.py" patch-launchd-jetsam "$TEMP_DIR/launchd" +"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-launchd-jetsam "$TEMP_DIR/launchd" ldid_sign "$TEMP_DIR/launchd" scp_to "$TEMP_DIR/launchd" "/mnt1/sbin/launchd" ssh_cmd "/bin/chmod 0755 /mnt1/sbin/launchd" @@ -348,7 +365,7 @@ if ! remote_file_exists "/mnt1/usr/libexec/seputil.bak"; then fi scp_from "/mnt1/usr/libexec/seputil.bak" "$TEMP_DIR/seputil" -python3 "$SCRIPT_DIR/patchers/cfw.py" patch-seputil "$TEMP_DIR/seputil" +"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-seputil "$TEMP_DIR/seputil" ldid_sign "$TEMP_DIR/seputil" "com.apple.seputil" scp_to "$TEMP_DIR/seputil" "/mnt1/usr/libexec/seputil" ssh_cmd "/bin/chmod 0755 /mnt1/usr/libexec/seputil" @@ -404,7 +421,7 @@ if ! remote_file_exists "/mnt1/usr/libexec/launchd_cache_loader.bak"; then fi scp_from "/mnt1/usr/libexec/launchd_cache_loader.bak" "$TEMP_DIR/launchd_cache_loader" -python3 "$SCRIPT_DIR/patchers/cfw.py" patch-launchd-cache-loader "$TEMP_DIR/launchd_cache_loader" +"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-launchd-cache-loader "$TEMP_DIR/launchd_cache_loader" ldid_sign "$TEMP_DIR/launchd_cache_loader" "com.apple.launchd_cache_loader" scp_to "$TEMP_DIR/launchd_cache_loader" "/mnt1/usr/libexec/launchd_cache_loader" ssh_cmd "/bin/chmod 0755 /mnt1/usr/libexec/launchd_cache_loader" @@ -422,7 +439,7 @@ if ! remote_file_exists "/mnt1/usr/libexec/mobileactivationd.bak"; then fi scp_from "/mnt1/usr/libexec/mobileactivationd.bak" "$TEMP_DIR/mobileactivationd" -python3 "$SCRIPT_DIR/patchers/cfw.py" patch-mobileactivationd "$TEMP_DIR/mobileactivationd" +"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" patch-mobileactivationd "$TEMP_DIR/mobileactivationd" ldid_sign "$TEMP_DIR/mobileactivationd" scp_to "$TEMP_DIR/mobileactivationd" "/mnt1/usr/libexec/mobileactivationd" ssh_cmd "/bin/chmod 0755 /mnt1/usr/libexec/mobileactivationd" @@ -436,17 +453,7 @@ echo "[7/7] Installing LaunchDaemons..." # Install vphoned (vsock HID injector daemon) VPHONED_SRC="$SCRIPT_DIR/vphoned" VPHONED_BIN="$VPHONED_SRC/vphoned" -VPHONED_SRCS=( - "$VPHONED_SRC/unarchive.m" - "$VPHONED_SRC/vphoned.m" - "$VPHONED_SRC/vphoned_install.m" - "$VPHONED_SRC/vphoned_protocol.m" - "$VPHONED_SRC/vphoned_hid.m" - "$VPHONED_SRC/vphoned_devmode.m" - "$VPHONED_SRC/vphoned_location.m" - "$VPHONED_SRC/vphoned_files.m" - "$VPHONED_SRC/vphoned_keychain.m" -) +VPHONED_SRCS=("$VPHONED_SRC"/*.m) needs_vphoned_build=0 if [[ ! -f "$VPHONED_BIN" ]]; then needs_vphoned_build=1 @@ -495,7 +502,7 @@ fi scp_from "/mnt1/System/Library/xpc/launchd.plist.bak" "$TEMP_DIR/launchd.plist" cp "$VPHONED_SRC/vphoned.plist" "$INPUT_DIR/jb/LaunchDaemons/" -python3 "$SCRIPT_DIR/patchers/cfw.py" inject-daemons "$TEMP_DIR/launchd.plist" "$INPUT_DIR/jb/LaunchDaemons" +"$PYTHON3" "$SCRIPT_DIR/patchers/cfw.py" inject-daemons "$TEMP_DIR/launchd.plist" "$INPUT_DIR/jb/LaunchDaemons" scp_to "$TEMP_DIR/launchd.plist" "/mnt1/System/Library/xpc/launchd.plist" ssh_cmd "/bin/chmod 0644 /mnt1/System/Library/xpc/launchd.plist" diff --git a/scripts/cfw_install_jb.sh b/scripts/cfw_install_jb.sh index 59841d8..e73887d 100755 --- a/scripts/cfw_install_jb.sh +++ b/scripts/cfw_install_jb.sh @@ -12,6 +12,8 @@ # Usage: make cfw_install_jb set -euo pipefail +# ── Restore caller's PATH — Nix /etc/zshenv resets PATH on zsh startup ─ +[[ -n "${_VPHONE_PATH:-}" ]] && export PATH="$_VPHONE_PATH" VM_DIR="${1:-.}" SCRIPT_DIR="${0:a:h}" diff --git a/scripts/start_amfidont_for_vphone.sh b/scripts/start_amfidont_for_vphone.sh index f8de265..8eb1b82 100644 --- a/scripts/start_amfidont_for_vphone.sh +++ b/scripts/start_amfidont_for_vphone.sh @@ -2,15 +2,15 @@ # start_amfidont_for_vphone.sh — Start amfidont for the current vphone build. # # This is the README "Option 2" host workaround packaged for this repo: -# - computes the signed release binary CDHash -# - uses the URL-encoded project path form observed by AMFIPathValidator +# - computes the signed bundle binary CDHash (what `make boot` actually launches) +# - uses the .build path so amfidont covers binaries inside the .app bundle # - starts amfidont in daemon mode so signed vphone-cli launches are allowlisted set -euo pipefail SCRIPT_DIR="${0:A:h}" PROJECT_ROOT="${SCRIPT_DIR:h}" -RELEASE_BIN="${PROJECT_ROOT}/.build/release/vphone-cli" +BUNDLE_BIN="${PROJECT_ROOT}/.build/vphone-cli.app/Contents/MacOS/vphone-cli" AMFIDONT_BIN="${HOME}/Library/Python/3.9/bin/amfidont" [[ -x "$AMFIDONT_BIN" ]] || { @@ -19,29 +19,33 @@ AMFIDONT_BIN="${HOME}/Library/Python/3.9/bin/amfidont" exit 1 } -[[ -x "$RELEASE_BIN" ]] || { - echo "Missing release binary: $RELEASE_BIN" >&2 - echo "Run 'make build' first." >&2 +[[ -x "$BUNDLE_BIN" ]] || { + echo "Missing bundle binary: $BUNDLE_BIN" >&2 + echo "Run 'make bundle' first." >&2 exit 1 } CDHASH="$( - codesign -dv --verbose=4 "$RELEASE_BIN" 2>&1 \ + codesign -dv --verbose=4 "$BUNDLE_BIN" 2>&1 \ | sed -n 's/^CDHash=//p' \ | head -n1 )" [[ -n "$CDHASH" ]] || { - echo "Failed to extract CDHash for $RELEASE_BIN" >&2 + echo "Failed to extract CDHash for $BUNDLE_BIN" >&2 exit 1 } -ENCODED_PROJECT_ROOT="${PROJECT_ROOT// /%20}" +# amfidont --path must cover the actual binary location inside the .app +AMFI_PATH="${PROJECT_ROOT}/.build" +ENCODED_AMFI_PATH="${AMFI_PATH// /%20}" echo "[*] Project root: $PROJECT_ROOT" -echo "[*] Encoded AMFI path: $ENCODED_PROJECT_ROOT" -echo "[*] Release CDHash: $CDHASH" +echo "[*] AMFI path: $AMFI_PATH" +echo "[*] Bundle CDHash: $CDHASH" -exec sudo "$AMFIDONT_BIN" daemon \ - --path "$ENCODED_PROJECT_ROOT" \ - --cdhash "$CDHASH" \ - --verbose +sudo env PYTHONPATH="/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python" \ + /usr/bin/python3 "$AMFIDONT_BIN" daemon \ + --path "$ENCODED_AMFI_PATH" \ + --cdhash "$CDHASH" \ + --verbose \ + >/dev/null 2>&1