mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-04-05 04:59:05 +08:00
add testing ramdisk: boot chain only (no rootfs, kernel will panic)
Sign patched firmware components (iBSS/iBEC/SPTM/DeviceTree/SEP/TXM/ kernelcache) into IMG4 without ramdisk or trustcache. Useful for verifying boot chain patches in isolation.
This commit is contained in:
10
Makefile
10
Makefile
@@ -71,6 +71,8 @@ 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 rootfs, kernel will panic)"
|
||||
@echo " make testing_ramdisk_send Send testing boot chain to device"
|
||||
@echo ""
|
||||
@echo "CFW:"
|
||||
@echo " make cfw_install Install CFW mods via SSH"
|
||||
@@ -217,7 +219,7 @@ restore:
|
||||
# Ramdisk
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
.PHONY: ramdisk_build ramdisk_send
|
||||
.PHONY: ramdisk_build ramdisk_send testing_ramdisk_build testing_ramdisk_send
|
||||
|
||||
ramdisk_build:
|
||||
cd $(VM_DIR) && $(PYTHON) "$(CURDIR)/$(SCRIPTS)/ramdisk_build.py" .
|
||||
@@ -225,6 +227,12 @@ 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"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# CFW
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
303
scripts/testing_ramdisk_build.py
Normal file
303
scripts/testing_ramdisk_build.py
Normal file
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
fw_build_testing_ramdisk.py — Build a minimal signed boot chain for testing.
|
||||
|
||||
Packs only firmware components (iBSS, iBEC, SPTM, DeviceTree, SEP, TXM,
|
||||
kernelcache) into signed IMG4 files. No ramdisk, no trustcache.
|
||||
|
||||
The kernel is expected to boot and then panic (no rootfs). This is useful
|
||||
for verifying that patched boot-chain components (iBSS/iBEC/LLB/iBoot/TXM/
|
||||
kernelcache) work correctly.
|
||||
|
||||
Usage:
|
||||
python3 fw_build_testing_ramdisk.py [vm_directory]
|
||||
|
||||
Prerequisites:
|
||||
pip install pyimg4
|
||||
Run fw_patch.py first to patch boot-chain components.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import gzip
|
||||
import os
|
||||
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 rootfs, kernel will panic)")
|
||||
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")
|
||||
|
||||
# ── 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: kernel will panic after boot (no rootfs — expected)")
|
||||
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()
|
||||
58
scripts/testing_ramdisk_send.sh
Normal file
58
scripts/testing_ramdisk_send.sh
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/bin/zsh
|
||||
# fw_send_testing_ramdisk.sh — Send testing boot chain to device via irecovery.
|
||||
#
|
||||
# Usage: ./fw_send_testing_ramdisk.sh [testing_ramdisk_dir]
|
||||
#
|
||||
# Expects device in DFU mode. Loads iBSS/iBEC, then boots with
|
||||
# SPTM, TXM, device tree, SEP, and kernel. No ramdisk or trustcache.
|
||||
# Kernel will panic after boot (no rootfs — expected).
|
||||
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 " (no rootfs — kernel will panic after boot)"
|
||||
|
||||
# 1. Load iBSS + iBEC (DFU → recovery)
|
||||
echo " [1/6] Loading iBSS..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBSS.vresearch101.RELEASE.img4"
|
||||
|
||||
echo " [2/6] Loading iBEC..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/iBEC.vresearch101.RELEASE.img4"
|
||||
"$IRECOVERY" -c go
|
||||
|
||||
sleep 1
|
||||
|
||||
# 2. Load SPTM
|
||||
echo " [3/6] Loading SPTM..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/sptm.vresearch1.release.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 3. Load TXM
|
||||
echo " [4/6] Loading TXM..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/txm.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 4. Load device tree
|
||||
echo " [5/6] Loading DeviceTree..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/DeviceTree.vphone600ap.img4"
|
||||
"$IRECOVERY" -c devicetree
|
||||
|
||||
# 5. Load SEP
|
||||
echo " [6/6] Loading SEP..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/sep-firmware.vresearch101.RELEASE.img4"
|
||||
"$IRECOVERY" -c firmware
|
||||
|
||||
# 6. Load kernel and boot
|
||||
echo " [*] Booting kernel..."
|
||||
"$IRECOVERY" -f "$RAMDISK_DIR/krnl.img4"
|
||||
"$IRECOVERY" -c bootx
|
||||
|
||||
echo "[+] Boot sequence sent. Kernel should boot and then panic (no rootfs)."
|
||||
Reference in New Issue
Block a user