How vphone-cli Patches the TXM Coprocessor for Different Firmware Variants

vphone-cli uses two specialized Swift classes—TXMPatcher for release builds and TXMDevPatcher for developer builds—to dynamically patch the TXM trust-cache checks and debugger gates in the flat binary firmware image.

The Lakr233/vphone-cli repository implements a sophisticated firmware patching pipeline that treats the Trusted Execution Monitor (TXM) as a critical pre-kernel stage requiring variant-specific modifications. Understanding how vphone-cli TXM coprocessor patching works reveals why jailbreak and developer firmware builds require different binary modifications than standard release builds.

The Two-Tier TXM Patcher Architecture

The patching system centers on two concrete implementations that share a common foundation but diverge based on the target firmware variant.

The Base TXMPatcher Class (Release Variant)

The TXMPatcher class, defined in sources/FirmwarePatcher/TXM/TXMPatcher.swift, handles the fundamental requirements for all firmware builds. Its primary responsibility is implementing the trust-cache bypass that allows the kernel to load custom trust-caches instead of enforcing Apple's default security policy.

This patcher scans the flat binary TXM image for two specific anchor points: the selector-24 hash-flags call site and the legacy binary-search guard. Upon locating these instructions using Capstone disassembly, it patches the conditional branches to NOP sequences or redirects them to harmless stubs, effectively neutralizing the trust-cache verification without altering the kernel itself.

The Extended TXMDevPatcher Class (Developer Variants)

For dev, jb, and exp firmware variants, sources/FirmwarePatcher/TXM/TXMDevPatcher.swift extends TXMPatcher with three additional modification groups:

  • Entitlement injection: Locates string references to "get-task-allow" and replaces the entitlement blob loading routine by patching the branch link (BL) site that calls the original entitlement loader.
  • Debugger gate removal: Identifies the "selector-42|29" stub used to gate debugger access, creates a code cave within the binary, and rewrites the branch instruction to always enable the debugging interface.
  • Developer-mode enablement: Finds the internal developer-mode guard and patches its conditional branch to unconditionally succeed, bypassing production restrictions.

How the Firmware Pipeline Selects the Right Patcher

The selection logic resides in sources/FirmwarePatcher/Pipeline/FirmwarePipeline.swift, which orchestrates the boot chain sequence:


AVPBooter → iBSS → iBEC → LLB → TXM → Kernel → DeviceTree

When the pipeline initializes, it evaluates the FirmwareVariant enum value provided in the options. If the variant matches .dev, .jb, or .exp, the pipeline instantiates TXMDevPatcher; otherwise, it uses the base TXMPatcher. Both classes operate on a mutable ByteBuffer created from the raw TXM image data, applying their respective patches before returning the modified binary to the pipeline.

// Simplified pipeline selection logic
if options.variant == .dev || options.variant == .jb || options.variant == .exp {
    let txmPatcher = TXMDevPatcher(data: txmData, verbose: true)
    txmPatcher.applyAll()
} else {
    let txmPatcher = TXMPatcher(data: txmData, verbose: true)
    txmPatcher.applyAll()
}

Technical Implementation of TXM Patches

The patching process relies on dynamic binary analysis rather than fixed offsets, making it resilient across firmware revisions.

Trust-Cache Bypass (Universal)

Both variants require neutralizing the TXM's trust-cache enforcement. The patcher uses buffer.findString to locate anchor strings like "selector24", then disassembles the surrounding ARM64 instructions (ADRP + ADD + BL patterns) using Capstone. It calculates the correct NOP encoding via Keystone and overwrites the branch instructions that would otherwise halt the boot process when encountering an unsigned trust-cache.

Entitlement Injection (Developer Variants Only)

When patching for jailbreak or developer mode, TXMDevPatcher searches for the "get-task-allow" string reference within the binary. After locating the entitlement loading routine, it creates a small code cave containing the custom entitlement blob pointer, then patches the original BL instruction to branch to this cave instead of the standard entitlement validator.

Debugger Gate Removal (Developer Variants Only)

The debugger patch requires creating a functional code cave because the replacement logic is more complex than a simple NOP. The patcher identifies the "selector-42|29" validation stub, allocates space within an unused region of the TXM binary, writes shellcode that unconditionally enables debugging, and patches the original gate branch to redirect execution to this cave using patchBranchToCave.

Developer-Mode Enablement (Developer Variants Only)

Similar to the debugger patch, this modification locates the internal developer-mode verification routine and patches its conditional branch (typically a CBZ or CBNZ instruction) to always take the "success" path, effectively hardcoding developer mode to active regardless of hardware fuses.

Dynamic Binary Scanning and Cave Generation

Because the TXM is a flat binary without Mach-O headers, vphone-cli cannot rely on symbol tables or pre-computed offsets. Instead, both patcher classes dynamically scan for string anchors and instruction patterns every time they run. This approach ensures compatibility across different firmware versions where binary layout shifts might otherwise break hardcoded patches.

When the patcher needs to inject custom logic (for debugger enablement or entitlement replacement), it employs a code cave strategy:

  1. Scans for null-filled or unused regions within the TXM image
  2. Aligns the cave to architecture boundaries (typically 4-byte aligned for ARM64)
  3. Writes the replacement shellcode or data blob into the cave
  4. Calculates the relative branch offset from the original patch site to the cave
  5. Uses patchBranchBack to return execution flow to the original routine after the custom code executes

Summary

  • vphone-cli implements TXM patching through two Swift classes in the sources/FirmwarePatcher/TXM/ directory.
  • TXMPatcher handles release builds by bypassing trust-cache checks via NOP patches to selector-24 and legacy hash guards.
  • TXMDevPatcher extends the base class to add entitlement injection, debugger gate removal, and developer-mode enforcement for jailbreak variants.
  • The FirmwarePipeline selects the appropriate patcher based on the FirmwareVariant enum value.
  • Both patchers use Capstone for disassembly and Keystone for re-assembly to ensure architecture-correct instruction encoding.
  • Dynamic scanning of the flat binary TXM image ensures patches work across firmware revisions without fixed offsets.

Frequently Asked Questions

What is the TXM coprocessor in iOS firmware?

The TXM (Trusted Execution Monitor) is a pre-kernel coprocessor binary that initializes security policies, including trust-cache validation and debugger authorization, before handing control to the main kernel. According to the vphone-cli source code, the TXM appears in the boot chain between LLB (Low Level Bootloader) and the Kernel, making it a critical enforcement point for device security policies.

Why does vphone-cli use dynamic scanning instead of fixed offsets?

The TXM ships as a flat binary without Mach-O headers or symbol tables, meaning instruction locations vary between firmware versions. By using Capstone to disassemble and scan for string anchors like "selector24" and "get-task-allow" at runtime, the patcher remains functional across iOS updates that would break hardcoded offset-based patching.

What is the difference between release and research TXM binaries?

Per the research documentation in research/txm_variant_diff.md, the release and research TXM binaries are identical (sharing the same SHA-256 hash) and differ only in a cosmetic variant string embedded in the binary. Consequently, vphone-cli does not maintain separate TXM files for different builds; instead, it applies the same base binary with variant-specific patches (trust-cache only for release, additional debugger/entitlement patches for research/dev builds).

How do I specify which TXM variant patcher to use?

You specify the variant using the command-line interface when invoking the firmware pipeline. The --variant flag accepts values like regular, dev, jb, or exp, which the FirmwarePipeline.swift uses to instantiate either TXMPatcher or TXMDevPatcher:

vphone-cli --txm <txm-im4p> --variant dev    # Uses TXMDevPatcher

vphone-cli --txm <txm-im4p> --variant regular # Uses TXMPatcher

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 →