# How to Perform CFW Post-Restore Device-Tree Patching with Python Scripts

> Learn to perform CFW post-restore device-tree patching using Python scripts. Mutate model, target-type, and compatible properties for correct VM hardware descriptors.

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

---

**The [`scripts/patchers/cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/patchers/cfw_patch_post_restore_dt.py) script in the Lakr233/vphone-cli repository rewrites the device-tree (DT) after an iOS restore to mutate `model`, `target-type`, and `compatible` properties to D47 virtual platform identifiers, ensuring the VM boots with corrected hardware descriptors.**

CFW post-restore patching is a critical step in the vPhone-Custom Firmware (CFW) installation pipeline. After a standard iOS restore completes, the device-tree must be modified to present virtualized "D47" platform identifiers instead of the original hardware signatures. The repository provides a dedicated Python helper that automates this mutation while preserving the cryptographic envelope of the IMG4 container.

## Understanding the Device-Tree Patching Script

The primary implementation lives at [`scripts/patchers/cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/patchers/cfw_patch_post_restore_dt.py). This self-contained utility operates on the DT payload that the restore process writes to the host-mounted filesystem, typically located at `/mnt5/<boot-hash>/usr/standalone/firmware/devicetree.img4`.

During a normal restore, DT fields such as `model`, `target-type`, and `compatible` must match the signed BuildManifest; otherwise the restore aborts. Once the restore finishes, these checks are no longer enforced, allowing the script to safely mutate the DT to the D47 virtual platform identifiers used by vPhone-CFW:

- **`model`** → `iPhone17,3`
- **`target-type`** → `D47`
- **`compatible`** → `D47AP`, `VPHONE600AP`, `AppleVirtualPlatformARM` (reordered)

The binary layout of a DT node is described in the script header (lines **57-68**) and mirrors the Swift implementation in [`DeviceTreePatcher.swift`](https://github.com/Lakr233/vphone-cli/blob/main/DeviceTreePatcher.swift).

## How the Patching Algorithm Works

The script follows a six-stage pipeline to modify the device-tree without altering the overall blob size.

### Loading and Decompressing the Image

The script accepts either a full `devicetree.img4` or a raw `devicetree.im4p`. It auto-detects the container by attempting to instantiate `pyimg4.IMG4`, falling back to `pyimg4.IM4P` if that fails (lines **44-51**). If the IM4P payload is compressed, it is decompressed using `im4p.payload.decompress()` (lines **62-66**).

### Parsing the Binary Structure

The `_parse_node` function walks the binary DT structure, building a tree of `DTNode` and `DTProperty` objects (lines **94-118**). This recursive descent parser handles the flattened device-tree format used by Apple boot firmware.

### Applying the Three Property Patches

The script applies three specific mutations while respecting each property's original slot length to guarantee the overall blob size remains unchanged (lines **122-129**):

1. **`model` patch** (lines **82-88**): Rewrites values like `iPhone99,11` to `iPhone17,3`
2. **`target-type` patch** (lines **90-96**): Changes `VPHONE600` to `D47`
3. **`compatible` patch** (lines **98-108**): Reorders the compatibility list to include `D47AP` as the primary entry

### Serializing and Repackaging

The `_serialize_node` function writes the updated nodes back to a byte buffer (lines **120-136**). The script then builds a new `IM4P` or `IMG4` preserving the original compression (lines **82-97**) and writes it to disk. If the DT already matches the target state, the script detects "no change" and exits without rewriting (lines **22-27**).

## Running the Patcher from the Command Line

The script requires Python 3.8+ and the `pyimg4` library declared in [`requirements.txt`](https://github.com/Lakr233/vphone-cli/blob/main/requirements.txt). Install dependencies with:

```bash
pip install pyimg4

```

Execute the patcher directly against a restored device-tree:

```bash
./scripts/patchers/cfw_patch_post_restore_dt.py /path/to/devicetree.img4

```

Add `--dry-run` to preview changes without writing to disk:

```bash
$ ./scripts/patchers/cfw_patch_post_restore_dt.py /mnt5/abcd1234/usr/standalone/firmware/devicetree.img4 --dry-run
  [.] /mnt5/abcd1234/usr/standalone/firmware/devicetree.img4: IMG4  desc='DeviceTree'  payload_compression=<Compression.LZFSE: 2>
  [.] DT blob: 1234 bytes
  [+] model: 'iPhone99,11' -> 'iPhone17,3'
  [+] target-type: 'VPHONE600' -> 'D47'
  [+] compatible: ['VPHONE600AP', '*', 'AppleVirtualPlatformARM'] -> ['D47AP', 'VPHONE600AP', 'AppleVirtualPlatformARM']
  [.] dry-run — not writing back

```

The CFW installation pipelines ([`cfw_install_jb.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_jb.sh), [`cfw_install_dev.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_dev.sh)) invoke this script automatically after copying the restored `devicetree.img4` to a temporary location, then copy the patched file back to ensure the VM boots with corrected DT without touching the live root filesystem.

## Integrating the Patcher into Custom Workflows

For automation pipelines, import the patching logic directly into Python scripts:

```python
from scripts.patchers.cfw_patch_post_restore_dt import patch_devicetree_file

# Path to the restored devicetree (IMG4 or IM4P)

dt_path = "/mnt5/abcd1234/usr/standalone/firmware/devicetree.img4"

# Apply the patch; returns the number of properties changed

changed = patch_devicetree_file(dt_path)
print(f"Patched {changed} DT properties")

```

Honor the exit codes when calling from shell scripts: `0` indicates success, `1` indicates an error, and `2` indicates usage errors.

## Summary

- The [`cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_patch_post_restore_dt.py) script performs CFW post-restore patching by rewriting three critical DT properties (`model`, `target-type`, `compatible`) to D47 identifiers.
- It handles both IMG4 and IM4P containers, auto-detecting the format and preserving original compression.
- The implementation guarantees idempotent operation—if the DT already matches the target state, the script exits without modification.
- The patcher maintains the original blob size by respecting property slot lengths during serialization.
- Integration examples are available in [`cfw_install_jb.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_jb.sh) and related installation scripts in the repository.

## Frequently Asked Questions

### What is the purpose of CFW post-restore device-tree patching?

After an iOS restore completes, the device-tree contains hardware-specific identifiers that do not match the virtualized D47 platform. The patching process rewrites these identifiers so the operating system boots correctly under the vPhone virtual machine monitor, presenting the expected `iPhone17,3` model and `D47` target type to the kernel.

### Does the script modify the original IMG4 signature?

No, the script preserves the cryptographic envelope of the IMG4 or IM4P container. It decompresses the payload, modifies the plaintext device-tree binary, then recompresses and repackages it without invalidating the signature. The mutation occurs in the payload data, not the protected header.

### Can I run the patcher on a live system?

The script is designed to operate on mounted filesystem images (such as those in `/mnt5/` during the restore process), not on active boot volumes. Running it against a live system device-tree could cause kernel panics or boot failures because the DT is memory-mapped and protected by the operating system.

### What dependencies are required to run the Python patcher?

You need Python 3.8 or newer and the `pyimg4` library. The repository includes a [`requirements.txt`](https://github.com/Lakr233/vphone-cli/blob/main/requirements.txt) file that declares this dependency. Install it using `pip install pyimg4` before executing the script.