# How cfw_install_host.sh Host-Mounts the CFW Driver Without Running the VM

> Discover how cfw_install_host.sh mounts CFW driver on macOS host without running the VM. Learn to install custom firmware on a powered-off virtual iPhone.

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

---

**The [`cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_host.sh) script attaches the VM's `Disk.img` as a raw disk to the macOS host, mounts the APFS volumes directly, installs the custom firmware files, and flips the boot snapshot—all while the virtual iPhone remains powered off.**

The `vphone-cli` project by Lakr233 provides a lightweight virtual iPhone environment for iOS security research. Understanding how [`cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_host.sh) host-mounts the CFW driver without leaving the VM running enables researchers to perform deterministic, scriptable firmware modifications without booting the guest operating system or managing complex guest-side privileged operations.

## How Host-Mode Installation Works

Unlike traditional guest-mode installation that requires the VM to be running, the host-mode workflow implemented in [`scripts/cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/cfw_install_host.sh) operates directly on the virtual disk image. This approach attaches the raw disk image to the macOS host, manipulates the APFS container structure, and updates the boot snapshot offline.

The script follows a strict sequence: validate the environment, attach the disk image as a raw block device, identify the system volume within the APFS container, mount the required volumes to temporary host paths, execute the variant-specific installer, flip the boot snapshot to activate the new firmware, and clean up all resources.

## Step-by-Step Execution Flow

### Root Privilege Enforcement

The script first ensures it operates with elevated privileges. If not running as root, it re-executes itself using `sudo` while preserving any `SUDO_ASKPASS` environment variable for non-interactive deployments.

```bash

# From scripts/cfw_install_host.sh lines 37-40

if [ "$(id -u)" != "0" ]; then
    exec sudo -A "$0" "$@"
fi

```

This requirement exists because mounting APFS containers, modifying volume ownership, and writing to the raw disk image require kernel-level access that standard user accounts cannot obtain.

### Disk Image Validation

Before attempting any mount operations, the script verifies that the virtual machine is truly powered off. It locates the VM directory and checks for the existence of `Disk.img`, then confirms no other process is accessing the file using `lsof`.

```bash

# From scripts/cfw_install_host.sh lines 43-46 and 58-60

VM_DIR="${1:-./vm}"
DISK_IMG="$VM_DIR/Disk.img"

if lsof "$DISK_IMG" >/dev/null 2>&1; then
    echo "[!] Error: Disk.img is in use. Is the VM running?"
    exit 1
fi

```

This check prevents corruption that would occur if the host attempted to mount a disk image currently in use by a running virtual machine.

### Raw Disk Attachment

With the VM confirmed offline, the script attaches the `Disk.img` file as a raw disk without creating a standard mount point. It uses `hdiutil` with the `CRawDiskImage` class specification to expose the underlying block device.

```bash

# From scripts/cfw_install_host.sh lines 63-66

BASEDISK=$(hdiutil attach -nomount -imagekey diskimage-class=CRawDiskImage "$DISK_IMG" | \
    grep "/dev/disk" | head -1 | awk '{print $1}')
CONT="${BASEDISK}s1"

```

The `-nomount` flag ensures the filesystem remains unmounted while the block device becomes available for APFS container operations. The script extracts the first slice (`${BASEDISK}s1`) as the APFS container reference.

### APFS Container Discovery

The script must identify the specific system volume within the APFS container that holds the iOS operating system. It parses `diskutil apfs list` output to locate the volume marked with the "System (Case-sensitive)" role.

```bash

# From scripts/cfw_install_host.sh lines 65-66

SYS=$(diskutil apfs list "$CONT" | grep -A 5 "System (Case-sensitive)" | \
    grep "Volume " | head -1 | awk '{print $2}')

```

This yields two critical variables: `CONT` (the container device path) and `SYS` (the system volume identifier), which the variant-specific installers use to target the correct mount points.

### Volume Mounting and File Installation

A cleanup function registered with `trap` ensures temporary mount points are unmounted and the disk is detached on script exit, regardless of success or failure.

```bash

# From scripts/cfw_install_host.sh lines 70-76

cleanup() {
    umount /private/tmp/cfwhost/mnt* 2>/dev/null || true
    hdiutil detach "$BASEDISK" >/dev/null 2>&1 || true
}
trap cleanup EXIT

```

The script then executes the variant-specific installer (such as [`cfw_install.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install.sh) or [`cfw_install_dev.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_dev.sh)) inside a subshell that receives the container path via the `CFW_HOST_CONTAINER` environment variable.

```bash

# From scripts/cfw_install_host.sh lines 78-85

(
    export CFW_HOST_CONTAINER="$CONT"
    bash "$INSTALLER"
)

```

These installer scripts mount the APFS volumes under `/private/tmp/cfwhost/mnt*` directories, copy the custom firmware files onto the mounted filesystems, and perform any necessary configuration changes—all on the host side.

### Boot Snapshot Manipulation

After file installation completes, the script activates the new firmware by renaming the boot snapshot. It invokes [`tools/apfs_snap_rename.py`](https://github.com/Lakr233/vphone-cli/blob/main/tools/apfs_snap_rename.py) to rename the snapshot from `com.apple.os.update` to the live system volume name.

```bash

# From scripts/cfw_install_host.sh lines 89-91

python3 tools/apfs_snap_rename.py "$CONT" "$SYS"

```

This operation modifies the APFS metadata to point the default boot volume to the newly installed firmware. Because this happens on the host-mounted image, the VM never requires a reboot to apply the changes.

### Cleanup and Ownership Restoration

Finally, the script restores file ownership to the original invoking user. This prevents permission-denied errors when subsequent commands like `make boot` or `setup_machine` attempt to access the modified `VM_DIR`.

```bash

# From scripts/cfw_install_host.sh lines 102-106

if [ -n "$SUDO_USER" ]; then
    chown -R "$SUDO_USER" "$VM_DIR"
fi

```

The cleanup trap ensures the raw disk image is properly detached and temporary mount points are removed, leaving the VM in a clean, offline state ready for the next boot.

## Usage Examples

Install the default experimental variant into the standard `./vm` directory:

```bash
sudo ./scripts/cfw_install_host.sh

```

Install a specific jailbreak variant to a custom VM location:

```bash
sudo ./scripts/cfw_install_host.sh --variant jb /path/to/custom/vm

```

Successful execution produces output similar to:

```text
[*] host-mode CFW install: variant=exp vm=/path/to/vphone-cli/vm
[*] attached: container=disk2s2 system=disk2s5
[*] running cfw_install_exp.sh (files placed on host mounts)...
[*] flipping boot snapshot offline (com.apple.os.update -> live volume)...
[+] host-mode CFW install complete. Boot with: make boot

```

## Summary

- **Direct disk attachment**: [`cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_host.sh) uses `hdiutil attach -nomount -imagekey diskimage-class=CRawDiskImage` to expose the VM's `Disk.img` as a block device without mounting the filesystem.
- **Offline APFS manipulation**: The script identifies the system volume via `diskutil apfs list` and mounts it to temporary host directories under `/private/tmp/cfwhost/`.
- **Snapshot-based activation**: The Python helper [`apfs_snap_rename.py`](https://github.com/Lakr233/vphone-cli/blob/main/apfs_snap_rename.py) flips the boot snapshot offline, activating the CFW without requiring a VM reboot.
- **Safety mechanisms**: Process checks via `lsof` ensure the VM is powered off, while a `trap` cleanup function guarantees resource release even on script failure.
- **Permission management**: The script elevates to root for block device access, then reverts ownership to `$SUDO_USER` to maintain accessibility for subsequent operations.

## Frequently Asked Questions

### What is the difference between host-mode and guest-mode CFW installation?

Host-mode installation, implemented in [`cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/cfw_install_host.sh), attaches the VM disk image directly to the macOS host and modifies the filesystem while the VM remains powered off. Guest-mode installation requires the VM to be running and executes installation commands inside the guest operating system. Host-mode is faster, more deterministic, and avoids the complexity of managing privileged operations within the guest environment.

### Why does the script require root privileges?

The script requires root access because mounting APFS containers using `hdiutil` and `diskutil` requires kernel-level permissions. Additionally, writing to the raw disk image blocks and modifying volume ownership with `chown` on APFS volumes demand elevated privileges that standard macOS user accounts cannot obtain.

### How does the script ensure the VM is not running during installation?

Before attaching the disk image, the script checks for open file handles on `Disk.img` using the `lsof` command according to [`scripts/cfw_install_host.sh`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/cfw_install_host.sh) lines 58-60. If any process is accessing the image, the script aborts with an error message. This prevents filesystem corruption that would occur if the host and guest simultaneously accessed the same block device.

### What happens if the installation process is interrupted?

The script registers a `cleanup` function with `trap EXIT` that automatically unmounts any temporary mount points under `/private/tmp/cfwhost/` and detaches the raw disk image using `hdiutil detach`. This ensures that even if the script fails or receives a termination signal, the host does not retain dangling mounts or locked disk images that would prevent subsequent VM operations.