# How vphone-cli Patches the DeviceTree for EXP Variants

> Learn how vphone-cli patches DeviceTree for EXP variants using a two-stage process to inject iPhone 17,3 identity properties for improved compatibility.

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

---

**vphone-cli employs a two-stage patching process—modifying the DeviceTree in-memory during firmware generation and applying post-restore fixes—to inject iPhone 17,3 (D47AP) identity properties for EXP variants.**

The vphone-cli tool, maintained in the `Lakr233/vphone-cli` repository, generates custom firmware (CFW) images for virtualized iPhone environments. When building the EXP variant, the tool must transform the DeviceTree binary to present a complete hardware identity matching a real iPhone 17,3 (D47AP), enabling features like Camera app functionality and accurate libMobileGestalt queries.

## Two-Stage DeviceTree Patching Architecture

The vphone-cli patch DeviceTree EXP variant workflow splits modifications across two distinct phases to balance build-time efficiency with restore process compatibility.

### Stage 1: In-Memory Patching with DeviceTreePatcher

During CFW generation, [`DeviceTreePatcher.swift`](https://github.com/Lakr233/vphone-cli/blob/main/DeviceTreePatcher.swift) parses the flat DeviceTree binary and applies property transformations in memory. The `DeviceTreePatcher` class constructor accepts a boolean `includeIdentityPatches` parameter that the calling code sets to `true` specifically for EXP variants.

```swift
// Create the patcher with EXP‑specific identity patches enabled.
let dtPatcher = DeviceTreePatcher(
    data: dtData,
    verbose: true,
    includeIdentityPatches: true
)

// Discover and apply all patches.
let applied = try dtPatcher.apply()
let patchedDT = dtPatcher.patchedData

```

The `apply()` method internally calls `applyPatches(root:)`, which merges patch arrays and resolves target nodes via `resolveNode`. Each modification uses helper encoders like `encodeFixedString`, `encodeInteger`, and `encodeFixedBytes`, with changes recorded as `PatchRecord` instances.

### Stage 2: Post-Restore Property Injection

Certain identity fields break the restore-signed path if modified during the initial build. The Python script [`cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_patch_post_restore_dt.py) runs after the restore phase completes to inject `model` and `target-type` properties directly into the rebuilt DeviceTree payload.

```bash
python3 scripts/patchers/cfw_patch_post_restore_dt.py \
    --dt-path /path/to/rebuilt/devicetree.bin \
    --variant exp

```

## Base Property Patches Applied to All Variants

Regardless of variant, `DeviceTreePatcher` always applies four base property patches:

- `serial-number`
- `home-button-type`
- `artwork-device-subtype`
- `island-notch-location`

These modifications execute via the standard patching flow without requiring the `includeIdentityPatches` flag.

## EXP-Specific Identity Patches

When `includeIdentityPatches` is enabled for the EXP variant, the patcher activates two additional modification groups that transform the virtual device into an iPhone 17,3 profile.

### Identity Rewrite Property Patches

The `identityPropertyPatches` array rewrites low- to medium-risk identifiers affecting user-space queries. These patches modify properties including:

- `target-sub-type`
- `compatible`
- `fdr-product-type`
- `sub-product-type`
- `unique-model`
- Multiple `arm-io` properties

These changes ensure Gestalt queries return D47AP-specific values without disrupting the kernel boot process.

### Experimental Node Additions

The `experimentalNodeAdditions` group inserts new child nodes under `/device-tree/product` and `/arm-io` paths. Under `/device-tree/product`, the patcher adds nodes for `camera`, `facetime`, `audio`, and `iopm`. The `/arm-io` path receives several stub nodes required by system frameworks.

The insertion logic checks for existing nodes before writing, making the operation idempotent. This prevents duplicate entries if the patching process runs multiple times.

## Implementation Details and Code Flow

The Swift implementation follows a strict parse-patch-serialize pipeline. In [`DeviceTreePatcher.swift`](https://github.com/Lakr233/vphone-cli/blob/main/DeviceTreePatcher.swift), the workflow first parses the raw DT payload into a node tree, applies the merged patch collections, then serializes the modified structure back to binary format.

```swift
// Parse the raw DT payload
let root = try parsePayload(buffer.data)

// Apply base patches +, if EXP, identity patches + experimental nodes
try applyPatches(root: root)

// Serialize the modified tree back to binary
rebuiltData = serializePayload(root)

```

The [`FirmwarePipeline.swift`](https://github.com/Lakr233/vphone-cli/blob/main/FirmwarePipeline.swift) file orchestrates this process, instantiating `DeviceTreePatcher` with the correct `includeIdentityPatches` value based on the target variant being built.

## Summary

- vphone-cli uses a dual-phase approach to patch DeviceTree for EXP variants, combining Swift in-memory modification with Python post-processing.
- The [`DeviceTreePatcher.swift`](https://github.com/Lakr233/vphone-cli/blob/main/DeviceTreePatcher.swift) implementation applies four base patches universally, with EXP variants receiving additional identity rewrites and experimental node additions.
- Identity patches modify properties like `target-sub-type`, `compatible`, and `unique-model` to match iPhone 17,3 (D47AP) specifications.
- Experimental nodes provide required properties for Camera.app, SpringBoard, and libMobileGestalt under `/device-tree/product` and `/arm-io` paths.
- The [`cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_patch_post_restore_dt.py) script handles `model` and `target-type` injection after restore to preserve signed restore compatibility.

## Frequently Asked Questions

### What is the EXP variant in vphone-cli?

The EXP variant is an experimental firmware configuration that presents a complete iPhone 17,3 (D47AP) device profile to the virtual machine. It enables advanced features like Always-On Technology and full Camera app functionality by providing the comprehensive DeviceTree structure that iOS frameworks expect from real hardware.

### Why are model and target-type patched after restore instead of during build?

These specific properties break the restore-signed verification path if modified during the initial firmware generation phase. By leaving `model` and `target-type` unmodified until the [`cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_patch_post_restore_dt.py) script runs post-restore, vphone-cli maintains compatibility with Apple's signed restore process while still achieving the final D47AP identity profile.

### What files handle the DeviceTree patching logic?

The primary implementation resides in [`DeviceTreePatcher.swift`](https://github.com/Lakr233/vphone-cli/blob/main/DeviceTreePatcher.swift), which handles parsing, property modification, and serialization. [`FirmwarePipeline.swift`](https://github.com/Lakr233/vphone-cli/blob/main/FirmwarePipeline.swift) manages the build-time invocation of the patcher with appropriate flags for each variant. The [`cfw_patch_post_restore_dt.py`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_patch_post_restore_dt.py) script in the `scripts/patchers/` directory completes the process after device restoration.

### Is the DeviceTree patching process idempotent?

Yes. When adding experimental nodes, the patcher checks for existing nodes before insertion and skips any that are already present. This idempotent design allows the patching process to run safely multiple times without creating duplicate entries or corruption in the DeviceTree structure.