What Happens in Phase 2 of the Firmware Patching (CFW Installation) Process

Phase 2 is the core patch-application step that transforms a pristine iOS IPSW image into a custom firmware (CFW) by applying binary patches to iBoot, launchd, and system services, injecting dylibs, and verifying checksums before boot.

The vphone-cli project provides a pipeline for virtualizing iOS on Apple Silicon Macs. After Phase 1 prepares a merged cloud-OS and iPhone hybrid image, Phase 2 of the firmware patching process executes the actual CFW installation by modifying critical system binaries in-place within the VM’s working directory.

Boot-Nonce Handling (Sub-step 2-1)

The first operation in Phase 2 establishes the boot-nonce environment variable required for the iOS bootloader to accept the custom image. According to the research documented in research/iboot_patches.md, the scripts/cfw_install.sh orchestrator checks for an existing BOOT_NONCE variable in the environment. If absent, it invokes a helper utility to generate a fresh random nonce and injects it directly into the iBoot binary before the system boots. This ensures the secure boot chain recognizes the patched firmware as valid during subsequent startup phases.

Launchd Jetsam Patch (Sub-step 2-2)

Next, Phase 2 disables aggressive memory throttling that would otherwise terminate the VM’s background processes. The Python module scripts/patchers/cfw_patch_jetsam.py performs this modification by:

  1. Locating the anchor string "jetsam property category" within the launchd binary
  2. Resolving the surrounding code reference via ADRP+ADD instruction pairs
  3. Scanning for the conditional branch that enforces throttling
  4. Rewriting the branch to an unconditional jump (b <target>) using the Keystone assembler engine

The detailed algorithm flow is documented in research/cfw_patch_launchd_jetsam.md. This patch prevents the virtualized iOS environment from killing essential daemons under memory pressure.

System-Service Patches (Sub-step 2-3)

Beyond launchd, Phase 2 applies additional binary patches to boot-time services including launchdhook.dylib, the kernel, and boot-args. Each patcher module located under scripts/patchers/ follows a consistent pattern:

  • Locate an anchor string or symbol within the target binary
  • Resolve virtual addresses through ADRP+ADD sequences
  • Identify conditional branches controlling VM-specific restrictions
  • Replace branches with unconditional jumps or NOP instructions to enable private-API usage and load custom kernel extensions

These modifications collectively bypass VM-specific restrictions and enable jailbreak capabilities required for experimental variants.

Dylib Injection (Sub-step 2-4)

After patching core binaries, Phase 2 injects the custom dynamic library /cores/launchdhook.dylib to implement runtime hooks. The inject-dylib helper, called from cfw_install_jb.sh, performs the following:

  • Copies the dylib into the VM’s /cores/ directory
  • Adds an appropriate load command to the target binary (typically the patched launchd)
  • Ensures the library loads early in the boot sequence to intercept system calls

This injection mechanism provides the runtime environment necessary for CFW-specific functionality.

Verification and Logging (Sub-step 2-5)

The final sub-step validates all modifications before allowing the VM to boot. The orchestration script calculates SHA-256 checksums for every patched binary, comparing before-and-after digests to confirm successful writes. If any verification fails, the pipeline aborts immediately and prevents the VM from booting with corrupted firmware. Status lines print to the console throughout the process, providing a concise audit trail of applied patches.

Key Source Files and Entry Points

Understanding Phase 2 requires familiarity with these specific files in the Lakr233/vphone-cli repository:

How to Run Phase 2 Manually

While the standard workflow executes Phase 2 automatically, you can invoke specific components manually for debugging or development:


# Run the complete CFW installation pipeline (Phases 1-3)

make cfw_install

# Or execute Phase 2 explicitly via the installation script

./scripts/cfw_install.sh

Inspect the Jetsam patch behavior without writing changes:

python3 scripts/patchers/cfw_patch_jetsam.py \
    --binary ./vm_root/usr/sbin/launchd \
    --dry-run

Verify the boot-nonce was properly set during Phase 2:

grep BOOT_NONCE ./vm_root/boot/efi/bootargs.txt

Summary

  • Phase 2 applies in-place binary modifications to the firmware prepared in Phase 1, located in /var/vm/.
  • The process follows a strict sequence: boot-nonce handling, launchd Jetsam patching, system-service patches, dylib injection, and cryptographic verification.
  • All patches use Keystone for assembly and target ARM64 instruction sequences (ADRP+ADD) to locate patch points.
  • scripts/cfw_install.sh orchestrates the entire phase, calling specialized Python modules under scripts/patchers/.
  • SHA-256 checksums verify every modification; failures abort the boot process to prevent corruption.

Frequently Asked Questions

What happens if the boot-nonce is missing during Phase 2?

If the BOOT_NONCE environment variable is not set, the Phase 2 script automatically generates a fresh random nonce and injects it into the iBoot binary. This ensures the secure boot chain accepts the custom firmware without manual intervention.

Why does Phase 2 patch the launchd Jetsam settings?

The Jetsam patch prevents iOS from aggressively killing background processes due to memory pressure. In a virtualized environment, the default Jetsam throttling would terminate essential VM daemons; the patch rewrites the conditional branch to an unconditional jump, effectively disabling this restriction.

Where are the patched firmware files stored during Phase 2?

All modifications occur in-place within the VM’s working directory, typically /var/vm/. The original IPSW components from Phase 1 are overwritten with their CFW-ready counterparts, including patched versions of iBoot, launchd, and the kernel.

Can I run individual Phase 2 patches without executing the full script?

Yes. While scripts/cfw_install.sh runs the complete sequence, individual Python modules under scripts/patchers/ can be executed standalone. For example, cfw_patch_jetsam.py accepts --binary and --dry-run arguments to test specific patches against a target binary without modifying the full VM image.

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 →