mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-09-06 17:54:26 +00:00
Isolate multi-VM setup with deterministic device targeting
This commit is contained in:
@@ -8,6 +8,10 @@ CPU ?= 8
|
|||||||
MEMORY ?= 8192
|
MEMORY ?= 8192
|
||||||
DISK_SIZE ?= 64
|
DISK_SIZE ?= 64
|
||||||
CFW_INPUT ?= cfw_input
|
CFW_INPUT ?= cfw_input
|
||||||
|
RESTORE_UDID ?=
|
||||||
|
RESTORE_ECID ?=
|
||||||
|
IRECOVERY_ECID ?=
|
||||||
|
SSH_PORT ?= 2222
|
||||||
|
|
||||||
# ─── Build info ──────────────────────────────────────────────────
|
# ─── Build info ──────────────────────────────────────────────────
|
||||||
GIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
GIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||||
@@ -77,7 +81,7 @@ help:
|
|||||||
@echo " make cfw_install_dev Install CFW mods via SSH (dev mode)"
|
@echo " make cfw_install_dev Install CFW mods via SSH (dev mode)"
|
||||||
@echo " make cfw_install_jb Install CFW + JB extensions (jetsam/procursus/basebin)"
|
@echo " make cfw_install_jb Install CFW + JB extensions (jetsam/procursus/basebin)"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Variables: VM_DIR=$(VM_DIR) CPU=$(CPU) MEMORY=$(MEMORY) DISK_SIZE=$(DISK_SIZE)"
|
@echo "Variables: VM_DIR=$(VM_DIR) CPU=$(CPU) MEMORY=$(MEMORY) DISK_SIZE=$(DISK_SIZE) SSH_PORT=$(SSH_PORT)"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
# Setup
|
# Setup
|
||||||
@@ -208,10 +212,16 @@ fw_patch_jb:
|
|||||||
.PHONY: restore_get_shsh restore
|
.PHONY: restore_get_shsh restore
|
||||||
|
|
||||||
restore_get_shsh:
|
restore_get_shsh:
|
||||||
cd $(VM_DIR) && "$(CURDIR)/$(IDEVICERESTORE)" -e -y ./iPhone*_Restore -t
|
cd $(VM_DIR) && "$(CURDIR)/$(IDEVICERESTORE)" \
|
||||||
|
$(if $(RESTORE_UDID),-u $(RESTORE_UDID),) \
|
||||||
|
$(if $(RESTORE_ECID),-i $(RESTORE_ECID),) \
|
||||||
|
-e -y ./iPhone*_Restore -t
|
||||||
|
|
||||||
restore:
|
restore:
|
||||||
cd $(VM_DIR) && "$(CURDIR)/$(IDEVICERESTORE)" -e -y ./iPhone*_Restore
|
cd $(VM_DIR) && "$(CURDIR)/$(IDEVICERESTORE)" \
|
||||||
|
$(if $(RESTORE_UDID),-u $(RESTORE_UDID),) \
|
||||||
|
$(if $(RESTORE_ECID),-i $(RESTORE_ECID),) \
|
||||||
|
-e -y ./iPhone*_Restore
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
# Ramdisk
|
# Ramdisk
|
||||||
@@ -223,7 +233,8 @@ ramdisk_build:
|
|||||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/ramdisk_build.py" .
|
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/ramdisk_build.py" .
|
||||||
|
|
||||||
ramdisk_send:
|
ramdisk_send:
|
||||||
cd $(VM_DIR) && IRECOVERY="$(CURDIR)/$(IRECOVERY)" zsh "$(CURDIR)/$(SCRIPTS)/ramdisk_send.sh"
|
cd $(VM_DIR) && IRECOVERY="$(CURDIR)/$(IRECOVERY)" IRECOVERY_ECID="$(IRECOVERY_ECID)" \
|
||||||
|
zsh "$(CURDIR)/$(SCRIPTS)/ramdisk_send.sh"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════
|
||||||
# CFW
|
# CFW
|
||||||
@@ -232,10 +243,10 @@ ramdisk_send:
|
|||||||
.PHONY: cfw_install cfw_install_dev cfw_install_jb
|
.PHONY: cfw_install cfw_install_dev cfw_install_jb
|
||||||
|
|
||||||
cfw_install:
|
cfw_install:
|
||||||
cd $(VM_DIR) && zsh "$(CURDIR)/$(SCRIPTS)/cfw_install.sh" .
|
cd $(VM_DIR) && SSH_PORT="$(SSH_PORT)" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install.sh" .
|
||||||
|
|
||||||
cfw_install_dev:
|
cfw_install_dev:
|
||||||
cd $(VM_DIR) && zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_dev.sh" .
|
cd $(VM_DIR) && SSH_PORT="$(SSH_PORT)" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_dev.sh" .
|
||||||
|
|
||||||
cfw_install_jb:
|
cfw_install_jb:
|
||||||
cd $(VM_DIR) && zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_jb.sh" .
|
cd $(VM_DIR) && SSH_PORT="$(SSH_PORT)" zsh "$(CURDIR)/$(SCRIPTS)/cfw_install_jb.sh" .
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ CFW_INPUT="cfw_input"
|
|||||||
CFW_ARCHIVE="cfw_input.tar.zst"
|
CFW_ARCHIVE="cfw_input.tar.zst"
|
||||||
TEMP_DIR="$VM_DIR/.cfw_temp"
|
TEMP_DIR="$VM_DIR/.cfw_temp"
|
||||||
|
|
||||||
SSH_PORT=2222
|
SSH_PORT="${SSH_PORT:-2222}"
|
||||||
SSH_PASS="alpine"
|
SSH_PASS="alpine"
|
||||||
SSH_USER="root"
|
SSH_USER="root"
|
||||||
SSH_HOST="localhost"
|
SSH_HOST="localhost"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ CFW_INPUT="cfw_input"
|
|||||||
CFW_ARCHIVE="cfw_input.tar.zst"
|
CFW_ARCHIVE="cfw_input.tar.zst"
|
||||||
TEMP_DIR="$VM_DIR/.cfw_temp"
|
TEMP_DIR="$VM_DIR/.cfw_temp"
|
||||||
|
|
||||||
SSH_PORT=2222
|
SSH_PORT="${SSH_PORT:-2222}"
|
||||||
SSH_PASS="alpine"
|
SSH_PASS="alpine"
|
||||||
SSH_USER="root"
|
SSH_USER="root"
|
||||||
SSH_HOST="localhost"
|
SSH_HOST="localhost"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ CFW_JB_INPUT="cfw_jb_input"
|
|||||||
CFW_JB_ARCHIVE="cfw_jb_input.tar.zst"
|
CFW_JB_ARCHIVE="cfw_jb_input.tar.zst"
|
||||||
TEMP_DIR="$VM_DIR/.cfw_temp"
|
TEMP_DIR="$VM_DIR/.cfw_temp"
|
||||||
|
|
||||||
SSH_PORT=2222
|
SSH_PORT="${SSH_PORT:-2222}"
|
||||||
SSH_PASS="alpine"
|
SSH_PASS="alpine"
|
||||||
SSH_USER="root"
|
SSH_USER="root"
|
||||||
SSH_HOST="localhost"
|
SSH_HOST="localhost"
|
||||||
|
|||||||
+35
-17
@@ -8,8 +8,26 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
IRECOVERY="${IRECOVERY:-irecovery}"
|
IRECOVERY="${IRECOVERY:-irecovery}"
|
||||||
|
IRECOVERY_ECID="${IRECOVERY_ECID:-}"
|
||||||
RAMDISK_DIR="${1:-Ramdisk}"
|
RAMDISK_DIR="${1:-Ramdisk}"
|
||||||
|
|
||||||
|
IRECOVERY_ARGS=()
|
||||||
|
if [[ -n "$IRECOVERY_ECID" ]]; then
|
||||||
|
IRECOVERY_ECID="${IRECOVERY_ECID#0x}"
|
||||||
|
IRECOVERY_ECID="${IRECOVERY_ECID#0X}"
|
||||||
|
[[ "$IRECOVERY_ECID" =~ ^[0-9A-Fa-f]{1,16}$ ]] || {
|
||||||
|
echo "[-] Invalid IRECOVERY_ECID: ${IRECOVERY_ECID}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
IRECOVERY_ECID="0x${IRECOVERY_ECID:u}"
|
||||||
|
IRECOVERY_ARGS=(-i "$IRECOVERY_ECID")
|
||||||
|
echo "[*] Using ECID selector for irecovery: ${IRECOVERY_ECID}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
irecovery_cmd() {
|
||||||
|
"$IRECOVERY" "${IRECOVERY_ARGS[@]}" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
if [[ ! -d "$RAMDISK_DIR" ]]; then
|
if [[ ! -d "$RAMDISK_DIR" ]]; then
|
||||||
echo "[-] Ramdisk directory not found: $RAMDISK_DIR"
|
echo "[-] Ramdisk directory not found: $RAMDISK_DIR"
|
||||||
echo " Run 'make ramdisk_build' first."
|
echo " Run 'make ramdisk_build' first."
|
||||||
@@ -30,48 +48,48 @@ fi
|
|||||||
|
|
||||||
# 1. Load iBSS + iBEC (DFU → recovery)
|
# 1. Load iBSS + iBEC (DFU → recovery)
|
||||||
echo " [1/8] Loading iBSS..."
|
echo " [1/8] Loading iBSS..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBSS.vresearch101.RELEASE.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/iBSS.vresearch101.RELEASE.img4"
|
||||||
|
|
||||||
echo " [2/8] Loading iBEC..."
|
echo " [2/8] Loading iBEC..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBEC.vresearch101.RELEASE.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/iBEC.vresearch101.RELEASE.img4"
|
||||||
"$IRECOVERY" -c go
|
irecovery_cmd -c go
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# 2. Load SPTM
|
# 2. Load SPTM
|
||||||
echo " [3/8] Loading SPTM..."
|
echo " [3/8] Loading SPTM..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/sptm.vresearch1.release.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/sptm.vresearch1.release.img4"
|
||||||
"$IRECOVERY" -c firmware
|
irecovery_cmd -c firmware
|
||||||
|
|
||||||
# 3. Load TXM
|
# 3. Load TXM
|
||||||
echo " [4/8] Loading TXM..."
|
echo " [4/8] Loading TXM..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/txm.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/txm.img4"
|
||||||
"$IRECOVERY" -c firmware
|
irecovery_cmd -c firmware
|
||||||
|
|
||||||
# 4. Load trustcache
|
# 4. Load trustcache
|
||||||
echo " [5/8] Loading trustcache..."
|
echo " [5/8] Loading trustcache..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/trustcache.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/trustcache.img4"
|
||||||
"$IRECOVERY" -c firmware
|
irecovery_cmd -c firmware
|
||||||
|
|
||||||
# 5. Load ramdisk
|
# 5. Load ramdisk
|
||||||
echo " [6/8] Loading ramdisk..."
|
echo " [6/8] Loading ramdisk..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/ramdisk.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/ramdisk.img4"
|
||||||
sleep 2
|
sleep 2
|
||||||
"$IRECOVERY" -c ramdisk
|
irecovery_cmd -c ramdisk
|
||||||
|
|
||||||
# 6. Load device tree
|
# 6. Load device tree
|
||||||
echo " [7/8] Loading device tree..."
|
echo " [7/8] Loading device tree..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/DeviceTree.vphone600ap.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/DeviceTree.vphone600ap.img4"
|
||||||
"$IRECOVERY" -c devicetree
|
irecovery_cmd -c devicetree
|
||||||
|
|
||||||
# 7. Load SEP
|
# 7. Load SEP
|
||||||
echo " [8/8] Loading SEP..."
|
echo " [8/8] Loading SEP..."
|
||||||
"$IRECOVERY" -f "$RAMDISK_DIR/sep-firmware.vresearch101.RELEASE.img4"
|
irecovery_cmd -f "$RAMDISK_DIR/sep-firmware.vresearch101.RELEASE.img4"
|
||||||
"$IRECOVERY" -c firmware
|
irecovery_cmd -c firmware
|
||||||
|
|
||||||
# 8. Load kernel and boot
|
# 8. Load kernel and boot
|
||||||
echo " [*] Booting kernel..."
|
echo " [*] Booting kernel..."
|
||||||
"$IRECOVERY" -f "$KERNEL_IMG"
|
irecovery_cmd -f "$KERNEL_IMG"
|
||||||
"$IRECOVERY" -c bootx
|
irecovery_cmd -c bootx
|
||||||
|
|
||||||
echo "[+] Boot sequence complete. Device should be booting into ramdisk."
|
echo "[+] Boot sequence complete. Device should be booting into ramdisk."
|
||||||
|
|||||||
+129
-26
@@ -17,7 +17,7 @@ cd "$PROJECT_ROOT"
|
|||||||
|
|
||||||
LOG_DIR="${PROJECT_ROOT}/setup_logs"
|
LOG_DIR="${PROJECT_ROOT}/setup_logs"
|
||||||
DFU_LOG="${LOG_DIR}/boot_dfu.log"
|
DFU_LOG="${LOG_DIR}/boot_dfu.log"
|
||||||
IPROXY_LOG="${LOG_DIR}/iproxy_2222.log"
|
IPROXY_LOG=""
|
||||||
|
|
||||||
DFU_PID=""
|
DFU_PID=""
|
||||||
IPROXY_PID=""
|
IPROXY_PID=""
|
||||||
@@ -32,9 +32,16 @@ POST_RESTORE_KILL_DELAY="${POST_RESTORE_KILL_DELAY:-30}"
|
|||||||
POST_KILL_SETTLE_DELAY="${POST_KILL_SETTLE_DELAY:-5}"
|
POST_KILL_SETTLE_DELAY="${POST_KILL_SETTLE_DELAY:-5}"
|
||||||
RAMDISK_SSH_TIMEOUT="${RAMDISK_SSH_TIMEOUT:-60}"
|
RAMDISK_SSH_TIMEOUT="${RAMDISK_SSH_TIMEOUT:-60}"
|
||||||
RAMDISK_SSH_INTERVAL="${RAMDISK_SSH_INTERVAL:-2}"
|
RAMDISK_SSH_INTERVAL="${RAMDISK_SSH_INTERVAL:-2}"
|
||||||
RAMDISK_SSH_PORT="${RAMDISK_SSH_PORT:-2222}"
|
RAMDISK_SSH_PORT="${RAMDISK_SSH_PORT:-}"
|
||||||
RAMDISK_SSH_USER="${RAMDISK_SSH_USER:-root}"
|
RAMDISK_SSH_USER="${RAMDISK_SSH_USER:-root}"
|
||||||
RAMDISK_SSH_PASS="${RAMDISK_SSH_PASS:-alpine}"
|
RAMDISK_SSH_PASS="${RAMDISK_SSH_PASS:-alpine}"
|
||||||
|
RAMDISK_SSH_PORT_EXPLICIT=0
|
||||||
|
if [[ -n "$RAMDISK_SSH_PORT" ]]; then
|
||||||
|
RAMDISK_SSH_PORT_EXPLICIT=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEVICE_UDID=""
|
||||||
|
DEVICE_ECID=""
|
||||||
JB_MODE=0
|
JB_MODE=0
|
||||||
DEV_MODE=0
|
DEV_MODE=0
|
||||||
SKIP_PROJECT_SETUP=0
|
SKIP_PROJECT_SETUP=0
|
||||||
@@ -49,6 +56,92 @@ require_cmd() {
|
|||||||
command -v "$cmd" >/dev/null 2>&1 || die "Missing required command: $cmd"
|
command -v "$cmd" >/dev/null 2>&1 || die "Missing required command: $cmd"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalize_ecid() {
|
||||||
|
local ecid="$1"
|
||||||
|
ecid="${ecid#0x}"
|
||||||
|
ecid="${ecid#0X}"
|
||||||
|
[[ "$ecid" =~ ^[0-9A-Fa-f]{1,16}$ ]] || return 1
|
||||||
|
printf "%016s" "${ecid:u}" | tr ' ' '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
load_device_identity() {
|
||||||
|
local prediction_file="${VM_DIR_ABS}/udid-prediction.txt"
|
||||||
|
local timeout=30
|
||||||
|
local waited=0
|
||||||
|
local key value
|
||||||
|
local udid_ecid
|
||||||
|
|
||||||
|
while [[ ! -f "$prediction_file" && "$waited" -lt "$timeout" ]]; do
|
||||||
|
if [[ -n "$DFU_PID" ]] && ! kill -0 "$DFU_PID" 2>/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
(( waited++ ))
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -f "$prediction_file" ]] || die "Missing ${prediction_file}. Rebuild and run make boot_dfu to generate it."
|
||||||
|
|
||||||
|
DEVICE_UDID=""
|
||||||
|
DEVICE_ECID=""
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
case "$key" in
|
||||||
|
UDID)
|
||||||
|
DEVICE_UDID="${value:u}"
|
||||||
|
;;
|
||||||
|
ECID)
|
||||||
|
DEVICE_ECID="$(normalize_ecid "$value" || true)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$prediction_file"
|
||||||
|
|
||||||
|
[[ "$DEVICE_UDID" =~ ^[0-9A-F]{8}-[0-9A-F]{16}$ ]] \
|
||||||
|
|| die "Invalid UDID in ${prediction_file}: ${DEVICE_UDID}"
|
||||||
|
|
||||||
|
if [[ -z "$DEVICE_ECID" ]]; then
|
||||||
|
DEVICE_ECID="${DEVICE_UDID#*-}"
|
||||||
|
fi
|
||||||
|
[[ "$DEVICE_ECID" =~ ^[0-9A-F]{16}$ ]] \
|
||||||
|
|| die "Invalid ECID in ${prediction_file}: ${DEVICE_ECID}"
|
||||||
|
|
||||||
|
udid_ecid="${DEVICE_UDID#*-}"
|
||||||
|
[[ "$udid_ecid" == "$DEVICE_ECID" ]] \
|
||||||
|
|| die "UDID/ECID mismatch in ${prediction_file}: ${DEVICE_UDID} vs 0x${DEVICE_ECID}"
|
||||||
|
|
||||||
|
echo "[+] Device identity loaded: UDID=${DEVICE_UDID} ECID=0x${DEVICE_ECID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
port_is_listening() {
|
||||||
|
local port="$1"
|
||||||
|
lsof -n -t -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
pick_random_ssh_port() {
|
||||||
|
local attempt port
|
||||||
|
for attempt in {1..200}; do
|
||||||
|
port=$((20000 + (RANDOM % 40000)))
|
||||||
|
if ! port_is_listening "$port"; then
|
||||||
|
echo "$port"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
choose_ramdisk_ssh_port() {
|
||||||
|
if [[ -n "$RAMDISK_SSH_PORT" ]]; then
|
||||||
|
[[ "$RAMDISK_SSH_PORT" == <-> ]] || die "RAMDISK_SSH_PORT must be an integer"
|
||||||
|
(( RAMDISK_SSH_PORT >= 1 && RAMDISK_SSH_PORT <= 65535 )) \
|
||||||
|
|| die "RAMDISK_SSH_PORT out of range: ${RAMDISK_SSH_PORT}"
|
||||||
|
if port_is_listening "$RAMDISK_SSH_PORT"; then
|
||||||
|
die "RAMDISK_SSH_PORT ${RAMDISK_SSH_PORT} is already in use"
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
RAMDISK_SSH_PORT="$(pick_random_ssh_port)" \
|
||||||
|
|| die "Failed to allocate a random local SSH forward port"
|
||||||
|
}
|
||||||
|
|
||||||
collect_vm_lock_pids() {
|
collect_vm_lock_pids() {
|
||||||
local -a paths pids
|
local -a paths pids
|
||||||
local path pid
|
local path pid
|
||||||
@@ -329,12 +422,19 @@ wait_for_post_restore_reboot() {
|
|||||||
|
|
||||||
wait_for_recovery() {
|
wait_for_recovery() {
|
||||||
local irecovery="${PROJECT_ROOT}/.limd/bin/irecovery"
|
local irecovery="${PROJECT_ROOT}/.limd/bin/irecovery"
|
||||||
|
local -a query_args
|
||||||
[[ -x "$irecovery" ]] || die "irecovery not found at $irecovery"
|
[[ -x "$irecovery" ]] || die "irecovery not found at $irecovery"
|
||||||
|
|
||||||
|
if [[ -n "$DEVICE_ECID" ]]; then
|
||||||
|
query_args=(-i "0x${DEVICE_ECID}")
|
||||||
|
else
|
||||||
|
query_args=()
|
||||||
|
fi
|
||||||
|
|
||||||
echo "[*] Waiting for recovery/DFU endpoint..."
|
echo "[*] Waiting for recovery/DFU endpoint..."
|
||||||
local i
|
local i
|
||||||
for i in {1..90}; do
|
for i in {1..90}; do
|
||||||
if "$irecovery" -q >/dev/null 2>&1; then
|
if "$irecovery" "${query_args[@]}" -q >/dev/null 2>&1; then
|
||||||
echo "[+] Device endpoint is reachable"
|
echo "[+] Device endpoint is reachable"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
@@ -346,29 +446,28 @@ wait_for_recovery() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
start_iproxy_2222() {
|
start_iproxy() {
|
||||||
local iproxy_bin
|
local iproxy_bin
|
||||||
local -a stale_pids
|
|
||||||
local pid
|
|
||||||
iproxy_bin="${PROJECT_ROOT}/.limd/bin/iproxy"
|
iproxy_bin="${PROJECT_ROOT}/.limd/bin/iproxy"
|
||||||
[[ -x "$iproxy_bin" ]] || die "iproxy not found at $iproxy_bin (run: make setup_libimobiledevice)"
|
[[ -x "$iproxy_bin" ]] || die "iproxy not found at $iproxy_bin (run: make setup_libimobiledevice)"
|
||||||
|
[[ -n "$DEVICE_UDID" ]] || die "Device UDID is empty; cannot start isolated iproxy"
|
||||||
|
|
||||||
stale_pids=(${(@f)$(lsof -n -t -iTCP:2222 -sTCP:LISTEN 2>/dev/null || true)})
|
choose_ramdisk_ssh_port
|
||||||
if (( ${#stale_pids[@]} > 0 )); then
|
|
||||||
echo "[*] Found stale listener(s) on tcp/2222, terminating..."
|
if port_is_listening "$RAMDISK_SSH_PORT"; then
|
||||||
for pid in "${stale_pids[@]}"; do
|
if [[ "$RAMDISK_SSH_PORT_EXPLICIT" == "1" ]]; then
|
||||||
[[ -z "$pid" || "$pid" == "$$" ]] && continue
|
die "RAMDISK_SSH_PORT ${RAMDISK_SSH_PORT} is already in use"
|
||||||
kill_descendants "$pid"
|
fi
|
||||||
kill -9 "$pid" >/dev/null 2>&1 || true
|
RAMDISK_SSH_PORT="$(pick_random_ssh_port)" \
|
||||||
done
|
|| die "Failed to allocate a free random local SSH forward port"
|
||||||
sleep 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
IPROXY_LOG="${LOG_DIR}/iproxy_${RAMDISK_SSH_PORT}.log"
|
||||||
mkdir -p "$LOG_DIR"
|
mkdir -p "$LOG_DIR"
|
||||||
: > "$IPROXY_LOG"
|
: > "$IPROXY_LOG"
|
||||||
|
|
||||||
echo "[*] Starting iproxy 2222 -> 22..."
|
echo "[*] Starting iproxy ${RAMDISK_SSH_PORT} -> 22 (UDID=${DEVICE_UDID})..."
|
||||||
("$iproxy_bin" 2222 22 >"$IPROXY_LOG" 2>&1) &
|
("$iproxy_bin" -u "$DEVICE_UDID" "$RAMDISK_SSH_PORT" 22 >"$IPROXY_LOG" 2>&1) &
|
||||||
IPROXY_PID=$!
|
IPROXY_PID=$!
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -431,14 +530,16 @@ wait_for_ramdisk_ssh() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "[-] Timed out waiting for ramdisk SSH readiness."
|
echo "[-] Timed out waiting for ramdisk SSH readiness."
|
||||||
echo "[-] iproxy log tail:"
|
if [[ -n "$IPROXY_LOG" ]]; then
|
||||||
tail -n 40 "$IPROXY_LOG" 2>/dev/null || true
|
echo "[-] iproxy log tail:"
|
||||||
|
tail -n 40 "$IPROXY_LOG" 2>/dev/null || true
|
||||||
|
fi
|
||||||
echo "[-] boot_dfu log tail:"
|
echo "[-] boot_dfu log tail:"
|
||||||
tail -n 60 "$DFU_LOG" 2>/dev/null || true
|
tail -n 60 "$DFU_LOG" 2>/dev/null || true
|
||||||
die "Ramdisk SSH did not become ready in ${RAMDISK_SSH_TIMEOUT}s."
|
die "Ramdisk SSH did not become ready in ${RAMDISK_SSH_TIMEOUT}s."
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_iproxy_2222() {
|
stop_iproxy() {
|
||||||
if [[ -n "$IPROXY_PID" ]] && kill -0 "$IPROXY_PID" 2>/dev/null; then
|
if [[ -n "$IPROXY_PID" ]] && kill -0 "$IPROXY_PID" 2>/dev/null; then
|
||||||
echo "[*] Stopping iproxy (pid=$IPROXY_PID)..."
|
echo "[*] Stopping iproxy (pid=$IPROXY_PID)..."
|
||||||
kill_descendants "$IPROXY_PID"
|
kill_descendants "$IPROXY_PID"
|
||||||
@@ -522,9 +623,10 @@ main() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo "=== Restore phase ==="
|
echo "=== Restore phase ==="
|
||||||
start_boot_dfu
|
start_boot_dfu
|
||||||
|
load_device_identity
|
||||||
wait_for_recovery
|
wait_for_recovery
|
||||||
run_make "Restore" restore_get_shsh
|
run_make "Restore" restore_get_shsh RESTORE_UDID="$DEVICE_UDID" RESTORE_ECID="0x$DEVICE_ECID"
|
||||||
run_make "Restore" restore
|
run_make "Restore" restore RESTORE_UDID="$DEVICE_UDID" RESTORE_ECID="0x$DEVICE_ECID"
|
||||||
wait_for_post_restore_reboot
|
wait_for_post_restore_reboot
|
||||||
stop_boot_dfu
|
stop_boot_dfu
|
||||||
echo "[*] Waiting ${POST_KILL_SETTLE_DELAY}s for cleanup before ramdisk stage..."
|
echo "[*] Waiting ${POST_KILL_SETTLE_DELAY}s for cleanup before ramdisk stage..."
|
||||||
@@ -533,16 +635,17 @@ main() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo "=== Ramdisk + CFW phase ==="
|
echo "=== Ramdisk + CFW phase ==="
|
||||||
start_boot_dfu
|
start_boot_dfu
|
||||||
|
load_device_identity
|
||||||
wait_for_recovery
|
wait_for_recovery
|
||||||
run_make "Ramdisk" ramdisk_build
|
run_make "Ramdisk" ramdisk_build
|
||||||
run_make "Ramdisk" ramdisk_send
|
run_make "Ramdisk" ramdisk_send IRECOVERY_ECID="0x$DEVICE_ECID"
|
||||||
start_iproxy_2222
|
start_iproxy
|
||||||
|
|
||||||
wait_for_ramdisk_ssh
|
wait_for_ramdisk_ssh
|
||||||
|
|
||||||
run_make "CFW install" "$cfw_install_target"
|
run_make "CFW install" "$cfw_install_target" SSH_PORT="$RAMDISK_SSH_PORT"
|
||||||
stop_boot_dfu
|
stop_boot_dfu
|
||||||
stop_iproxy_2222
|
stop_iproxy
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== First boot ==="
|
echo "=== First boot ==="
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ class VPhoneControl {
|
|||||||
let timeoutSeconds = max(Int(timeout.rounded()), 1)
|
let timeoutSeconds = max(Int(timeout.rounded()), 1)
|
||||||
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + timeout) { [weak self] in
|
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + timeout) { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
guard let pending = self.removePending(id: id) else { return }
|
guard let pending = removePending(id: id) else { return }
|
||||||
pending.handler(.failure(ControlError.requestTimedOut(type: type, seconds: timeoutSeconds)))
|
pending.handler(.failure(ControlError.requestTimedOut(type: type, seconds: timeoutSeconds)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class VPhoneFileBrowserModel {
|
|||||||
transferName = nil
|
transferName = nil
|
||||||
await refresh()
|
await refresh()
|
||||||
// Set error after refresh so refresh() doesn't clear it before the alert fires.
|
// Set error after refresh so refresh() doesn't clear it before the alert fires.
|
||||||
if let e = uploadError { self.error = e }
|
if let e = uploadError { error = e }
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewFolder(name: String) async {
|
func createNewFolder(name: String) async {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import Virtualization
|
|||||||
/// Minimum host OS for PV=3: macOS 15.0 (Sequoia)
|
/// Minimum host OS for PV=3: macOS 15.0 (Sequoia)
|
||||||
///
|
///
|
||||||
enum VPhoneHardware {
|
enum VPhoneHardware {
|
||||||
|
/// Fixed CPID for the current vphone hardware descriptor.
|
||||||
|
static let udidChipID: UInt32 = 0xFE01
|
||||||
|
|
||||||
static func createModel() throws -> VZMacHardwareModel {
|
static func createModel() throws -> VZMacHardwareModel {
|
||||||
// platformVersion=3, boardID=0x90, ISA=2 matches vresearch101
|
// platformVersion=3, boardID=0x90, ISA=2 matches vresearch101
|
||||||
let desc = Dynamic._VZMacHardwareModelDescriptor()
|
let desc = Dynamic._VZMacHardwareModelDescriptor()
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ class VPhoneLocationProvider: NSObject {
|
|||||||
private var replayTask: Task<Void, Never>?
|
private var replayTask: Task<Void, Never>?
|
||||||
private var replayName: String?
|
private var replayName: String?
|
||||||
|
|
||||||
var isReplaying: Bool { replayTask != nil }
|
var isReplaying: Bool {
|
||||||
|
replayTask != nil
|
||||||
|
}
|
||||||
|
|
||||||
init(control: VPhoneControl) {
|
init(control: VPhoneControl) {
|
||||||
self.control = control
|
self.control = control
|
||||||
@@ -135,7 +137,7 @@ class VPhoneLocationProvider: NSObject {
|
|||||||
var index = 0
|
var index = 0
|
||||||
while !Task.isCancelled {
|
while !Task.isCancelled {
|
||||||
let point = points[index]
|
let point = points[index]
|
||||||
self.sendSimulatedLocation(
|
sendSimulatedLocation(
|
||||||
latitude: point.latitude,
|
latitude: point.latitude,
|
||||||
longitude: point.longitude,
|
longitude: point.longitude,
|
||||||
altitude: point.altitude,
|
altitude: point.altitude,
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ class VPhoneVirtualMachine: NSObject, VZVirtualMachineDelegate {
|
|||||||
var kernelDebugPort: Int = 5909
|
var kernelDebugPort: Int = 5909
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct DeviceIdentity {
|
||||||
|
let cpidHex: String
|
||||||
|
let ecidHex: String
|
||||||
|
let udid: String
|
||||||
|
}
|
||||||
|
|
||||||
init(options: Options) throws {
|
init(options: Options) throws {
|
||||||
// --- Hardware model (PV=3) ---
|
// --- Hardware model (PV=3) ---
|
||||||
let hwModel = try VPhoneHardware.createModel()
|
let hwModel = try VPhoneHardware.createModel()
|
||||||
@@ -36,17 +42,34 @@ class VPhoneVirtualMachine: NSObject, VZVirtualMachineDelegate {
|
|||||||
let platform = VZMacPlatformConfiguration()
|
let platform = VZMacPlatformConfiguration()
|
||||||
|
|
||||||
// Persist machineIdentifier for stable ECID
|
// Persist machineIdentifier for stable ECID
|
||||||
|
let machineIdentifier: VZMacMachineIdentifier
|
||||||
if let savedData = try? Data(contentsOf: options.machineIDURL),
|
if let savedData = try? Data(contentsOf: options.machineIDURL),
|
||||||
let savedID = VZMacMachineIdentifier(dataRepresentation: savedData)
|
let savedID = VZMacMachineIdentifier(dataRepresentation: savedData)
|
||||||
{
|
{
|
||||||
platform.machineIdentifier = savedID
|
machineIdentifier = savedID
|
||||||
print("[vphone] Loaded machineIdentifier (ECID stable)")
|
print("[vphone] Loaded machineIdentifier (ECID stable)")
|
||||||
} else {
|
} else {
|
||||||
let newID = VZMacMachineIdentifier()
|
let newID = VZMacMachineIdentifier()
|
||||||
platform.machineIdentifier = newID
|
machineIdentifier = newID
|
||||||
try newID.dataRepresentation.write(to: options.machineIDURL)
|
try newID.dataRepresentation.write(to: options.machineIDURL)
|
||||||
print("[vphone] Created new machineIdentifier -> \(options.machineIDURL.lastPathComponent)")
|
print("[vphone] Created new machineIdentifier -> \(options.machineIDURL.lastPathComponent)")
|
||||||
}
|
}
|
||||||
|
platform.machineIdentifier = machineIdentifier
|
||||||
|
|
||||||
|
if let identity = Self.resolveDeviceIdentity(machineIdentifier: machineIdentifier) {
|
||||||
|
print("[vphone] ECID: 0x\(identity.ecidHex)")
|
||||||
|
print("[vphone] Predicted UDID: \(identity.udid)")
|
||||||
|
do {
|
||||||
|
let outputURL = try Self.writeUDIDPrediction(
|
||||||
|
identity: identity, machineIDURL: options.machineIDURL
|
||||||
|
)
|
||||||
|
print("[vphone] Wrote UDID prediction: \(outputURL.path)")
|
||||||
|
} catch {
|
||||||
|
print("[vphone] Warning: failed to write udid-prediction.txt: \(error)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("[vphone] Warning: failed to resolve ECID from machineIdentifier")
|
||||||
|
}
|
||||||
|
|
||||||
let auxStorage = try VZMacAuxiliaryStorage(
|
let auxStorage = try VZMacAuxiliaryStorage(
|
||||||
creatingStorageAt: options.nvramURL,
|
creatingStorageAt: options.nvramURL,
|
||||||
@@ -204,6 +227,39 @@ class VPhoneVirtualMachine: NSObject, VZVirtualMachineDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func resolveDeviceIdentity(machineIdentifier: VZMacMachineIdentifier)
|
||||||
|
-> DeviceIdentity?
|
||||||
|
{
|
||||||
|
let ecidValue: UInt64? = if let ecid = Dynamic(machineIdentifier)._ECID.asUInt64 {
|
||||||
|
ecid
|
||||||
|
} else if let ecidNumber = Dynamic(machineIdentifier)._ECID.asObject as? NSNumber {
|
||||||
|
ecidNumber.uint64Value
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let ecidValue else { return nil }
|
||||||
|
|
||||||
|
let cpidHex = String(format: "%08X", VPhoneHardware.udidChipID)
|
||||||
|
let ecidHex = String(format: "%016llX", ecidValue)
|
||||||
|
let udid = "\(cpidHex)-\(ecidHex)"
|
||||||
|
return DeviceIdentity(cpidHex: cpidHex, ecidHex: ecidHex, udid: udid)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func writeUDIDPrediction(identity: DeviceIdentity, machineIDURL: URL) throws -> URL {
|
||||||
|
let outputURL = machineIDURL.deletingLastPathComponent().appendingPathComponent(
|
||||||
|
"udid-prediction.txt"
|
||||||
|
)
|
||||||
|
let content = """
|
||||||
|
UDID=\(identity.udid)
|
||||||
|
CPID=0x\(identity.cpidHex)
|
||||||
|
ECID=0x\(identity.ecidHex)
|
||||||
|
MACHINE_IDENTIFIER=\(machineIDURL.lastPathComponent)
|
||||||
|
"""
|
||||||
|
try content.write(to: outputURL, atomically: true, encoding: .utf8)
|
||||||
|
return outputURL
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Battery
|
// MARK: - Battery
|
||||||
|
|
||||||
/// Update the synthetic battery charge and connectivity at runtime.
|
/// Update the synthetic battery charge and connectivity at runtime.
|
||||||
|
|||||||
Reference in New Issue
Block a user