mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-09-05 17:14:28 +00:00
Remove temporary testing scripts and Makefile targets
These were used for per-patch kernel JB debugging (C23 bisect, single-patch boot test, batch testing). No longer needed now that A2, C23, and C24 are all boot-tested and fixed.
This commit is contained in:
@@ -71,11 +71,6 @@ help:
|
||||
@echo "Ramdisk:"
|
||||
@echo " make ramdisk_build Build signed SSH ramdisk"
|
||||
@echo " make ramdisk_send Send ramdisk to device"
|
||||
@echo " make testing_ramdisk_build Build boot chain only (no SSH, no CFW)"
|
||||
@echo " make testing_ramdisk_send Send testing boot chain to device"
|
||||
@echo " make testing_do_save Full pipeline + save base kernel backup"
|
||||
@echo " make testing_do_patch PATCH=name Test single JB kernel patch (fast)"
|
||||
@echo " make testing_kernel_patch PATCH=name Restore+patch kernel only (no boot)"
|
||||
@echo ""
|
||||
@echo "CFW:"
|
||||
@echo " make cfw_install Install CFW mods via SSH"
|
||||
@@ -222,7 +217,7 @@ restore:
|
||||
# Ramdisk
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
.PHONY: ramdisk_build ramdisk_send testing_ramdisk_build testing_ramdisk_send testing_do testing_do_save testing_kernel_patch testing_do_patch testing_c23_bisect
|
||||
.PHONY: ramdisk_build ramdisk_send
|
||||
|
||||
ramdisk_build:
|
||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/ramdisk_build.py" .
|
||||
@@ -230,30 +225,6 @@ ramdisk_build:
|
||||
ramdisk_send:
|
||||
cd $(VM_DIR) && IRECOVERY="$(CURDIR)/$(IRECOVERY)" zsh "$(CURDIR)/$(SCRIPTS)/ramdisk_send.sh"
|
||||
|
||||
testing_ramdisk_build:
|
||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/testing_ramdisk_build.py" .
|
||||
|
||||
testing_ramdisk_send:
|
||||
cd $(VM_DIR) && IRECOVERY="$(CURDIR)/$(IRECOVERY)" zsh "$(CURDIR)/$(SCRIPTS)/testing_ramdisk_send.sh"
|
||||
|
||||
testing_do:
|
||||
zsh "$(CURDIR)/$(SCRIPTS)/testing_do.sh"
|
||||
|
||||
testing_do_save:
|
||||
zsh "$(CURDIR)/$(SCRIPTS)/testing_do_save.sh"
|
||||
|
||||
testing_kernel_patch:
|
||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/testing_kernel_patch.py" . $(PATCH)
|
||||
|
||||
testing_do_patch:
|
||||
zsh "$(CURDIR)/$(SCRIPTS)/testing_do_patch.sh" $(PATCH)
|
||||
|
||||
testing_batch:
|
||||
zsh "$(CURDIR)/$(SCRIPTS)/testing_batch.sh" $(PATCHES)
|
||||
|
||||
testing_c23_bisect:
|
||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/testing_c23_bisect.py" . $(VARIANT)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# CFW
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# testing_batch.sh — Batch-test kernel JB patches one at a time.
|
||||
#
|
||||
# Prerequisite: run `make testing_do_save` first to create base backup.
|
||||
#
|
||||
# For each patch:
|
||||
# 1. Restore base kernel + apply single patch
|
||||
# 2. Build ramdisk
|
||||
# 3. Boot DFU (vphone-cli --no-graphics --dfu)
|
||||
# 4. Send boot chain via irecovery
|
||||
# 5. Wait up to 2 minutes, watching for panic
|
||||
# 6. Save output → testing_results/<patch_name>.log
|
||||
# 7. Kill everything, move to next patch
|
||||
#
|
||||
# Usage:
|
||||
# ./testing_batch.sh # test all uncommented B/C patches
|
||||
# ./testing_batch.sh patch_mac_mount patch_dounmount # test specific patches
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${0:a:h}")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
VM_DIR="$PROJECT_DIR/${VM_DIR:-vm}"
|
||||
RESULTS_DIR="$PROJECT_DIR/testing_results"
|
||||
TIMEOUT_SECS=120 # 2 minutes
|
||||
|
||||
BINARY="$PROJECT_DIR/.build/release/vphone-cli"
|
||||
IRECOVERY="$PROJECT_DIR/.limd/bin/irecovery"
|
||||
PYTHON="$PROJECT_DIR/.venv/bin/python3"
|
||||
|
||||
mkdir -p "$RESULTS_DIR"
|
||||
|
||||
# ─── Default patch list (all B + C patches) ────────────────────────
|
||||
ALL_PATCHES=(
|
||||
patch_post_validation_additional # B5
|
||||
patch_proc_security_policy # B6
|
||||
patch_proc_pidinfo # B7
|
||||
patch_convert_port_to_map # B8
|
||||
patch_vm_fault_enter_prepare # B9
|
||||
patch_vm_map_protect # B10
|
||||
patch_mac_mount # B11
|
||||
patch_dounmount # B12
|
||||
patch_bsd_init_auth # B13
|
||||
patch_spawn_validate_persona # B14
|
||||
patch_task_for_pid # B15
|
||||
patch_load_dylinker # B16
|
||||
patch_shared_region_map # B17
|
||||
patch_nvram_verify_permission # B18
|
||||
patch_io_secure_bsd_root # B19
|
||||
patch_thid_should_crash # B20
|
||||
patch_cred_label_update_execve # C21
|
||||
patch_syscallmask_apply_to_proc # C22
|
||||
patch_hook_cred_label_update_execve # C23
|
||||
patch_kcall10 # C24
|
||||
)
|
||||
|
||||
# Use args if provided, otherwise test all
|
||||
if (( $# > 0 )); then
|
||||
PATCHES=("$@")
|
||||
else
|
||||
PATCHES=("${ALL_PATCHES[@]}")
|
||||
fi
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " Batch kernel JB patch tester"
|
||||
echo " Testing ${#PATCHES[@]} patch(es), ${TIMEOUT_SECS}s timeout each"
|
||||
echo " Results → $RESULTS_DIR/"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# ─── Verify backup exists ──────────────────────────────────────────
|
||||
RESTORE_DIR=$(find "$VM_DIR" -maxdepth 1 -type d -name '*Restore*' | head -1)
|
||||
KERNEL_BACKUP=$(find "$RESTORE_DIR" -name 'kernelcache.research.vphone600.base_backup' 2>/dev/null | head -1)
|
||||
if [[ -z "$KERNEL_BACKUP" ]]; then
|
||||
echo "[-] No kernel backup found. Run 'make testing_do_save' first."
|
||||
exit 1
|
||||
fi
|
||||
echo "[*] Kernel backup: $KERNEL_BACKUP"
|
||||
echo ""
|
||||
|
||||
# ─── Summary file ──────────────────────────────────────────────────
|
||||
SUMMARY="$RESULTS_DIR/_summary.txt"
|
||||
echo "Batch test run: $(date)" > "$SUMMARY"
|
||||
echo "Timeout: ${TIMEOUT_SECS}s" >> "$SUMMARY"
|
||||
echo "─────────────────────────────────────────" >> "$SUMMARY"
|
||||
|
||||
# ─── Test each patch ───────────────────────────────────────────────
|
||||
PASS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
ERROR_COUNT=0
|
||||
TOTAL=${#PATCHES[@]}
|
||||
|
||||
for i in {1..$TOTAL}; do
|
||||
PATCH="${PATCHES[$i]}"
|
||||
LOG="$RESULTS_DIR/${PATCH}.log"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " [$i/$TOTAL] Testing: $PATCH"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Clean up any prior vphone-cli
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Header in log
|
||||
{
|
||||
echo "Patch: $PATCH"
|
||||
echo "Date: $(date)"
|
||||
echo "Timeout: ${TIMEOUT_SECS}s"
|
||||
echo "══════════════════════════════════════════"
|
||||
} > "$LOG"
|
||||
|
||||
# Step 1: Restore kernel + apply patch
|
||||
echo " [1/4] Patching kernel..."
|
||||
{
|
||||
echo ""
|
||||
echo "── kernel patch ──"
|
||||
"$PYTHON" "$PROJECT_DIR/scripts/testing_kernel_patch.py" "$VM_DIR" "$PATCH" 2>&1
|
||||
} >> "$LOG" 2>&1
|
||||
|
||||
PATCH_EXIT=$?
|
||||
if (( PATCH_EXIT != 0 )); then
|
||||
echo " [-] Patch failed (exit $PATCH_EXIT) — skipping"
|
||||
echo "$PATCH ERROR patch_failed" >> "$SUMMARY"
|
||||
ERROR_COUNT=$(( ERROR_COUNT + 1 ))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Step 2: Build ramdisk
|
||||
echo " [2/4] Building ramdisk..."
|
||||
{
|
||||
echo ""
|
||||
echo "── ramdisk build ──"
|
||||
make -C "$PROJECT_DIR" testing_ramdisk_build VM_DIR="$VM_DIR" 2>&1
|
||||
} >> "$LOG" 2>&1
|
||||
|
||||
# Step 3: Boot DFU (capture output)
|
||||
echo " [3/4] Booting DFU..."
|
||||
{
|
||||
echo ""
|
||||
echo "── boot output ──"
|
||||
} >> "$LOG"
|
||||
|
||||
# Start vphone-cli in background, capturing output
|
||||
"$BINARY" \
|
||||
--rom "$VM_DIR/AVPBooter.vresearch1.bin" \
|
||||
--disk "$VM_DIR/Disk.img" \
|
||||
--nvram "$VM_DIR/nvram.bin" \
|
||||
--machine-id "$VM_DIR/machineIdentifier.bin" \
|
||||
--cpu 8 --memory 8192 \
|
||||
--sep-rom "$VM_DIR/AVPSEPBooter.vresearch1.bin" \
|
||||
--sep-storage "$VM_DIR/SEPStorage" \
|
||||
--no-graphics --dfu \
|
||||
>> "$LOG" 2>&1 &
|
||||
VM_PID=$!
|
||||
|
||||
# Step 4: Send boot chain
|
||||
echo " [4/4] Sending boot chain..."
|
||||
{
|
||||
echo ""
|
||||
echo "── ramdisk send ──"
|
||||
make -C "$PROJECT_DIR" testing_ramdisk_send VM_DIR="$VM_DIR" 2>&1
|
||||
} >> "$LOG" 2>&1 || true
|
||||
|
||||
# ─── Monitor for panic / success / timeout ─────────────────────
|
||||
echo " [*] Monitoring for ${TIMEOUT_SECS}s..."
|
||||
|
||||
RESULT="TIMEOUT"
|
||||
START_TIME=$SECONDS
|
||||
|
||||
while (( SECONDS - START_TIME < TIMEOUT_SECS )); do
|
||||
# Check if VM died
|
||||
if ! kill -0 "$VM_PID" 2>/dev/null; then
|
||||
RESULT="VM_DIED"
|
||||
break
|
||||
fi
|
||||
|
||||
# Check log for panic
|
||||
if grep -qi 'panic' "$LOG" 2>/dev/null; then
|
||||
sleep 5 # let full panic log flush
|
||||
RESULT="PANIC"
|
||||
break
|
||||
fi
|
||||
|
||||
# Check log for successful boot (lockdownd socket accepted)
|
||||
if grep -q 'sock.*accepted.*62078' "$LOG" 2>/dev/null; then
|
||||
RESULT="BOOT_OK"
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 3
|
||||
done
|
||||
|
||||
ELAPSED=$(( SECONDS - START_TIME ))
|
||||
|
||||
# Kill VM
|
||||
kill -9 "$VM_PID" 2>/dev/null || true
|
||||
wait "$VM_PID" 2>/dev/null || true
|
||||
# Also kill any orphaned vphone-cli
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
|
||||
# Append result to log
|
||||
{
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════"
|
||||
echo "RESULT: $RESULT"
|
||||
echo "ELAPSED: ${ELAPSED}s"
|
||||
} >> "$LOG"
|
||||
|
||||
# Record
|
||||
case "$RESULT" in
|
||||
BOOT_OK)
|
||||
echo " [+] BOOT OK after ${ELAPSED}s → $LOG"
|
||||
echo "$PATCH BOOT_OK ${ELAPSED}s" >> "$SUMMARY"
|
||||
PASS_COUNT=$(( PASS_COUNT + 1 ))
|
||||
;;
|
||||
PANIC)
|
||||
echo " [X] PANIC after ${ELAPSED}s → $LOG"
|
||||
echo "$PATCH PANIC ${ELAPSED}s" >> "$SUMMARY"
|
||||
FAIL_COUNT=$(( FAIL_COUNT + 1 ))
|
||||
;;
|
||||
VM_DIED)
|
||||
echo " [X] VM died after ${ELAPSED}s → $LOG"
|
||||
echo "$PATCH VM_DIED ${ELAPSED}s" >> "$SUMMARY"
|
||||
FAIL_COUNT=$(( FAIL_COUNT + 1 ))
|
||||
;;
|
||||
TIMEOUT)
|
||||
echo " [?] TIMEOUT after ${TIMEOUT_SECS}s → $LOG"
|
||||
echo "$PATCH TIMEOUT ${TIMEOUT_SECS}s" >> "$SUMMARY"
|
||||
ERROR_COUNT=$(( ERROR_COUNT + 1 ))
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# ─── Final summary ─────────────────────────────────────────────────
|
||||
{
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo "TOTAL: $TOTAL PASS: $PASS_COUNT FAIL: $FAIL_COUNT ERROR: $ERROR_COUNT"
|
||||
} >> "$SUMMARY"
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " DONE — $TOTAL tested: $PASS_COUNT pass, $FAIL_COUNT fail, $ERROR_COUNT error"
|
||||
echo " Summary: $SUMMARY"
|
||||
echo " Logs: $RESULTS_DIR/<patch_name>.log"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
cat "$SUMMARY"
|
||||
@@ -1,258 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
testing_c23_bisect.py — Bisect C23 shellcode to find which part causes PAC panic.
|
||||
|
||||
Usage:
|
||||
python3 testing_c23_bisect.py <vm_dir> <variant>
|
||||
|
||||
Variants (progressive complexity):
|
||||
A — PACIBSP + save/restore regs + B hook+4 (stack frame, no calls)
|
||||
B — A + mrs tpidr_el1 + vfs_context build (register reads, no calls)
|
||||
C — B + BL vnode_getattr (external function call)
|
||||
D — C + ownership propagation (uid/gid/csflags writes)
|
||||
E — full shellcode (same as kernel_jb_patch_hook_cred_label.py)
|
||||
|
||||
Each variant is strictly additive — if A boots, B adds only the next layer.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from fw_patch import find_file, find_restore_dir, load_firmware, save_firmware
|
||||
from patchers.kernel_jb import KernelJBPatcher
|
||||
from patchers.kernel_jb_base import asm, _rd32, _rd64, NOP
|
||||
|
||||
PACIBSP = bytes([0x7F, 0x23, 0x03, 0xD5])
|
||||
|
||||
|
||||
def build_variant(kp, variant, cave, orig_hook, vnode_getattr_off):
|
||||
"""Build shellcode for the given variant, return list of 4-byte parts."""
|
||||
|
||||
# Helper: encode BL/B
|
||||
def bl(src, dst):
|
||||
return kp._encode_bl(src, dst)
|
||||
|
||||
def b(src, dst):
|
||||
return kp._encode_b(src, dst)
|
||||
|
||||
# B resume always at the last slot
|
||||
# We'll pad all variants to 46 slots for consistency.
|
||||
#
|
||||
# Variant A: stack frame only
|
||||
# Variant B: + tpidr_el1 / vfs_context
|
||||
# Variant C: + BL vnode_getattr
|
||||
# Variant D: + ownership propagation
|
||||
# Variant E: full (same as production)
|
||||
|
||||
parts = []
|
||||
|
||||
if variant in ("A", "B", "C", "D", "E"):
|
||||
parts.append(PACIBSP) # 0: relocated from hook
|
||||
# In full shellcode, slot 1 is: cbz x3, #0xb0 → slot 45
|
||||
# For variant A, skip the cbz (just NOP), so we always enter the frame
|
||||
if variant == "A":
|
||||
parts.append(NOP) # 1
|
||||
else:
|
||||
parts.append(asm("cbz x3, #0xb0")) # 1: if vp==NULL → slot 45
|
||||
parts.append(asm("sub sp, sp, #0x400")) # 2
|
||||
parts.append(asm("stp x29, x30, [sp]")) # 3
|
||||
parts.append(asm("stp x0, x1, [sp, #16]")) # 4
|
||||
parts.append(asm("stp x2, x3, [sp, #32]")) # 5
|
||||
parts.append(asm("stp x4, x5, [sp, #48]")) # 6
|
||||
parts.append(asm("stp x6, x7, [sp, #64]")) # 7
|
||||
|
||||
if variant in ("B", "C", "D", "E"):
|
||||
# Build vfs_context
|
||||
parts.append(asm("mrs x8, tpidr_el1")) # 8: current_thread
|
||||
parts.append(asm("stp x8, x0, [sp, #0x70]")) # 9: {thread, cred}
|
||||
parts.append(asm("add x2, sp, #0x70")) # 10: ctx = &vfs_ctx
|
||||
# Setup vnode_getattr args
|
||||
parts.append(asm("ldr x0, [sp, #0x28]")) # 11: x0 = vp (saved x3)
|
||||
parts.append(asm("add x1, sp, #0x80")) # 12: x1 = &vattr
|
||||
parts.append(asm("mov w8, #0x380")) # 13: vattr size
|
||||
parts.append(asm("stp xzr, x8, [x1]")) # 14: init vattr
|
||||
parts.append(asm("stp xzr, xzr, [x1, #0x10]")) # 15: init vattr+16
|
||||
parts.append(NOP) # 16
|
||||
parts.append(NOP) # 17
|
||||
elif variant == "A":
|
||||
# Pad slots 8-17 with NOP
|
||||
for _ in range(10):
|
||||
parts.append(NOP)
|
||||
|
||||
if variant in ("C", "D", "E"):
|
||||
# BL vnode_getattr
|
||||
vnode_bl_off = cave + 18 * 4
|
||||
vnode_bl = bl(vnode_bl_off, vnode_getattr_off)
|
||||
if not vnode_bl:
|
||||
print(" [-] BL to vnode_getattr out of range")
|
||||
return None
|
||||
parts.append(vnode_bl) # 18: BL vnode_getattr
|
||||
elif variant in ("A", "B"):
|
||||
parts.append(NOP) # 18
|
||||
|
||||
if variant in ("C", "D", "E"):
|
||||
# After BL, check result — jump to restore on error
|
||||
parts.append(asm("cbnz x0, #0x4c")) # 19: error → slot 38
|
||||
elif variant in ("A", "B"):
|
||||
parts.append(NOP) # 19
|
||||
|
||||
if variant in ("D", "E"):
|
||||
# Ownership propagation
|
||||
parts.append(asm("mov w2, #0")) # 20: changed = 0
|
||||
parts.append(asm("ldr w8, [sp, #0xCC]")) # 21: va_mode
|
||||
parts.append(bytes([0xA8, 0x00, 0x58, 0x36])) # 22: tbz w8,#11
|
||||
parts.append(asm("ldr w8, [sp, #0xC4]")) # 23: va_uid
|
||||
parts.append(asm("ldr x0, [sp, #0x18]")) # 24: new_cred
|
||||
parts.append(asm("str w8, [x0, #0x18]")) # 25: cred->uid
|
||||
parts.append(asm("mov w2, #1")) # 26: changed = 1
|
||||
parts.append(asm("ldr w8, [sp, #0xCC]")) # 27: va_mode
|
||||
parts.append(bytes([0xA8, 0x00, 0x50, 0x36])) # 28: tbz w8,#10
|
||||
parts.append(asm("mov w2, #1")) # 29: changed = 1
|
||||
parts.append(asm("ldr w8, [sp, #0xC8]")) # 30: va_gid
|
||||
parts.append(asm("ldr x0, [sp, #0x18]")) # 31: new_cred
|
||||
parts.append(asm("str w8, [x0, #0x28]")) # 32: cred->gid
|
||||
parts.append(asm("cbz w2, #0x14")) # 33: if !changed → slot 38
|
||||
parts.append(asm("ldr x0, [sp, #0x20]")) # 34: proc
|
||||
parts.append(asm("ldr w8, [x0, #0x454]")) # 35: p_csflags
|
||||
parts.append(asm("orr w8, w8, #0x100")) # 36: CS_VALID
|
||||
parts.append(asm("str w8, [x0, #0x454]")) # 37: store
|
||||
elif variant in ("A", "B", "C"):
|
||||
# Pad slots 20-37 with NOP
|
||||
for _ in range(18):
|
||||
parts.append(NOP)
|
||||
|
||||
# Restore and resume — always present (slots 38-45)
|
||||
if variant in ("A", "B", "C", "D", "E"):
|
||||
parts.append(asm("ldp x0, x1, [sp, #16]")) # 38
|
||||
parts.append(asm("ldp x2, x3, [sp, #32]")) # 39
|
||||
parts.append(asm("ldp x4, x5, [sp, #48]")) # 40
|
||||
parts.append(asm("ldp x6, x7, [sp, #64]")) # 41
|
||||
parts.append(asm("ldp x29, x30, [sp]")) # 42
|
||||
parts.append(asm("add sp, sp, #0x400")) # 43
|
||||
parts.append(NOP) # 44
|
||||
|
||||
# B hook+4
|
||||
b_resume_off = cave + 45 * 4
|
||||
b_resume = b(b_resume_off, orig_hook + 4)
|
||||
if not b_resume:
|
||||
print(" [-] B to hook+4 out of range")
|
||||
return None
|
||||
parts.append(b_resume) # 45
|
||||
|
||||
assert len(parts) == 46, f"Expected 46 parts, got {len(parts)}"
|
||||
return parts
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: {sys.argv[0]} <vm_dir> <variant>")
|
||||
print(f" Variants: A B C D E")
|
||||
sys.exit(1)
|
||||
|
||||
vm_dir = os.path.abspath(sys.argv[1])
|
||||
variant = sys.argv[2].upper()
|
||||
if variant not in ("A", "B", "C", "D", "E"):
|
||||
print(f"[-] Unknown variant: {variant}")
|
||||
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")
|
||||
backup_path = kernel_path + ".base_backup"
|
||||
|
||||
if not os.path.exists(backup_path):
|
||||
print(f"[-] No backup found: {backup_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Restore from backup
|
||||
shutil.copy2(backup_path, kernel_path)
|
||||
print(f"[*] Restored kernel from backup")
|
||||
|
||||
# Load
|
||||
im4p, data, was_im4p, original_raw = load_firmware(kernel_path)
|
||||
print(f"[*] Loaded: {len(data)} bytes")
|
||||
|
||||
kp = KernelJBPatcher(data)
|
||||
|
||||
# ── Find vnode_getattr ──
|
||||
vnode_getattr_off = kp._resolve_symbol("_vnode_getattr")
|
||||
if vnode_getattr_off < 0:
|
||||
vnode_getattr_off = kp._find_vnode_getattr_via_string()
|
||||
if vnode_getattr_off < 0:
|
||||
print("[-] vnode_getattr not found")
|
||||
sys.exit(1)
|
||||
print(f"[+] vnode_getattr at 0x{vnode_getattr_off:X}")
|
||||
|
||||
# ── Find sandbox ops table ──
|
||||
ops_table = kp._find_sandbox_ops_table_via_conf()
|
||||
if ops_table is None:
|
||||
print("[-] sandbox ops table not found")
|
||||
sys.exit(1)
|
||||
|
||||
# ── Find hook (largest in ops[0:30]) ──
|
||||
hook_index = -1
|
||||
orig_hook = -1
|
||||
best_size = 0
|
||||
for idx in range(0, 30):
|
||||
entry = kp._read_ops_entry(ops_table, idx)
|
||||
if entry is None or entry <= 0:
|
||||
continue
|
||||
if not any(s <= entry < e for s, e in kp.code_ranges):
|
||||
continue
|
||||
fend = kp._find_func_end(entry, 0x2000)
|
||||
fsize = fend - entry
|
||||
if fsize > best_size:
|
||||
best_size = fsize
|
||||
hook_index = idx
|
||||
orig_hook = entry
|
||||
|
||||
if hook_index < 0 or best_size < 1000:
|
||||
print(f"[-] hook not found (best: idx={hook_index}, size={best_size})")
|
||||
sys.exit(1)
|
||||
print(f"[+] hook at ops[{hook_index}] = 0x{orig_hook:X} ({best_size} bytes)")
|
||||
|
||||
# Verify PACIBSP
|
||||
first_insn = data[orig_hook:orig_hook + 4]
|
||||
if first_insn != PACIBSP:
|
||||
print(f"[-] first insn not PACIBSP (got 0x{_rd32(data, orig_hook):08X})")
|
||||
sys.exit(1)
|
||||
|
||||
# ── Find code cave (200 bytes) ──
|
||||
cave = kp._find_code_cave(200)
|
||||
if cave < 0:
|
||||
print("[-] no code cave found")
|
||||
sys.exit(1)
|
||||
print(f"[+] code cave at 0x{cave:X}")
|
||||
|
||||
# ── Build variant shellcode ──
|
||||
print(f"\n[*] Building variant {variant}")
|
||||
parts = build_variant(kp, variant, cave, orig_hook, vnode_getattr_off)
|
||||
if parts is None:
|
||||
sys.exit(1)
|
||||
|
||||
# Write shellcode to data
|
||||
for i, part in enumerate(parts):
|
||||
off = cave + i * 4
|
||||
data[off:off + 4] = part
|
||||
|
||||
# Patch function entry: PACIBSP → B cave
|
||||
b_to_cave = kp._encode_b(orig_hook, cave)
|
||||
if not b_to_cave:
|
||||
print("[-] B to cave out of range")
|
||||
sys.exit(1)
|
||||
data[orig_hook:orig_hook + 4] = b_to_cave
|
||||
|
||||
print(f"[+] Variant {variant}: {len(parts)} instructions written to cave")
|
||||
print(f"[+] Trampoline: B 0x{cave:X} at 0x{orig_hook:X}")
|
||||
|
||||
# Save
|
||||
save_firmware(kernel_path, im4p, data, was_im4p, original_raw)
|
||||
print(f"[+] Saved: {kernel_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
# Bisect C23 shellcode — restore, patch variant, rebuild ramdisk, boot.
|
||||
#
|
||||
# Usage: ./testing_c23_bisect.sh <variant>
|
||||
# or: make testing_c23_bisect_boot VARIANT=A
|
||||
#
|
||||
# Variants: A B C D E (see testing_c23_bisect.py)
|
||||
|
||||
typeset -a CHILD_PIDS=()
|
||||
|
||||
cleanup() {
|
||||
echo "\n[c23] cleaning up..."
|
||||
for pid in "${CHILD_PIDS[@]}"; do
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "[c23] killing PID $pid"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${0:a:h}")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
VARIANT="${1:-}"
|
||||
if [[ -z "$VARIANT" ]]; then
|
||||
echo "Usage: $0 <variant>"
|
||||
echo " Variants: A B C D E"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VM_DIR="${VM_DIR:-vm}"
|
||||
|
||||
echo "[c23] ═══════════════════════════════════════════"
|
||||
echo "[c23] Bisect variant: $VARIANT"
|
||||
echo "[c23] ═══════════════════════════════════════════"
|
||||
|
||||
# Kill existing
|
||||
echo "[c23] killing existing vphone-cli..."
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Restore + patch variant
|
||||
echo "[c23] restoring base kernel + applying variant $VARIANT"
|
||||
make testing_c23_bisect VARIANT="$VARIANT"
|
||||
|
||||
# Rebuild ramdisk
|
||||
echo "[c23] testing_ramdisk_build..."
|
||||
make testing_ramdisk_build
|
||||
|
||||
# Send ramdisk in background
|
||||
echo "[c23] testing_ramdisk_send (background)..."
|
||||
make testing_ramdisk_send &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
# Boot
|
||||
echo "[c23] boot_dfu..."
|
||||
make boot_dfu &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
echo "[c23] waiting for boot (PID ${CHILD_PIDS[-1]})..."
|
||||
wait "${CHILD_PIDS[-1]}" 2>/dev/null || true
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
# ─── Track child PIDs for cleanup ───────────────────────────────────
|
||||
typeset -a CHILD_PIDS=()
|
||||
|
||||
cleanup() {
|
||||
echo "\n[testing_do] cleaning up..."
|
||||
for pid in "${CHILD_PIDS[@]}"; do
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "[testing_do] killing PID $pid"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${0:a:h}")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# ─── Kill existing vphone-cli ──────────────────────────────────────
|
||||
echo "[testing_do] killing existing vphone-cli..."
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# ─── Build pipeline ───────────────────────────────────────────────
|
||||
echo "[testing_do] fw_prepare..."
|
||||
make fw_prepare
|
||||
|
||||
echo "[testing_do] fw_patch_jb..."
|
||||
make fw_patch_jb
|
||||
|
||||
echo "[testing_do] testing_ramdisk_build..."
|
||||
make testing_ramdisk_build
|
||||
|
||||
# ─── Send ramdisk in background ───────────────────────────────────
|
||||
echo "[testing_do] testing_ramdisk_send (background)..."
|
||||
make testing_ramdisk_send &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
# ─── Boot DFU ─────────────────────────────────────────────────────
|
||||
echo "[testing_do] boot_dfu..."
|
||||
make boot_dfu &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
echo "[testing_do] waiting for boot_dfu (PID ${CHILD_PIDS[-1]})..."
|
||||
wait "${CHILD_PIDS[-1]}" 2>/dev/null || true
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
# Fast test a single kernel JB patch.
|
||||
# Restores base kernel backup, applies one patch, rebuilds ramdisk, boots.
|
||||
#
|
||||
# Usage: ./testing_do_patch.sh <patch_name>
|
||||
# or: make testing_do_patch PATCH=patch_vm_fault_enter_prepare
|
||||
|
||||
# ─── Track child PIDs for cleanup ───────────────────────────────────
|
||||
typeset -a CHILD_PIDS=()
|
||||
|
||||
cleanup() {
|
||||
echo "\n[patch] cleaning up..."
|
||||
for pid in "${CHILD_PIDS[@]}"; do
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "[patch] killing PID $pid"
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${0:a:h}")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
PATCH_NAME="${1:-}"
|
||||
if [[ -z "$PATCH_NAME" ]]; then
|
||||
echo "Usage: $0 <patch_name>"
|
||||
echo " e.g. $0 patch_vm_fault_enter_prepare"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VM_DIR="${VM_DIR:-vm}"
|
||||
|
||||
# ─── Kill existing vphone-cli ──────────────────────────────────────
|
||||
echo "[patch] killing existing vphone-cli..."
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# ─── Restore kernel + apply single patch ───────────────────────────
|
||||
echo "[patch] restoring base kernel + applying: $PATCH_NAME"
|
||||
make testing_kernel_patch PATCH="$PATCH_NAME"
|
||||
|
||||
# ─── Rebuild ramdisk ────────────────────────────────────────────────
|
||||
echo "[patch] testing_ramdisk_build..."
|
||||
make testing_ramdisk_build
|
||||
|
||||
# ─── Send ramdisk in background ────────────────────────────────────
|
||||
echo "[patch] testing_ramdisk_send (background)..."
|
||||
make testing_ramdisk_send &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
# ─── Boot DFU ──────────────────────────────────────────────────────
|
||||
echo "[patch] boot_dfu..."
|
||||
make boot_dfu &
|
||||
CHILD_PIDS+=($!)
|
||||
|
||||
echo "[patch] waiting for boot_dfu (PID ${CHILD_PIDS[-1]})..."
|
||||
wait "${CHILD_PIDS[-1]}" 2>/dev/null || true
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
# Save a base-patched kernel for fast per-patch testing.
|
||||
# Run once, then use testing_do_patch.sh <patch_name> to test individual patches.
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${0:a:h}")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
VM_DIR="${VM_DIR:-vm}"
|
||||
|
||||
# ─── Kill existing vphone-cli ──────────────────────────────────────
|
||||
echo "[save] killing existing vphone-cli..."
|
||||
pkill -9 vphone-cli 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# ─── Full pipeline with base patches only ──────────────────────────
|
||||
echo "[save] fw_prepare..."
|
||||
make fw_prepare
|
||||
|
||||
echo "[save] fw_patch_jb..."
|
||||
make fw_patch_jb
|
||||
|
||||
# ─── Find and save kernelcache backup ──────────────────────────────
|
||||
RESTORE_DIR=$(find "$VM_DIR" -maxdepth 1 -type d -name '*Restore*' | head -1)
|
||||
KERNEL_PATH=$(find "$RESTORE_DIR" -name 'kernelcache.research.vphone600' | head -1)
|
||||
|
||||
if [[ -z "$KERNEL_PATH" ]]; then
|
||||
echo "[-] kernelcache not found in $RESTORE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_PATH="${KERNEL_PATH}.base_backup"
|
||||
cp "$KERNEL_PATH" "$BACKUP_PATH"
|
||||
echo "[save] kernel backup saved: $BACKUP_PATH ($(wc -c < "$BACKUP_PATH") bytes)"
|
||||
echo "[save] done. Now use: make testing_do_patch PATCH=<name>"
|
||||
echo ""
|
||||
echo "Available patch names:"
|
||||
echo " patch_post_validation_additional (B5)"
|
||||
echo " patch_proc_security_policy (B6)"
|
||||
echo " patch_proc_pidinfo (B7)"
|
||||
echo " patch_convert_port_to_map (B8)"
|
||||
echo " patch_vm_fault_enter_prepare (B9)"
|
||||
echo " patch_vm_map_protect (B10)"
|
||||
echo " patch_mac_mount (B11)"
|
||||
echo " patch_dounmount (B12)"
|
||||
echo " patch_bsd_init_auth (B13)"
|
||||
echo " patch_spawn_validate_persona (B14)"
|
||||
echo " patch_task_for_pid (B15)"
|
||||
echo " patch_load_dylinker (B16)"
|
||||
echo " patch_shared_region_map (B17)"
|
||||
echo " patch_nvram_verify_permission (B18)"
|
||||
echo " patch_io_secure_bsd_root (B19)"
|
||||
echo " patch_thid_should_crash (B20)"
|
||||
echo " patch_cred_label_update_execve (C21)"
|
||||
echo " patch_syscallmask_apply_to_proc (C22)"
|
||||
echo " patch_hook_cred_label_update_execve (C23)"
|
||||
echo " patch_kcall10 (C24)"
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
testing_kernel_patch.py — Restore base kernel backup and apply a single JB patch.
|
||||
|
||||
Usage:
|
||||
python3 testing_kernel_patch.py <vm_directory> <patch_name> [patch_name2 ...]
|
||||
|
||||
Example:
|
||||
python3 testing_kernel_patch.py vm patch_vm_fault_enter_prepare
|
||||
python3 testing_kernel_patch.py vm patch_mac_mount patch_dounmount
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from fw_patch import find_file, find_restore_dir, load_firmware, save_firmware
|
||||
from patchers.kernel_jb import KernelJBPatcher
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: {sys.argv[0]} <vm_dir> <patch_name> [patch_name2 ...]")
|
||||
sys.exit(1)
|
||||
|
||||
vm_dir = os.path.abspath(sys.argv[1])
|
||||
patch_names = sys.argv[2:]
|
||||
|
||||
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")
|
||||
backup_path = kernel_path + ".base_backup"
|
||||
|
||||
if not os.path.exists(backup_path):
|
||||
print(f"[-] No backup found: {backup_path}")
|
||||
print(f" Run 'make testing_do_save' first.")
|
||||
sys.exit(1)
|
||||
|
||||
# Restore from backup
|
||||
shutil.copy2(backup_path, kernel_path)
|
||||
print(f"[*] Restored kernel from backup ({os.path.getsize(backup_path)} bytes)")
|
||||
|
||||
# Load the kernel
|
||||
im4p, data, was_im4p, original_raw = load_firmware(kernel_path)
|
||||
fmt = "IM4P" if was_im4p else "raw"
|
||||
print(f"[*] Loaded: {fmt}, {len(data)} bytes")
|
||||
|
||||
# Create patcher (inherits from KernelJBPatcherBase which inherits from KernelPatcher)
|
||||
kp = KernelJBPatcher(data)
|
||||
|
||||
# Apply each requested patch
|
||||
applied = 0
|
||||
for patch_name in patch_names:
|
||||
method = getattr(kp, patch_name, None)
|
||||
if method is None:
|
||||
print(f"[-] Unknown patch: {patch_name}")
|
||||
print(f" Available patches:")
|
||||
for name in sorted(dir(kp)):
|
||||
if name.startswith("patch_") and callable(getattr(kp, name)):
|
||||
print(f" {name}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n[*] Applying: {patch_name}")
|
||||
method()
|
||||
|
||||
# Apply the collected patches
|
||||
for off, patch_bytes, _ in kp.patches:
|
||||
data[off : off + len(patch_bytes)] = patch_bytes
|
||||
applied += 1
|
||||
|
||||
print(f"\n[+] {applied} patch(es) applied from {len(patch_names)} method(s)")
|
||||
|
||||
# Save
|
||||
save_firmware(kernel_path, im4p, data, was_im4p, original_raw)
|
||||
print(f"[+] Saved: {kernel_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,362 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
testing_ramdisk_build.py — Build a minimal signed boot chain for testing.
|
||||
|
||||
Packs firmware components (iBSS, iBEC, SPTM, DeviceTree, SEP, TXM,
|
||||
kernelcache) with the stock ramdisk into signed IMG4 files. No SSH
|
||||
tools or CFW — just the base boot chain for quick patch verification.
|
||||
|
||||
Usage:
|
||||
python3 testing_ramdisk_build.py [vm_directory]
|
||||
|
||||
Prerequisites:
|
||||
pip install pyimg4
|
||||
Run fw_patch.py first to patch boot-chain components.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import gzip
|
||||
import os
|
||||
import plistlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
if _SCRIPT_DIR not in sys.path:
|
||||
sys.path.insert(0, _SCRIPT_DIR)
|
||||
|
||||
from pyimg4 import IM4M, IM4P, IMG4
|
||||
|
||||
from fw_patch import (
|
||||
load_firmware,
|
||||
_save_im4p_with_payp,
|
||||
find_restore_dir,
|
||||
find_file,
|
||||
)
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
# Configuration
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
|
||||
OUTPUT_DIR = "TestingRamdisk"
|
||||
TEMP_DIR = "testing_ramdisk_temp"
|
||||
|
||||
# IM4P fourccs for restore mode
|
||||
TXM_FOURCC = "trxm"
|
||||
KERNEL_FOURCC = "rkrn"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
# SHSH / signing helpers
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
def find_shsh(shsh_dir):
|
||||
"""Find first SHSH blob in directory."""
|
||||
for ext in ("*.shsh", "*.shsh2"):
|
||||
matches = sorted(glob.glob(os.path.join(shsh_dir, ext)))
|
||||
if matches:
|
||||
return matches[0]
|
||||
return None
|
||||
|
||||
|
||||
def extract_im4m(shsh_path, im4m_path):
|
||||
"""Extract IM4M manifest from SHSH blob (handles gzip-compressed)."""
|
||||
raw = open(shsh_path, "rb").read()
|
||||
if raw[:2] == b"\x1f\x8b":
|
||||
raw = gzip.decompress(raw)
|
||||
tmp = shsh_path + ".tmp"
|
||||
try:
|
||||
open(tmp, "wb").write(raw)
|
||||
subprocess.run(
|
||||
["pyimg4", "im4m", "extract", "-i", tmp, "-o", im4m_path],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
finally:
|
||||
if os.path.exists(tmp):
|
||||
os.remove(tmp)
|
||||
|
||||
|
||||
def sign_img4(im4p_path, img4_path, im4m_path, tag=None):
|
||||
"""Create IMG4 from IM4P + IM4M using pyimg4 Python API."""
|
||||
im4p = IM4P(open(im4p_path, "rb").read())
|
||||
if tag:
|
||||
im4p.fourcc = tag
|
||||
im4m = IM4M(open(im4m_path, "rb").read())
|
||||
img4 = IMG4(im4p=im4p, im4m=im4m)
|
||||
with open(img4_path, "wb") as f:
|
||||
f.write(img4.output())
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
# Firmware extraction
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
def extract_to_raw(src_path, raw_path):
|
||||
"""Extract IM4P payload to .raw file. Returns (im4p_obj, data, original_raw)."""
|
||||
im4p, data, was_im4p, original_raw = load_firmware(src_path)
|
||||
with open(raw_path, "wb") as f:
|
||||
f.write(bytes(data))
|
||||
return im4p, data, original_raw
|
||||
|
||||
|
||||
def create_im4p_uncompressed(raw_data, fourcc, description, output_path):
|
||||
"""Create uncompressed IM4P from raw data."""
|
||||
new_im4p = IM4P(
|
||||
fourcc=fourcc,
|
||||
description=description,
|
||||
payload=bytes(raw_data),
|
||||
)
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(new_im4p.output())
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
# Main
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
def main():
|
||||
vm_dir = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else os.getcwd())
|
||||
|
||||
if not os.path.isdir(vm_dir):
|
||||
print(f"[-] Not a directory: {vm_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
# Find SHSH
|
||||
shsh_dir = os.path.join(vm_dir, "shsh")
|
||||
shsh_path = find_shsh(shsh_dir)
|
||||
if not shsh_path:
|
||||
print(f"[-] No SHSH blob found in {shsh_dir}/")
|
||||
print(" Place your .shsh file in the shsh/ directory.")
|
||||
sys.exit(1)
|
||||
|
||||
# Find restore directory
|
||||
restore_dir = find_restore_dir(vm_dir)
|
||||
if not restore_dir:
|
||||
print(f"[-] No *Restore* directory found in {vm_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
# Create temp and output directories
|
||||
temp_dir = os.path.join(vm_dir, TEMP_DIR)
|
||||
output_dir = os.path.join(vm_dir, OUTPUT_DIR)
|
||||
for d in (temp_dir, output_dir):
|
||||
if os.path.exists(d):
|
||||
shutil.rmtree(d)
|
||||
os.makedirs(d)
|
||||
|
||||
print(f"[*] Testing ramdisk — boot chain only (no SSH, no CFW)")
|
||||
print(f"[*] VM directory: {vm_dir}")
|
||||
print(f"[*] Restore directory: {restore_dir}")
|
||||
print(f"[*] SHSH blob: {shsh_path}")
|
||||
|
||||
# Extract IM4M from SHSH
|
||||
im4m_path = os.path.join(temp_dir, "vphone.im4m")
|
||||
print(f"\n[*] Extracting IM4M from SHSH...")
|
||||
extract_im4m(shsh_path, im4m_path)
|
||||
|
||||
# ── 1. iBSS (already patched — extract & sign) ───────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 1. iBSS (already patched — extract & sign)")
|
||||
print(f"{'=' * 60}")
|
||||
ibss_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/dfu/iBSS.vresearch101.RELEASE.im4p"],
|
||||
"iBSS",
|
||||
)
|
||||
ibss_raw = os.path.join(temp_dir, "iBSS.raw")
|
||||
ibss_im4p = os.path.join(temp_dir, "iBSS.im4p")
|
||||
im4p_obj, data, _ = extract_to_raw(ibss_src, ibss_raw)
|
||||
create_im4p_uncompressed(data, im4p_obj.fourcc, im4p_obj.description, ibss_im4p)
|
||||
sign_img4(
|
||||
ibss_im4p,
|
||||
os.path.join(output_dir, "iBSS.vresearch101.RELEASE.img4"),
|
||||
im4m_path,
|
||||
)
|
||||
print(f" [+] iBSS.vresearch101.RELEASE.img4")
|
||||
|
||||
# ── 2. iBEC (already patched — sign as-is, no boot-args change)
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 2. iBEC (already patched — sign as-is)")
|
||||
print(f"{'=' * 60}")
|
||||
ibec_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/dfu/iBEC.vresearch101.RELEASE.im4p"],
|
||||
"iBEC",
|
||||
)
|
||||
ibec_raw = os.path.join(temp_dir, "iBEC.raw")
|
||||
ibec_im4p = os.path.join(temp_dir, "iBEC.im4p")
|
||||
im4p_obj, data, _ = extract_to_raw(ibec_src, ibec_raw)
|
||||
create_im4p_uncompressed(data, im4p_obj.fourcc, im4p_obj.description, ibec_im4p)
|
||||
sign_img4(
|
||||
ibec_im4p,
|
||||
os.path.join(output_dir, "iBEC.vresearch101.RELEASE.img4"),
|
||||
im4m_path,
|
||||
)
|
||||
print(f" [+] iBEC.vresearch101.RELEASE.img4")
|
||||
|
||||
# ── 3. SPTM (sign only) ─────────────────────────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 3. SPTM (sign only)")
|
||||
print(f"{'=' * 60}")
|
||||
sptm_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/sptm.vresearch1.release.im4p"],
|
||||
"SPTM",
|
||||
)
|
||||
sign_img4(
|
||||
sptm_src,
|
||||
os.path.join(output_dir, "sptm.vresearch1.release.img4"),
|
||||
im4m_path,
|
||||
tag="sptm",
|
||||
)
|
||||
print(f" [+] sptm.vresearch1.release.img4")
|
||||
|
||||
# ── 4. DeviceTree (sign only) ────────────────────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 4. DeviceTree (sign only)")
|
||||
print(f"{'=' * 60}")
|
||||
dt_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/all_flash/DeviceTree.vphone600ap.im4p"],
|
||||
"DeviceTree",
|
||||
)
|
||||
sign_img4(
|
||||
dt_src,
|
||||
os.path.join(output_dir, "DeviceTree.vphone600ap.img4"),
|
||||
im4m_path,
|
||||
tag="rdtr",
|
||||
)
|
||||
print(f" [+] DeviceTree.vphone600ap.img4")
|
||||
|
||||
# ── 5. SEP (sign only) ───────────────────────────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 5. SEP (sign only)")
|
||||
print(f"{'=' * 60}")
|
||||
sep_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/all_flash/sep-firmware.vresearch101.RELEASE.im4p"],
|
||||
"SEP",
|
||||
)
|
||||
sign_img4(
|
||||
sep_src,
|
||||
os.path.join(output_dir, "sep-firmware.vresearch101.RELEASE.img4"),
|
||||
im4m_path,
|
||||
tag="rsep",
|
||||
)
|
||||
print(f" [+] sep-firmware.vresearch101.RELEASE.img4")
|
||||
|
||||
# ── 6. TXM (already patched — repack & sign) ─────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 6. TXM (already patched — repack & sign)")
|
||||
print(f"{'=' * 60}")
|
||||
txm_src = find_file(
|
||||
restore_dir,
|
||||
["Firmware/txm.iphoneos.research.im4p"],
|
||||
"TXM",
|
||||
)
|
||||
txm_raw = os.path.join(temp_dir, "txm.raw")
|
||||
im4p_obj, data, original_raw = extract_to_raw(txm_src, txm_raw)
|
||||
txm_im4p = os.path.join(temp_dir, "txm.im4p")
|
||||
_save_im4p_with_payp(txm_im4p, TXM_FOURCC, data, original_raw)
|
||||
sign_img4(txm_im4p, os.path.join(output_dir, "txm.img4"), im4m_path)
|
||||
print(f" [+] txm.img4")
|
||||
|
||||
# ── 7. Kernelcache (already patched — repack as rkrn) ────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 7. Kernelcache (already patched — repack as rkrn)")
|
||||
print(f"{'=' * 60}")
|
||||
kc_src = find_file(
|
||||
restore_dir,
|
||||
["kernelcache.research.vphone600"],
|
||||
"kernelcache",
|
||||
)
|
||||
kc_raw = os.path.join(temp_dir, "kcache.raw")
|
||||
im4p_obj, data, original_raw = extract_to_raw(kc_src, kc_raw)
|
||||
print(f" format: IM4P, {len(data)} bytes")
|
||||
kc_im4p = os.path.join(temp_dir, "krnl.im4p")
|
||||
_save_im4p_with_payp(kc_im4p, KERNEL_FOURCC, data, original_raw)
|
||||
sign_img4(kc_im4p, os.path.join(output_dir, "krnl.img4"), im4m_path)
|
||||
print(f" [+] krnl.img4")
|
||||
|
||||
# ── 8. Base ramdisk + trustcache ─────────────────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" 8. Base ramdisk + trustcache")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
tc_bin = shutil.which("trustcache")
|
||||
if not tc_bin:
|
||||
print("[-] trustcache not found. Run: make setup_tools")
|
||||
sys.exit(1)
|
||||
|
||||
# Read RestoreRamDisk path from BuildManifest
|
||||
bm_path = os.path.join(restore_dir, "BuildManifest.plist")
|
||||
with open(bm_path, "rb") as f:
|
||||
bm = plistlib.load(f)
|
||||
ramdisk_rel = bm["BuildIdentities"][0]["Manifest"]["RestoreRamDisk"]["Info"]["Path"]
|
||||
ramdisk_src = os.path.join(restore_dir, ramdisk_rel)
|
||||
|
||||
# Extract base ramdisk DMG
|
||||
ramdisk_raw = os.path.join(temp_dir, "ramdisk.raw.dmg")
|
||||
subprocess.run(
|
||||
["pyimg4", "im4p", "extract", "-i", ramdisk_src, "-o", ramdisk_raw],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
# Mount base ramdisk, build trustcache from its contents
|
||||
mountpoint = os.path.join(vm_dir, "testing_ramdisk_mnt")
|
||||
os.makedirs(mountpoint, exist_ok=True)
|
||||
try:
|
||||
subprocess.run(
|
||||
["sudo", "hdiutil", "attach", "-mountpoint", mountpoint,
|
||||
ramdisk_raw, "-owners", "off"],
|
||||
check=True,
|
||||
)
|
||||
|
||||
print(" Building trustcache from base ramdisk...")
|
||||
tc_raw = os.path.join(temp_dir, "ramdisk.tc")
|
||||
tc_im4p = os.path.join(temp_dir, "trustcache.im4p")
|
||||
subprocess.run([tc_bin, "create", tc_raw, mountpoint], check=True, capture_output=True)
|
||||
subprocess.run(
|
||||
["pyimg4", "im4p", "create", "-i", tc_raw, "-o", tc_im4p, "-f", "rtsc"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
sign_img4(tc_im4p, os.path.join(output_dir, "trustcache.img4"), im4m_path)
|
||||
print(f" [+] trustcache.img4")
|
||||
finally:
|
||||
subprocess.run(
|
||||
["sudo", "hdiutil", "detach", "-force", mountpoint], capture_output=True
|
||||
)
|
||||
|
||||
# Sign base ramdisk as-is
|
||||
rd_im4p = os.path.join(temp_dir, "ramdisk.im4p")
|
||||
subprocess.run(
|
||||
["pyimg4", "im4p", "create", "-i", ramdisk_raw, "-o", rd_im4p, "-f", "rdsk"],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
sign_img4(rd_im4p, os.path.join(output_dir, "ramdisk.img4"), im4m_path)
|
||||
print(f" [+] ramdisk.img4 (base, unmodified)")
|
||||
|
||||
# ── Cleanup ──────────────────────────────────────────────────
|
||||
print(f"\n[*] Cleaning up {TEMP_DIR}/...")
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
# ── Summary ──────────────────────────────────────────────────
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" Testing ramdisk build complete!")
|
||||
print(f" Output: {output_dir}/")
|
||||
print(f" Note: boot chain only — no SSH, no CFW")
|
||||
print(f"{'=' * 60}")
|
||||
for f in sorted(os.listdir(output_dir)):
|
||||
size = os.path.getsize(os.path.join(output_dir, f))
|
||||
print(f" {f:45s} {size:>10,} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/bin/zsh
|
||||
# testing_ramdisk_send.sh — Send testing boot chain to device via irecovery.
|
||||
#
|
||||
# Usage: ./testing_ramdisk_send.sh [testing_ramdisk_dir]
|
||||
#
|
||||
# Expects device in DFU mode. Loads iBSS/iBEC, then boots with
|
||||
# SPTM, TXM, trustcache, ramdisk, device tree, SEP, and kernel.
|
||||
# Boot chain only — no SSH, no CFW.
|
||||
set -euo pipefail
|
||||
|
||||
IRECOVERY="${IRECOVERY:-irecovery}"
|
||||
RAMDISK_DIR="${1:-TestingRamdisk}"
|
||||
|
||||
if [[ ! -d "$RAMDISK_DIR" ]]; then
|
||||
echo "[-] Testing ramdisk directory not found: $RAMDISK_DIR"
|
||||
echo " Run 'make testing_ramdisk_build' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Sending testing boot chain from $RAMDISK_DIR ..."
|
||||
echo " (boot chain only — no SSH, no CFW)"
|
||||
|
||||
# 1. Load iBSS + iBEC (DFU → recovery)
|
||||
echo " [1/8] Loading iBSS..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBSS.vresearch101.RELEASE.img4"
|
||||
|
||||
echo " [2/8] Loading iBEC..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBEC.vresearch101.RELEASE.img4"
|
||||
"$IRECOVERY" -c go
|
||||
|
||||
sleep 1
|
||||
|
||||
# 2. Load SPTM
|
||||
echo " [3/8] Loading SPTM..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/sptm.vresearch1.release.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 3. Load TXM
|
||||
echo " [4/8] Loading TXM..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/txm.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 4. Load trustcache
|
||||
echo " [5/8] Loading trustcache..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/trustcache.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 5. Load ramdisk
|
||||
echo " [6/8] Loading ramdisk..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/ramdisk.img4"
|
||||
sleep 2
|
||||
"$IRECOVERY" -c ramdisk
|
||||
|
||||
# 6. Load device tree
|
||||
echo " [7/8] Loading device tree..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/DeviceTree.vphone600ap.img4"
|
||||
"$IRECOVERY" -c devicetree
|
||||
|
||||
# 7. Load SEP
|
||||
echo " [8/8] Loading SEP..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/sep-firmware.vresearch101.RELEASE.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 8. Load kernel and boot
|
||||
echo " [*] Booting kernel..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/krnl.img4"
|
||||
"$IRECOVERY" -c bootx
|
||||
|
||||
echo "[+] Boot sequence sent."
|
||||
Reference in New Issue
Block a user