# How vPhone-CLI Rewrites the Device-Tree for EXP Variants Using DeviceTreePatcher

> Learn how vPhone-CLI rewrites device-trees for EXP variants using DeviceTreePatcher. Discover how it spoofs device identity for IMG4 compatibility.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: how-to-guide
- Published: 2026-08-31

---

**vPhone-CLI uses a Python-based DeviceTreePatcher implemented in [`scripts/patchers/cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/patchers/cfw_patch_post_restore_dt.py) to modify the iOS device-tree after restoration, spoofing a genuine iPhone 17,3 identity by rewriting the `model`, `target-type`, and `compatible` properties while maintaining exact binary size for IMG4 container compatibility.**

The **vphone-cli** repository provides virtualization tools for running iOS on Apple Silicon, including an experimental (EXP) variant that requires post-restore filesystem patches. The DeviceTreePatcher is critical for EXP builds because it rewrites the virtual device's identity after the restore process but before the VM boots, ensuring iBoot validation passes while preserving virtualization features.

## Why Device-Tree Rewriting is Required for EXP Variants

When restoring an iOS filesystem for EXP variants, the base image contains generic virtualization identifiers that fail Apple's boot-time validation. The iBoot bootloader checks the device-tree `model` and `target-type` properties against the signed BuildManifest, rejecting boots from unrecognized virtual hardware identifiers like `"iPhone99,11"` or `"VPHONE600"`.

The DeviceTreePatcher solves this by running *after* the restore completes (when the VM filesystem is already populated) to swap three specific properties:

- **`model`**: Changes from `"iPhone99,11"` to `"iPhone17,3"`【/scripts/patchers/cfw_patch_post_restore_dt.py#L82-L88】
- **`target-type`**: Rewrites `"VPHONE600"` to `"D47"`【/scripts/patchers/cfw_patch_post_restore_dt.py#L90-L96】
- **`compatible`**: Reorders compatible strings, inserting `"D47AP"` as the first entry while retaining `"VPHONE600AP"` and `"AppleVirtualPlatformARM"` for kernel compatibility【/scripts/patchers/cfw_patch_post_restore_dt.py#L98-L106】

This post-restore timing is essential because it allows the restore process to complete with original virtual identifiers, then swaps them to real hardware identifiers ("D47" corresponds to iPhone 17,3) only after validation-sensitive stages have finished.

## How the DeviceTreePatcher Modifies the Binary Device-Tree

The patcher operates directly on the binary device-tree blob (`devicetree.img4`/`.im4p`) using a lightweight Python parser that mirrors the Swift `DeviceTreePatcher` implementation.

### Parsing the DT Blob Without Size Changes

The script parses the binary DT format by reading property counts, child counts, property headers, and values according to the standard device-tree binary specification【/scripts/patchers/cfw_patch_post_restore_dt.py#L54-L68】. It locates the root node (`device-tree`) and extracts target properties via the `_find_property` helper【/scripts/patchers/cfw_patch_post_restore_dt.py#L47-L51】.

**Crucially**, the patcher encodes new strings into the exact byte-length slots originally allocated for the old values. This constraint ensures the overall device-tree size remains unchanged, which is vital because the surrounding IMG4/IM4P headers store absolute offsets to the DT blob【/scripts/patchers/cfw_patch_post_restore_dt.py#L54-L58】.

### Rewriting the Three Identification Properties

After locating the properties, the script performs in-place string replacements:

1. **Model spoofing**: Replaces the virtual identifier with the real hardware model string
2. **Target-type mapping**: Swaps the internal virtual platform code for the genuine device hardware code
3. **Compatible string ordering**: Inserts the real device compatibility string (`D47AP`) at the front of the array while keeping legacy strings for backward compatibility with virtualization drivers【/scripts/patchers/cfw_patch_post_restore_dt.py#L98-L106】

The modified node tree is then serialized back to a byte array while preserving all other properties unchanged【/scripts/patchers/cfw_patch_post_restore_dt.py#L20-L38】.

### Reconstructing the IMG4 Container

After modifying the DT blob, the script rebuilds the IMG4/IM4P container using the `pyimg4` library. It preserves the original compression mode (LZFSE) and all cryptographic wrappers, ensuring the patched device-tree remains cryptographically valid and bootable【/scripts/patchers/cfw_patch_post_restore_dt.py#L82-L90】.

## Running the Device-Tree Patch in the EXP Pipeline

The EXP installation script ([`cfw_install_exp.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_exp.sh)) integrates the patcher into the firmware installation workflow. After mounting the restored filesystem, it invokes the Python script directly on the device-tree path:

```bash

# Inside the EXP install pipeline (cfw_install_exp.sh)

DEVICETREE_PATH="${BOOT_MOUNT}/usr/standalone/firmware/devicetree.img4"
python3 scripts/patchers/cfw_patch_post_restore_dt.py "$DEVICETREE_PATH"

```

You can also run the patch manually against a specific device-tree file:

```bash

# Patch a specific devicetree.img4 after manual restoration

python3 scripts/patchers/cfw_patch_post_restore_dt.py /mnt5/<boot-hash>/usr/standalone/firmware/devicetree.img4

```

### Idempotent Safety Mechanism

The patcher implements idempotency checks to prevent double-patching. If the device-tree already contains the target EXP values (e.g., `model` is already `"iPhone17,3"`), the script detects "no change" and exits without altering the file【/scripts/patchers/cfw_patch_post_restore_dt.py#L44-L46】. This safety feature prevents corruption during repeated installation attempts.

When changes are applied, the script outputs a detailed log showing the transformations:

```

[.] /mnt5/abcd1234/usr/standalone/firmware/devicetree.img4: IMG4  desc='DeviceTree'  payload_compression=<Compression.LZFSE>
[.] DT blob: 12345 bytes
[+] model: 'iPhone99,11' -> 'iPhone17,3'
[+] target-type: 'VPHONE600' -> 'D47'
[+] compatible: ['VPHONE600AP', 'AppleVirtualPlatformARM'] -> ['D47AP', 'VPHONE600AP', 'AppleVirtualPlatformARM']
[+] wrote /mnt5/abcd1234/usr/standalone/firmware/devicetree.img4

```

## Technical Constraints and Safety Mechanisms

The DeviceTreePatcher must respect several hard constraints imposed by the IMG4 format and iOS boot process:

**Fixed-Size Encoding**: New property values must match the byte length of original values exactly. The parser allocates buffers based on original property sizes, and expanding the DT blob would invalidate absolute offsets stored in the IMG4 header【/scripts/patchers/cfw_patch_post_restore_dt.py#L54-L58】.

**Compression Preservation**: The patcher uses `pyimg4` to handle LZFSE compression transparently, ensuring the modified device-tree maintains the same compression type as the original.

**Post-Restore Timing**: The patch runs after filesystem restoration but before the VM boots, ensuring the modified identifiers are present for iBoot's final verification phase without interfering with the restore validation itself.

## Summary

- **vPhone-CLI** uses [`scripts/patchers/cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/patchers/cfw_patch_post_restore_dt.py) to modify the iOS device-tree after filesystem restoration for EXP variants.
- The patcher changes three critical properties: `model` to `"iPhone17,3"`, `target-type` to `"D47"`, and reorders `compatible` strings to include `"D47AP"`.
- It preserves exact binary size when encoding new strings to prevent corrupting IMG4 container offsets.
- The implementation uses `pyimg4` for IMG4/IM4P manipulation and maintains idempotency to prevent double-patching.
- This post-restore rewrite allows the virtual device to pass iBoot validation as a real iPhone while retaining virtualization capabilities required for research.

## Frequently Asked Questions

### What is the purpose of the DeviceTreePatcher in vPhone-CLI?

The DeviceTreePatcher enables EXP (experimental) variants to boot successfully by spoofing the virtual device's hardware identity. It rewrites the device-tree properties after restoration so that iBoot recognizes the VM as a legitimate iPhone 17,3 rather than generic virtual hardware, allowing the system to pass Apple's boot-time validation checks.

### Which device-tree properties does the EXP patcher modify?

The patcher modifies three specific properties in the root node of the device-tree blob: `model` (changed from `"iPhone99,11"` to `"iPhone17,3"`), `target-type` (changed from `"VPHONE600"` to `"D47"`), and `compatible` (reordered to insert `"D47AP"` as the primary compatibility string)【/scripts/patchers/cfw_patch_post_restore_dt.py#L82-L106】.

### Why must the device-tree size remain unchanged after patching?

The device-tree is encapsulated within an IMG4 or IM4P container that stores absolute binary offsets to the DT blob in its headers. If the patcher expanded or contracted the device-tree size, these offsets would point to invalid memory locations, causing the boot chain to fail when loading the device-tree. The patcher encodes new strings into the exact original byte-length slots to maintain binary compatibility【/scripts/patchers/cfw_patch_post_restore_dt.py#L54-L58】.

### How does the patcher maintain idempotency?

The script checks existing property values before applying changes and exits without writing if the target EXP values are already present【/scripts/patchers/cfw_patch_post_restore_dt.py#L44-L46】. This prevents corruption from double-patching and allows the EXP installation pipeline to safely retry operations without worrying about cumulative modifications to the device-tree.