Files
Yaya48 5d9a60a926 hw/arm: add authenticated A6 IMG3 boot lab
Model the A6 crypto, interrupt, USB, and platform blocks needed to boot SecureROM through iBSS into iBEC Recovery.

Add local lab identity, IMG3, and APTicket tooling, patched macOS recovery utilities, UART and GDB access, and English end-user documentation.
2026-09-01 09:51:49 -07:00

367 lines
12 KiB
Markdown

# Apple A6 / iPhone 5 secure-boot lab
This repository contains an experimental QEMU `iphone5` machine for studying
the A6 IMG3 boot path in an isolated environment. It runs the guest
SecureROM, iBSS, and iBEC code with a locally generated root of trust and a
locally generated AES-256 GID key. The verified endpoint is iBEC Recovery
mode; booting a ramdisk or iOS is not implemented.
No Apple private key or GID key is included or required. Firmware files and
generated lab identities are intentionally ignored by Git.
## Verified flow
The following path has been tested on macOS:
1. The 64 KiB A6 SecureROM starts in DFU mode.
2. Patched `idevicerestore` selects the `n41ap` build identity from
`BuildManifest.plist` and sends the locally signed and encrypted iBSS.
3. Guest SecureROM parses the IMG3, validates its RSA chain and signature,
asks the emulated AES engine to unwrap the KBAG with the local GID slot,
and decrypts `DATA`.
4. iBSS parses and validates the local APTicket, including its device fields,
boot-nonce binding, certificate chain, signature, and iBEC digest.
5. iBSS unwraps and decrypts iBEC. QEMU performs a warm authenticated handoff
of the resulting image to its linked address at `0xbff00000`.
6. iBEC prints its real `iBootStage2` banner on UART and enumerates as
Recovery (`05ac:1281`). `irecovery -q` reports `MODE: Recovery` and
`IBFL: 0x1a`.
This is not a cycle-accurate reproduction of Apple silicon. The original
guest IMG3 and APTicket parsers execute, but the root and GID material are
lab-owned, crypto is provided by QEMU MMIO models, debug UART activation is
optionally assisted after authentication, and the final iBEC relocation is a
QEMU handoff. Timing, analog behavior, fault behavior, and unimplemented
peripherals must not be treated as hardware-equivalent.
## Host requirements
On macOS, install the QEMU build dependencies and the recovery-tool
dependencies. A typical Homebrew setup is:
```sh
brew install ninja pkg-config glib pixman libslirp \
autoconf automake libtool libimobiledevice libusb libzip
```
Python 3 and OpenSSL are required by the image-generation scripts. You must
also provide your own 65,536-byte A6 SecureROM dump. The repository does not
distribute it.
## Build QEMU
From the repository root:
```sh
./configure --target-list=arm-softmmu
ninja -C build qemu-system-arm
```
The launcher expects `build/qemu-system-arm`. Set `QEMU_BIN` if the binary is
elsewhere.
## Fetch the public bootloader files
The helper reads the iOS 10.3.4 IPSW ZIP directory with HTTP Range requests
and downloads only `BuildManifest.plist`, iBSS, and iBEC:
```sh
scripts/fetch-a6-bootloaders.py --board n41
```
The following files should then exist locally:
```text
firmware/BuildManifest.plist
firmware/iBSS.iphone5.RELEASE.dfu
firmware/iBSS.iphone5.RELEASE.bin
firmware/iBEC.iphone5.RELEASE.dfu
firmware/iBEC.iphone5.RELEASE.bin
```
The fetcher validates HTTP ranges, uncompressed sizes, and CRC-32 values. It
does not personalize, decrypt, sign, or modify the downloaded images.
## Build the macOS recovery tools
macOS does not expose the emulator as a system-wide USB device. Instead, the
QEMU USB model is connected over a localhost transport implemented in the
patched `libirecovery`. The normal IOKit backend remains unchanged when the
transport environment variable is absent.
Build the pinned and checksum-verified recovery tools locally:
```sh
scripts/build-macos-recovery-tools.sh
```
They are installed under `build/limd-prefix`; nothing is installed into
`/usr/local`. Always use the repository wrappers:
```sh
scripts/irecovery-qemu
scripts/idevicerestore-qemu
```
The QEMU endpoint defaults to `127.0.0.1:26050`. Change both sides with
`USB_BRIDGE_PORT` and `QEMU_USB_ENDPOINT` when running concurrent instances.
## Generate a new lab identity and boot chain
Create an empty output directory containing a new RSA hierarchy, a random
AES-256 GID key, a derived SecureROM, signed IMG3 files, encrypted payloads,
and a locally signed APTicket:
```sh
scripts/build-a6-lab-chain.sh \
"/absolute/path/to/SecureROM for s5l8950xsi, iBoot-1145.3" \
build/a6-lab
```
The output directory must be new or empty. Private PEM files and
`gid-key.bin` are created with owner-only permissions. Do not commit or share
that directory. The default ticket nonce is the value produced by the
current deterministic A6 lab boot (`8d82693c897d1b9d`). The optional third
argument exists for experiments in which the guest nonce is changed.
Start QEMU in the first terminal:
```sh
A6_LAB_DIR="$PWD/build/a6-lab" GDB_PORT=12345 \
./Run-iPhone5-macOS.sh
```
Boot iBSS and iBEC in a second terminal:
```sh
A6_LAB_DIR="$PWD/build/a6-lab" \
scripts/idevicerestore-qemu -d -y -P firmware
```
This wrapper automatically enables the explicit QEMU lab boot-only path. It
does not contact a TSS server and stops after iBEC reaches Recovery; it does
not restore a filesystem. Confirm the result with:
```sh
scripts/irecovery-qemu -q
```
Expected identifying fields include:
```text
CPID: 0x8950
ECID: 0x0000000000200000
IBFL: 0x1a
MODE: Recovery
PRODUCT: iPhone5,1
MODEL: n41ap
```
## UART console
UART0 is mapped at `0x32b00000` and connected to QEMU's standard console by
`-serial mon:stdio`. Run the launcher directly from the terminal in which you
want to see serial output. No extra serial client is needed.
Useful console controls are:
- `Ctrl+A`, then `C`: switch between UART and the QEMU monitor.
- `Ctrl+A`, then `X`: quit QEMU.
Release iBSS/iBEC images do not always enable serial output themselves. The
launcher therefore sets their `debug-uarts` boot argument to `3` after IMG3
authentication by default. Disable this assistance when testing native
firmware behavior:
```sh
DEBUG_UART=0 A6_LAB_DIR="$PWD/build/a6-lab" \
./Run-iPhone5-macOS.sh
```
With the default setting, a successful boot prints the authentic guest
`iBootStage2` banner followed by `Entering recovery mode, starting command
prompt`. Repeated NAND reset errors are currently expected because NAND is
not modeled; they do not prevent the Recovery endpoint from appearing.
## Live debugging with LLDB, GDB, or IDA
Set `GDB_PORT` to expose QEMU's remote GDB stub on localhost. Add `PAUSED=1`
to stop before the SecureROM's first instruction:
```sh
A6_LAB_DIR="$PWD/build/a6-lab" GDB_PORT=12345 PAUSED=1 \
./Run-iPhone5-macOS.sh
```
Attach LLDB:
```sh
lldb -o 'gdb-remote 127.0.0.1:12345'
```
Without `PAUSED=1`, the same command can attach to a running boot. Only one
debugger may control the GDB stub at a time.
Useful load addresses are:
| Stage | Address |
| --- | ---: |
| SecureROM | `0x00000000` |
| iBSS | `0x10000000` |
| iBEC | `0xbff00000` |
| UART0 MMIO | `0x32b00000` |
For IDA, load `build/a6-lab/iBEC.lab-root.bin` as a raw little-endian ARM
binary with image base `0xbff00000`. Select `Remote GDB debugger`, connect to
`127.0.0.1:12345`, and configure the same load address in the debugger's
memory map. The live PC should enter the `0xbff00000` region after the iBEC
handoff.
For a static-only iBSS session that intentionally bypasses IMG3 validation:
```sh
build/qemu-system-arm \
-machine iphone5 \
-bios firmware/s5l8950x-secure-rom.bin \
-device loader,file=firmware/iBSS.iphone5.RELEASE.bin,addr=0x10000000,cpu-num=0,force-raw=on \
-display none -serial mon:stdio \
-S -gdb tcp:127.0.0.1:12345
```
That loader command is useful for early breakpoints, but it bypasses the
SecureROM parser, signature checks, AES work, and normal handoff state. It is
not evidence of an authenticated boot.
## Build signed and encrypted custom images
The simplest supported workflow is to provide raw replacement `DATA`
payloads and rebuild an entirely new identity directory. Custom payloads must
retain exactly one copy of the original embedded 1,215-byte trust-anchor slot
so the builder can replace it with the generated lab root.
```sh
A6_IBSS_PAYLOAD="$PWD/payloads/iBSS.custom.bin" \
A6_IBEC_PAYLOAD="$PWD/payloads/iBEC.custom.bin" \
scripts/build-a6-lab-chain.sh \
"/absolute/path/to/SecureROM for s5l8950xsi, iBoot-1145.3" \
build/a6-custom
```
Boot that directory by setting `A6_LAB_DIR="$PWD/build/a6-custom"` in both
the launcher and `idevicerestore` commands shown above.
For individual IMG3 experiments, first inspect a source template:
```sh
scripts/a6-img3-info.py firmware/iBSS.iphone5.RELEASE.dfu
scripts/a6-img3-info.py firmware/iBSS.iphone5.RELEASE.dfu \
--extract-data build/iBSS.extracted.bin
```
Patch an iBoot payload to trust the existing lab root, then create an iBSS
with an embedded SHSH/CERT signature and GID-wrapped KBAG:
```sh
scripts/a6-lab-patch-iboot-root.py \
--source-rom "/absolute/path/to/original/SecureROM" \
--identity build/a6-lab \
--input payloads/iBSS.custom.bin \
--output build/iBSS.custom.lab-root.bin
scripts/a6-lab-img3.py \
--template firmware/iBSS.iphone5.RELEASE.dfu \
--payload build/iBSS.custom.lab-root.bin \
--identity build/a6-lab \
--encrypt \
--output build/iBSS.custom.signed-encrypted.dfu
```
An iBEC uses the external APTicket layout. Build its encrypted IMG3 first,
then bind the APTicket to that exact component and the guest boot nonce:
```sh
scripts/a6-lab-patch-iboot-root.py \
--source-rom "/absolute/path/to/original/SecureROM" \
--identity build/a6-lab \
--input payloads/iBEC.custom.bin \
--output build/iBEC.custom.lab-root.bin
scripts/a6-lab-img3.py \
--template firmware/iBEC.iphone5.RELEASE.dfu \
--payload build/iBEC.custom.lab-root.bin \
--identity build/a6-lab \
--encrypt --ticketed \
--output build/iBEC.custom.ticketed-encrypted.img3
scripts/a6-lab-apticket.py \
--component build/iBEC.custom.ticketed-encrypted.img3 \
--identity build/a6-lab \
--boot-nonce 8d82693c897d1b9d \
--ticket-output build/iBEC.custom.apticket.der \
--output build/iBEC.custom.signed-encrypted.dfu
```
The tools refuse to overwrite outputs and write JSON sidecars with hashes,
sizes, signed ranges, encryption state, device fields, and manifest tags.
`a6-lab-img3.py` uses a random content key and IV for every encrypted image;
the content key is wrapped by AES-256-CBC with the generated lab GID key.
## Environment variables
| Variable | Purpose | Default |
| --- | --- | --- |
| `A6_LAB_DIR` | Generated identity and boot-chain directory | unset |
| `A6_IBSS_PAYLOAD` | Raw custom iBSS payload for the chain builder | stock extracted iBSS |
| `A6_IBEC_PAYLOAD` | Raw custom iBEC payload for the chain builder | stock extracted iBEC |
| `A6_BOOT_NONCE` | Eight-byte ticket nonce in hexadecimal | `8d82693c897d1b9d` |
| `QEMU_BIN` | QEMU executable used by the launcher | `build/qemu-system-arm` |
| `USB_BRIDGE_PORT` | QEMU localhost recovery transport; `0` disables it | `26050` |
| `QEMU_USB_ENDPOINT` | Endpoint used by the host recovery wrappers | `127.0.0.1:26050` |
| `GDB_PORT` | Remote GDB listener | unset |
| `PAUSED` | Start with the CPU stopped | `0` |
| `DEBUG_UART` | Enable post-authentication debug UART assistance | `1` |
| `NATIVE_IMG3` | Enable authenticated native IMG3 handoff helpers | `1` with `A6_LAB_DIR` |
| `DFU_IMAGE` | Optional internal one-shot DFU image | unset |
## Windows host path
The Windows path uses a UDE test driver and a native TCP bridge to present
the virtual Apple DFU device to unmodified Windows recovery tools. Build QEMU
from MSYS2 MinGW64, then prepare the driver and bridge from PowerShell:
```powershell
.\windows\QemuA6Ude\prepare-package.ps1
.\windows\QemuA6UsbBridge\build-bridge.ps1 -Configuration Release
```
The first installation requires an administrator shell, test-signing mode,
and a restart:
```powershell
.\windows\QemuA6Ude\enable-test-mode.ps1
Restart-Computer
.\windows\QemuA6Ude\install-driver.ps1
```
Launch with:
```powershell
.\Run-iPhone5.ps1 -SkipBuild -GdbPort 12345
```
The local-key IMG3 generation and macOS `idevicerestore` boot-only wrapper
are currently the verified end-to-end path. The Windows UDE bridge has been
validated for DFU control traffic but not for the complete local iBSS/iBEC
workflow.
## Current limitations
- The verified endpoint is iBEC Recovery, not a ramdisk or iOS userspace.
- NAND is not implemented, so the guest emits repeated NAND errors.
- UART transmit and MMIO access are verified. UART receive is modeled, but a
complete interactive iBoot command session over serial is not guaranteed.
- The localhost macOS backend is visible only to the patched libirecovery
tools; it does not appear in Finder or System Information.
- The implementation is intended for deterministic parser and boot-flow
research, not for hardware timing or fault-injection equivalence.