# How to Use pymobiledevice3 for DFU Restore in iOS VMs: A Complete Technical Guide

> Learn to perform DFU restores on iOS VMs with pymobiledevice3 and vphone-cli. This guide details firmware operations using a bundled bridge script for seamless restoration. Get started now!

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

---

**`vphone-cli` performs DFU restores of iOS virtual machines by delegating low-level firmware operations to the pymobiledevice3 Python library through a bundled bridge script.**

This guide explains the architecture and implementation details for using **pymobiledevice3 for DFU restore in iOS VMs** within the Lakr233/vphone-cli project. The Swift-based CLI orchestrates Python processes via a bridging layer, enabling seamless firmware restoration inside virtualized iOS environments.

## Architecture Overview

The restore workflow cleanly separates Swift orchestration from Python DFU logic. This design ensures portability across macOS hosts and CI environments while leveraging pymobiledevice3's native device communication capabilities.

### Swift Orchestration Layer

The CLI entry point resides in [`VPhoneRestoreCLI.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneRestoreCLI.swift), which implements the `vphone restore` command. This layer handles argument parsing, VM bundle selection, and resource resolution before spawning the Python subprocess.

The core orchestration logic lives in `VPhoneRestoreCommand.run()`, where the `pmd3(_:,extra:)` helper constructs the bridge invocation:

```swift
var args = [
    resources.pmd3Bridge.path,    // → scripts/pymobiledevice3_bridge.py
    subcommand,                   // e.g., "restore-update"
    "--vm-dir", "."
]
if let udid { args += ["--udid", udid] }
args += ["--ecid", ecidValue] + extra
args += Array(repeating: "-v", count: min(v.rawValue, 2))

```

### Python Bridge Layer

The [`scripts/pymobiledevice3_bridge.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/pymobiledevice3_bridge.py) script acts as a thin translation layer between Swift CLI arguments and pymobiledevice3's Python API. It exposes sub-commands such as `restore-update` and `restore-get-shsh`, handling DFU client sessions, SHSH blob fetching, and AEA image decryption for offline restores.

## Environment Setup and Prerequisites

Before executing a DFU restore, [`VPhoneResources.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneResources.swift) ensures a compatible Python environment exists with required native dependencies.

### Python Interpreter Resolution

The `pythonExecutable()` method selects a usable interpreter through the following priority order:

1. **`VPHONE_PYTHON`** environment variable override
2. Development virtual environment (`.venv/bin/python3`)
3. Per-user managed venv (`~/.vphone/venv`)
4. Bootstrap a new venv on first run if none exist

The selected interpreter must satisfy runtime checks for **ipsw-parser** and **keystone-engine**, the two core native components required by pymobiledevice3. These dependencies are declared in [`requirements.txt`](https://github.com/Lakr233/vphone-cli/blob/main/requirements.txt) with the constraint `pymobiledevice3>=9.5.0`.

## Step-by-Step DFU Restore Workflow

Understanding the end-to-end execution flow helps diagnose issues and extend functionality.

### 1. Resource Resolution

`VPhoneResources.resolve()` discovers the bridge script location and validates the Python environment. It exposes the bridge path via `resources.pmd3Bridge`, pointing to [`scripts/pymobiledevice3_bridge.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/pymobiledevice3_bridge.py) in the repository root.

### 2. Command Construction

The Swift layer builds the argument vector targeting the bridge script with these key parameters:

- **`subcommand`**: The pymobiledevice3 operation (e.g., `restore-update`)
- **`--vm-dir`**: Path to the iOS VM bundle directory
- **`--udid`**: Optional device identifier for multi-device scenarios
- **`--ecid`**: Required device ECID for DFU targeting
- **`--tss`**: Path to SHSH blob for offline restores
- **`-v`**: Verbosity flags (up to two levels)

### 3. Process Execution

`VPhoneProcessRunner.runStreaming` spawns the Python process with streaming output:

```swift
let python = try resources.pythonExecutable()
return try VPhoneProcessRunner.runStreaming(python, args,
                                            cwd: bundle.url,
                                            echo: v.showsToolDetail)

```

This streams stdout/stderr to the console in real-time, with `showsToolDetail` controlling echo behavior.

### 4. Post-Restore Metadata

Upon successful completion (exit code 0), `VPhoneRestoreInfo.derive` extracts iOS and CloudOS version metadata from the restored firmware, writing the results to [`restore-info.json`](https://github.com/Lakr233/vphone-cli/blob/main/restore-info.json) within the VM bundle.

## Command-Line Usage Examples

The following `vphone restore` commands demonstrate practical pymobiledevice3 integration patterns.

### Basic DFU Restore

Restore a running VM using automatic ECID detection:

```bash
vphone restore myiPhone

```

### Specifying Device Identifiers

When multiple devices are present, provide explicit UDID and ECID:

```bash
vphone restore myiPhone --udid 00001234-5678-90AB-CDEF-1234567890AB

```

### Offline Restore with SHSH

Perform a fully offline restore using a cached SHSH blob and local AEA decryption:

```bash
vphone restore myiPhone --offline --tss /path/to/blob.shsh

```

### SHSH Blob Extraction Only

Fetch the SHSH blob without performing the actual restore:

```bash
vphone restore myiPhone --get-shsh

```

## Programmatic Implementation

You can replicate the CLI's restore logic in custom Swift tooling using the same bridging approach.

### Swift Bridge Invocation

This simplified implementation mirrors `VPhoneRestoreCommand.pmd3(_:,extra:)`:

```swift
func invokePymobiledevice3(_ subcommand: String,
                           vmBundle: URL,
                           ecid: String,
                           udid: String? = nil,
                           extra: [String] = []) throws -> Int32 {
    let resources = VPhoneResources.resolve()
    let python = try resources.pythonExecutable()
    
    var args = [
        resources.pmd3Bridge.path,
        subcommand,
        "--vm-dir", vmBundle.path,
        "--ecid", ecid
    ]
    
    if let udid = udid {
        args += ["--udid", udid]
    }
    args += extra
    
    return try VPhoneProcessRunner.runStreaming(
        python,
        args,
        cwd: vmBundle,
        echo: true
    )
}

```

### Direct Python Bridge Execution

For debugging or custom Python workflows, invoke the bridge directly:

```bash
python3 scripts/pymobiledevice3_bridge.py restore-update \
    --vm-dir /path/to/vm.bundle \
    --ecid 1234567890 \
    --udid 00001234-5678-90AB-CDEF-1234567890AB \
    -v -v

```

The bridge script instantiates `pymobiledevice3.restore.RestoreClient` and executes the DFU protocol sequence against the specified ECID.

## Summary

- **`vphone-cli`** orchestrates pymobiledevice3 via a Python bridge script located at [`scripts/pymobiledevice3_bridge.py`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/pymobiledevice3_bridge.py).
- **Environment management** automatically bootstraps Python venvs and validates dependencies (ipsw-parser, keystone-engine) through [`VPhoneResources.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneResources.swift).
- **Command construction** in `VPhoneRestoreCommand` assembles arguments including `--vm-dir`, `--ecid`, and optional `--tss` flags for offline operations.
- **Process execution** uses `VPhoneProcessRunner.runStreaming` for real-time output streaming during firmware restoration.
- **Metadata extraction** via `VPhoneRestoreInfo.derive` records iOS version information to [`restore-info.json`](https://github.com/Lakr233/vphone-cli/blob/main/restore-info.json) after successful restores.

## Frequently Asked Questions

### What Python version and dependencies does vphone-cli require for DFU restore?

vphone-cli requires Python 3 with pymobiledevice3 version 9.5.0 or higher. The interpreter must have **ipsw-parser** and **keystone-engine** installed, as these native libraries handle firmware parsing and ARM instruction emulation. The [`VPhoneResources.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneResources.swift) module automatically checks for these dependencies and can bootstrap a dedicated virtual environment at `~/.vphone/venv` if needed.

### How does the bridge script handle offline restores without internet connectivity?

When the `--tss` parameter is provided to the bridge script, [`pymobiledevice3_bridge.py`](https://github.com/Lakr233/vphone-cli/blob/main/pymobiledevice3_bridge.py) loads the cached SHSH blob locally instead of requesting it from Apple's TSS servers. It also decrypts AEA (Apple Encrypted Archive) firmware images locally using the provided signatures, allowing full DFU restores without network access to Apple's signing infrastructure.

### Can I use vphone-cli restore features on non-Apple Silicon Macs?

The architecture depends on pymobiledevice3's ability to communicate with iOS devices via USB or virtualized interfaces. While the Swift code is platform-agnostic, the underlying restore libraries (particularly those handling DFU mode and image decryption) typically require macOS with appropriate kernel drivers. The Python bridge itself runs on any platform supporting pymobiledevice3, but VM integration assumes a macOS host environment.

### What information does vphone-cli record after a successful DFU restore?

Upon successful completion (exit code 0), the CLI invokes `VPhoneRestoreInfo.derive` to parse the restored firmware bundle and extract the iOS version number, build identifier, and CloudOS version metadata. This information is serialized to [`restore-info.json`](https://github.com/Lakr233/vphone-cli/blob/main/restore-info.json) within the VM bundle directory, enabling version tracking and subsequent update eligibility checks.