ARM64Encoder in vphone-cli Firmware Patching: Pure-Swift ARM64 Instruction Generation
ARM64Encoder is a lightweight, pure-Swift utility that generates PC-relative ARM64 machine instructions on-the-fly, enabling vphone-cli to safely rewrite iOS firmware binaries without relying on external assemblers.
The vphone-cli project provides specialized tools for modifying iOS kernel and boot components. At its core, ARM64Encoder handles the deterministic construction of binary instructions, allowing developers to patch iBoot, TXM, and kernel binaries using in-memory encoding rather than platform-specific external tooling.
Core Instruction Encoding Capabilities
The encoder supports deterministic generation of essential ARM64 instruction types required for firmware manipulation. Each method returns a 4-byte Data value representing the encoded instruction, ready for direct binary injection.
Branch Instructions (B, BL, TBZ/TBNZ)
ARM64Encoder encodes unconditional and conditional branches with correct signed offset handling. This allows patchers to redirect execution flow by calculating PC-relative offsets between the current instruction and target addresses.
// Encode an unconditional branch from 0x1000 to 0x2000
if let branch = ARM64Encoder.encodeB(from: 0x1000, to: 0x2000) {
print(branch.hexEncodedString()) // → 0x14000000
}
The encoder also supports BL (branch with link) for function calls and TBZ/TBNZ (test bit and branch) for conditional flow control.
Address Loading Sequences (ADRP + ADD)
Firmware patches frequently need to materialize absolute addresses relative to the current program counter. ARM64Encoder generates the standard ADRP + ADD sequence used across Apple's 64-bit ARM architecture.
let pc = UInt64(0x1000)
let target = UInt64(0x2000) // 4 KB-aligned pages
let adrp = ARM64Encoder.encodeADRP(rd: 2, pc: pc, target: target)!
let addImm = ARM64Encoder.encodeAddImm12(rd: 2, rn: 2, imm12: 0x0)!
According to the vphone-cli source code, this pattern appears in sources/FirmwarePatcher/IBoot/IBootPatcher.swift at lines 348-356, where the encoder builds an address-loading pair to redirect a function pointer within iBoot firmware.
Immediate and Register Moves (MOVZ, MOV)
The utility encodes immediate value loading and register-to-register transfers essential for modifying constants and clearing registers during jailbreak patches.
// Load immediate 0x1 into register W6
let bytes = ARM64Encoder.encodeMovzW(rd: 6, imm16: 0x1)
// Move XZR (zero register) to X8: mov x8, xzr
let mov = ARM64Encoder.encodeMovX(rd: 8, rm: 31)
This capability is heavily utilized in sources/FirmwarePatcher/Kernel/JBPatches/KernelJBPatchSecureRoot.swift at line 49, where immediate moves replace existing instructions to disable security checks.
Branch Target Decoding
Beyond encoding, ARM64Encoder decodes branch targets from existing instructions. This allows patchers to analyze firmware code before applying modifications, ensuring that replaced instructions preserve original execution semantics.
let pc = 0x1000
let dest = 0x2000
if let bl = ARM64Encoder.encodeBL(from: pc, to: dest) {
let insn = UInt32(littleEndian: bl.withUnsafeBytes { $0.load(as: UInt32.self) })
let target = ARM64Encoder.decodeBranchTarget(insn: insn, pc: UInt64(pc))
assert(target == UInt64(dest))
}
Advantages Over External Assemblers
Using ARM64Encoder instead of the Keystone assembler provides three critical benefits for firmware patching workflows:
- Deterministic output – The pure-Swift implementation produces identical byte sequences across platforms without requiring external binaries or dynamic libraries.
- Safety validation – Encoders strictly validate operands including alignment, immediate ranges, and register limits before emitting bytes, reducing the risk of corrupting firmware images.
- Performance – Encoding occurs entirely in-memory using simple bitwise operations, making it ideal for batch patch generation scenarios where speed matters.
Real-World Implementation in vphone-cli
The encoder serves as the foundation for multiple firmware patching modules within the repository.
iBoot Patching
In sources/FirmwarePatcher/IBoot/IBootPatcher.swift, the encoder constructs PC-relative address calculations to modify bootloader behavior:
guard let newAdrp = ARM64Encoder.encodeADRP(rd: 2, pc: UInt64(adrpOff), target: UInt64(newOff)) else { … }
guard let newAdd = ARM64Encoder.encodeAddImm12(rd: 2, rn: 2, imm12: imm12) else { … }
This pattern appears at lines 348-356, demonstrating how the tool rewrites function pointer tables within iBoot binaries.
TXM Device Patching
The TXM patching module at sources/FirmwarePatcher/TXM/TXMDevPatcher.swift utilizes branch encoding to redirect execution back to stub code. At line 300, the implementation generates a branch instruction using:
guard let bInsn = ARM64Encoder.encodeB(from: body + 4, to: epilogueOff) else { … }
Kernel Jailbreak Patches
Various kernel patches located in sources/FirmwarePatcher/Kernel/JBPatches/ leverage the encoder for safe instruction replacement. The secure root patch specifically uses encodeMovzW to inject immediate values that bypass security constraints.
Project Structure and Testing
The encoder implementation spans several key files within the vphone-cli repository:
sources/FirmwarePatcher/ARM64/ARM64Encoder.swift– Core encoding and decoding routines for all supported instruction types.sources/FirmwarePatcher/ARM64/ARM64Constants.swift– Pre-computed constant tables generated by the encoder at build time.tests/FirmwarePatcherTests/FirmwarePatcherTests.swift– Unit tests verifying correctness ofencodeB,encodeBL,encodeADRP, and other critical methods.
These files collectively ensure that ARM64Encoder maintains bit-perfect accuracy when generating instructions for ARM64 firmware components.
Summary
- ARM64Encoder provides pure-Swift generation of PC-relative ARM64 instructions for firmware modification.
- The encoder supports branches (
B,BL), address loading (ADRP+ADD), and immediate moves (MOVZ,MOV) with built-in operand validation. - It eliminates dependencies on external assemblers like Keystone, offering deterministic, cross-platform patch generation.
- Core usage appears in
IBootPatcher.swift,TXMDevPatcher.swift, and various kernel jailbreak patches across the vphone-cli codebase. - Comprehensive unit tests in
FirmwarePatcherTests.swiftverify encoding correctness for production firmware manipulation.
Frequently Asked Questions
How does ARM64Encoder differ from the Keystone assembler?
ARM64Encoder is a self-contained Swift implementation that generates instructions deterministically without external dependencies. Unlike Keystone, which requires platform-specific binaries and runtime linking, the encoder performs all operations in-memory using bitwise calculations, ensuring consistent output across macOS, Linux, and other development environments.
What types of ARM64 instructions can ARM64Encoder generate?
The encoder supports unconditional branches (B), branch-with-link (BL), test-and-branch (TBZ/TBNZ), address page loading (ADRP), immediate additions (ADD with 12-bit immediates), immediate moves (MOVZ), and register moves (MOV Xd, Xm). It also decodes branch targets from existing instructions for analysis purposes.
How does ARM64Encoder ensure patch safety?
Each encoding method validates operands before emission, checking for proper 4-byte alignment in branch targets, valid immediate ranges, and legal register numbers. This strict validation prevents the generation of malformed instructions that could corrupt iBoot, kernel, or TXM firmware images during the patching process.
Where is ARM64Encoder used within the vphone-cli codebase?
The encoder appears throughout the firmware patching system, specifically in sources/FirmwarePatcher/IBoot/IBootPatcher.swift for bootloader modifications, sources/FirmwarePatcher/TXM/TXMDevPatcher.swift for device-specific patches, and multiple files under sources/FirmwarePatcher/Kernel/JBPatches/ for jailbreak implementations including KernelJBPatchSecureRoot.swift.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →