How vphone-cli Avoids Hardcoded Offsets in iOS Kernel Patching

vphone-cli eliminates hardcoded file offsets by using Capstone-driven disassembly to build searchable instruction indices, then locates patch sites dynamically through semantic pattern matching and generates replacement code with Keystone.

vphone-cli is an open-source firmware patching tool for iOS devices that applies kernel jailbreak patches without relying on static memory addresses. Instead of embedding hardcoded offsets that break across iOS versions, the tool uses dynamic binary analysis to discover patch locations semantically. This approach centers on the KernelPatcher class and its patch modules located in sources/FirmwarePatcher/Kernel/JBPatches.

Dynamic Mach-O Analysis and Instruction Indexing

The foundation of offset-free patching begins with comprehensive binary parsing. In sources/FirmwarePatcher/Kernel/KernelPatcher.swift, the findAll() method orchestrates the analysis pipeline by first parsing the kernel's Mach-O structure via parseMachO().

Rather than referencing fixed file positions, the patcher builds two searchable indices by walking the disassembly with Capstone:

  • ADRP index – Records every ADRP instruction to resolve string-based symbols to concrete code locations
  • BL index – Records every branch-and-link instruction to discover guard code-paths
parseMachO()
buildADRPIndex()
buildBLIndex()

These indices enable the system to locate instructions by their semantic shape and relationships rather than their byte offsets.

Semantic Pattern Matching for Target Discovery

Individual patch modules locate targets using semantic anchors instead of raw virtual addresses. According to the source code in sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchIomfbSwap.swift, patches explicitly declare "// Anchor (structural, no hardcoded offsets)" when targeting specific kernel functions.

The patcher identifies locations through multiple pattern types:

  • String literals – Locating functions by their log messages (e.g., "PE_i_can_has_debugger" or "TXM [Error]: CodeSignature")
  • Call-flow sequences – Matching patterns like ADRP+ADD → BL → TBNZ to find specific control flow paths
  • Instruction sequences – Detecting distinctive patterns such as BL … → CMP w0, #imm

For example, KernelJBPatchPEiCanHasDebugger.swift locates the target function by searching for the string literal "PE_i_can_has_debugger", then resolves its virtual address through the ADRP index without referencing any hardcoded offset.

Runtime Assembly Generation with Keystone

Once the target instruction is identified semantically, the patcher generates replacement code dynamically. Instead of embedding raw byte literals tied to specific offsets, vphone-cli uses Keystone-engine to assemble instructions from text.

// Example from a patch module
let newInst = asm("mov w0, #1")   // assembled by Keystone
replaceInstruction(at: targetVA, with: newInst)

This approach ensures that replacement instructions like NOP, mov w0,#1, or ret are generated fresh for each target location. The replaceInstruction(at:with:) method writes the new bytes back into the binary buffer using the virtually resolved address, maintaining complete position independence.

Verification Against Legacy Hardcoded Methods

The research/kernel_patcher_verification.md document demonstrates that this dynamic approach produces byte-identical results compared to the former hardcoded patch list for the vphone600 kernel. This verification confirms that semantic pattern matching achieves functional equivalence while eliminating version-fragile offsets.

The patching pipeline integrates these components in sources/FirmwarePatcher/Pipeline/FirmwarePipeline.swift:

let kernelData = try Data(contentsOf: kernelPath)
let patcher = KernelPatcher(data: kernelData, isDev: false, applyExcGuard: true)
try patcher.apply()               // discovers and applies all patches
let patchedKernel = patcher.data // patched binary ready for signing

Summary

  • Dynamic indexing via Capstone disassembly replaces static offset tables with searchable instruction maps
  • Semantic anchors including string literals and call-flow patterns enable location-independent target discovery
  • Keystone assembly generates replacement instructions at runtime, avoiding embedded byte literals
  • Cross-version compatibility is maintained because patterns persist across kernel revisions even when addresses shift
  • Verified equivalence confirms the dynamic approach matches legacy hardcoded patches without the maintenance burden

Frequently Asked Questions

Why are hardcoded offsets problematic for iOS kernel patching?

Hardcoded offsets require manual recalculation for every iOS version and device variant. When Apple compiles a new kernel, function locations shift due to code changes, making static address lists immediately obsolete. vphone-cli avoids this maintenance burden by discovering locations dynamically through code patterns that remain structurally consistent.

How does vphone-cli locate functions without knowing their addresses?

The tool builds indices of all ADRP and BL instructions in the Mach-O binary. Patch modules then query these indices to resolve references to specific strings or call sequences. For example, finding "PE_i_can_has_debugger" involves searching the ADRP index for the page-relative load of that string's address, then calculating the target virtual address from the instruction operands.

What disassembly tools does vphone-cli use for binary analysis?

vphone-cli uses Capstone for disassembling AArch64 instructions and building the searchable indices, then employs Keystone for assembling replacement instructions. This separation ensures that the patcher reads the original binary through Capstone's analysis engine and writes modifications through Keystone's assembler, never relying on pre-computed byte arrays.

Does dynamic patching impact performance compared to hardcoded offsets?

The indexing phase adds initial overhead during the patch application process, but this is negligible for firmware generation workflows. The resulting patched kernel is byte-identical to manually offset versions, meaning runtime performance on the device remains unchanged. The trade-off favors robustness over the minimal speed cost of disassembly.

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 →