How vphone-cli Patches the DeviceTree for EXP Variants
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 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.
// 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 runs after the restore phase completes to inject model and target-type properties directly into the rebuilt DeviceTree payload.
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-numberhome-button-typeartwork-device-subtypeisland-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-typecompatiblefdr-product-typesub-product-typeunique-model- Multiple
arm-ioproperties
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, 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.
// 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 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.swiftimplementation 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, andunique-modelto match iPhone 17,3 (D47AP) specifications. - Experimental nodes provide required properties for Camera.app, SpringBoard, and libMobileGestalt under
/device-tree/productand/arm-iopaths. - The
cfw_patch_post_restore_dt.pyscript handlesmodelandtarget-typeinjection 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 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, which handles parsing, property modification, and serialization. FirmwarePipeline.swift manages the build-time invocation of the patcher with appropriate flags for each variant. The 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →