How vphone-cli Performs DFU Restore on a Virtual iPhone: Complete Technical Guide

vphone-cli performs DFU restore on a virtual iPhone by booting the VM in DFU mode using private Virtualization.framework APIs, then orchestrating a multi-stage workflow via VPhoneCreateOrchestrator that probes the recovery endpoint, fetches SHSH blobs, and executes the firmware restore through the pmd3 Python bridge.

The open-source Lakr233/vphone-cli project enables developers to virtualize iOS devices on Apple Silicon Macs. Understanding how it handles DFU restore on a virtual iPhone requires examining its Swift implementation that interfaces with undocumented Virtualization.framework flags and external restoration utilities to simulate physical device workflows.

Booting the Virtual Machine in DFU Mode

The restore process begins in VPhoneVirtualMachine.swift, where the start(forceDFU:) method configures the virtual machine to boot into DFU mode. This functionality relies on a private API flag that is not publicly exposed in Apple's Virtualization.framework.

When forceDFU is set to true, the code creates a VZMacOSVirtualMachineStartOptions instance and invokes the undocumented _setForceDFU method through dynamic runtime binding:

// VPhoneVirtualMachine.swift (lines 52-63)
let opts = VZMacOSVirtualMachineStartOptions()
Dynamic(opts)._setForceDFU(forceDFU)          // Private API invocation

After setting the flag, the VM starts headlessly. If DFU mode is active, the CLI prints a diagnostic message indicating the device can be accessed with irecovery, the standard entry point for DFU communication:

print("[vphone] Starting\(forceDFU ? " DFU" : "")…")
if forceDFU { print("[vphone] VM started in DFU mode - connect with irecovery") }

Orchestrating the DFU Restore Workflow

The high-level coordination happens in VPhoneCreateOrchestrator.swift, specifically within runRestorePhase(bundleURL:verbosity:). This orchestrator manages the entire lifecycle: from launching the DFU process to validating post-restore state.

Launching the Managed DFU Process

To keep the VM running in DFU mode while performing host-side operations, the orchestrator spawns a managed background process using VPhoneManagedProcess. This process executes the same vphone-cli binary with the --dfu flag and a configuration path:

// VPhoneCreateOrchestrator.swift (lines 66-69)
let dfu = VPhoneManagedProcess(selfExecutable,
                               ["--config", configURL.path, "--dfu"],
                               cwd: bundleURL, echo: false)
try dfu.start()
defer { dfu.terminate() }

The defer block ensures the DFU process terminates after the restore completes, preventing zombie VM instances.

Probing the Recovery Endpoint

Once the VM is running, the orchestrator must confirm the virtual device has entered recovery/DFU mode and is reachable. It repeatedly invokes the pmd3 Python bridge with the recovery-probe sub-command, targeting the specific ECID (Exclusive Chip ID) of the virtual device:

// VPhoneCreateOrchestrator.swift (lines 69-78)
let result = try? VPhoneProcessRunner.runCapturing(
    python, [resources.pmd3Bridge.path,
             "recovery-probe", "--ecid", "0x\(ecid)", "--timeout", "2"])

The system attempts this probe up to 90 times with 2-second timeouts, ensuring the virtual hardware has initialized sufficiently to accept restoration commands.

Acquiring SHSH Blobs and Executing Restore

After confirming device connectivity, the workflow proceeds in two distinct phases: fetching the signed SHSH blob (required for firmware validation), then performing the actual system restore.

Fetching the SHSH Blob

The orchestrator calls pmd3 restore-get-shsh to obtain a signed SHSH blob from Apple's TSS servers. This step requires the UDID (Unique Device Identifier) and ECID of the virtual iPhone:

// VPhoneCreateOrchestrator.swift (lines 78-84)
let shshArgs = [resources.pmd3Bridge.path,
                "restore-get-shsh", "--vm-dir", ".", "--udid", udid,
                "--ecid", "0x\(ecid)"] + verbosityArgs
let shshCode = try VPhoneProcessRunner.runStreaming(python,
                                                    shshArgs, cwd: bundleURL,
                                                    echo: v.showsToolDetail)

The blob is saved to the VM bundle directory, serving as the cryptographic authorization token for the firmware installation.

Running the Firmware Restore

With the SHSH blob secured, the orchestrator executes pmd3 restore-update to perform the actual firmware installation. This command streams restore progress to the console:

// VPhoneCreateOrchestrator.swift (lines 86-92)
let restoreArgs = [resources.pmd3Bridge.path,
                   "restore-update", "--vm-dir", ".", "--udid", udid,
                   "--ecid", "0x\(ecid)"] + verbosityArgs
let restoreCode = try VPhoneProcessRunner.runStreaming(
    python, restoreArgs, cwd: bundleURL, echo: v.showsToolDetail)

Post-Restore Validation and Cleanup

After the restore command completes, the orchestrator performs critical validation to confirm success. It monitors the DFU process output for panic messages that indicate the VM has rebooted into the newly restored state:

// VPhoneCreateOrchestrator.swift (lines 97-108)
let dfuOutcome = dfu.waitForOutput(matching: "(?i)panic|kernel panic", timeout: 30)
switch dfuOutcome { … }

Detection of a panic or kernel panic message within 30 seconds signals that the virtual device has successfully rebooted and loaded the new firmware. The orchestrator then records version metadata to restore-info.json within the bundle directory before terminating the managed DFU process.

Practical Command-Line Examples

These commands implement the technical workflow described above:

Perform a complete DFU restore (online mode):


# Create VM bundle (first run only)

vphone-cli create my-phone

# Restore (automatically boots DFU, fetches SHSH, installs firmware)

vphone-cli restore my-phone

Fetch SHSH blob only (without restoring):

vphone-cli restore my-phone --get-shsh

Restore using cached SHSH and decrypted AEA images (offline mode):

vphone-cli restore my-phone --offline

According to the source code in VPhoneRestoreCLI.swift, these flags delegate to the same VPhoneCreateOrchestrator path, with the --dfu flag passed automatically during the restore phase.

Summary

  • Private API Dependency: DFU mode activation requires the undocumented _setForceDFU flag on VZMacOSVirtualMachineStartOptions in VPhoneVirtualMachine.swift.
  • Managed Process Architecture: The orchestrator uses VPhoneManagedProcess to maintain the VM in DFU mode while performing host-side restore operations.
  • Python Bridge Integration: All low-level restore logic (probing, SHSH fetching, firmware installation) is delegated to the external pmd3 Python tool via VPhoneProcessRunner.
  • State Detection: Successful restoration is verified by monitoring the serial output for panic messages, indicating the kernel has booted the new firmware image.
  • Metadata Tracking: Post-restore, the system records device information in restore-info.json for future reference.

Frequently Asked Questions

How does vphone-cli enable DFU mode without physical hardware buttons?

The tool uses a private Virtualization.framework API accessed via runtime dynamic binding. In VPhoneVirtualMachine.swift, the code calls _setForceDFU(true) on VZMacOSVirtualMachineStartOptions, a flag that forces the virtual machine to initialize in DFU state without requiring physical button combinations.

What is the pmd3 Python bridge and why is it necessary?

The pmd3 bridge is an external Python utility that handles Apple-specific restore protocols (TSS communication, IMG4 parsing, and device communication). Since these protocols require specialized cryptographic handling and USB communication patterns, vphone-cli delegates to this established tool rather than reimplementing the protocol stack in Swift, as seen in the recovery-probe and restore-update invocations.

How does the orchestrator detect a successful restore completion?

The system monitors the virtual machine's serial console output for specific panic strings. According to VPhoneCreateOrchestrator.swift, it waits up to 30 seconds for output matching the regex (?i)panic|kernel panic. Detection of such messages indicates the device has rebooted and loaded the new firmware kernel, at which point the DFU process is terminated.

Can I restore a virtual iPhone without an internet connection?

Yes, using the --offline flag. This mode requires a pre-existing .shsh blob file and decrypted AEA firmware images in the VM bundle directory. The VPhoneRestoreOps.swift file contains helper utilities for ECID resolution and AEA image decryption that support this offline workflow, bypassing the restore-get-shsh network request.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →