vphone-cli 是一个通过 Apple Virtualization.framework 引导虚拟 iPhone (iOS 26) 的研究工具,利用 PCC (Private Cloud Compute) 研究虚拟机基础设施实现完整的 iOS 虚拟化。
目录
项目起源与技术演进 系统全景 固件组装流水线 混合身份签名架构 动态二进制补丁系统 Metal GPU 加速——核心技术突破 越狱机制深度剖析 Swift 虚拟机运行时 Host-Guest 控制协议 Guest Daemon 架构 GUI 与交互系统 构建系统 技术风险与未来挑战
术语与核心概念
本文档涉及大量 iOS 安全研究领域的专有术语,此处集中解释以便理解后续章节:
| SHSH Blob | restore_get_shsh 阶段获取该票据,从中提取 IM4M (IMG4 Manifest) 签名清单,用于对补丁后的引导链组件重新签名,使 Secure Boot 链信任我们修改过的 iBSS/iBEC/内核等。 | |
| CFW | 定制固件cfw_install.sh 安装到虚拟 iPhone 上的一系列用户空间补丁集合,包括:Cryptex 文件系统注入、设备激活锁绕过(mobileactivationd)、launchd 缓存验证禁用、SEP 工具修补(seputil)、GPU 驱动注入、SSH 守护进程部署等。CFW 安装分 7 个阶段,通过 SSH Ramdisk 在恢复完成后写入根文件系统。 | |
| IPSW | ||
| DFU | _setForceDFU 私有 API 使虚拟机直接进入 DFU 模式。 | |
| IM4P / IM4M / IMG4 | ||
| Cryptex | dyld_shared_cache、libSystem.B.dylib 等)和应用运行时从主系统分区中分离出来,独立签名和更新。分为 SystemOS Cryptex(系统库)和 AppOS Cryptex(应用运行时)。使用 AEA (Authenticated Encryption Archive) 格式加密。 | |
| Ramdisk | ||
| MACF | ||
| AMFI | ||
| SIP | ||
| Entitlement | com.apple.private.virtualization 等私有 entitlement,正常情况下仅 Apple 自身二进制持有。 | |
| Trust Cache | vphoned、dropbear)需要生成对应的 trust cache 才能在 AMFI 启用的 Guest 中执行。 |
0. 项目起源与技术演进
0.1 发现:PCC 固件中的 vphone600ap 组件
2024 年底,Apple 推出 Private Cloud Compute,宣称开辟云端 AI 隐私新纪元。2025 年底,安全研究社区注意到一个异常信号:Apple 在 cloudOS 26 的 PCC 固件中新增了 vphone600ap 相关组件——包括专用内核缓存 kernelcache.research.vphone600、设备树 DeviceTree.vphone600ap.im4p 等。
这些组件的名称强烈暗示 **"iPhone Research Environment Virtual Machine"**。这是 Apple 计划为安全研究者构建的虚拟 iPhone 环境,还是一次意外泄露?考虑到 2021 年 iOS 15.0 beta 至 15.1 beta3 OTA 中曾包含 DEVELOPMENT/KASAN 内核(持续约 4 个月),意外泄露的可能性不能排除。
2026 年 1 月,@_inside 在 Twitter 发布了利用这些 vphone600ap 组件启动虚拟 iPhone 的演示。相比此前的 QEMUAppleSilicon (Inferno) 项目,该方案运行流畅且支持 Metal 加速。这直接催生了本项目的开发(2026 年 1 月 31 日启动)。
0.2 技术传承谱系
本项目并非从零构建,而是站在三个关键前序项目的基础之上:
┌─────────────────────────────────────────────────────────────────────┐│ 技术传承链 ││ ││ security-pcc (Apple 官方) ││ └── /System/Library/SecurityResearch/usr/bin/vrevm 源码 ││ 发现: PV=3, BDID=0x90, ISA=2 硬件模型参数 ││ 发现: AVPBooter/AVPSEPBooter ROM 路径 ││ 发现: 1290x2796 分辨率 (iPhone 14 Pro Max/15 Plus/15 Pro Max) ││ │ ││ ▼ ││ super-tart (JJTech0130) ││ └── tart macOS VM 的扩展版: 自定义 ROM, 串口, DFU, GDB ││ 提供了 Virtualization.framework 私有 API 的 Swift 封装 ││ │ ││ ▼ ││ vma2pwn (nick-botticelli) ││ └── macOS 12.0.1 虚拟机引导链完整修改 ││ 提供了固件补丁 + DFU 恢复 + idevicerestore 的完整工作流 ││ prepare.sh: IM4P 提取 → RAW 补丁 → IM4P 重建 ││ vma2pwn.sh: DFU 模式 → idevicerestore 恢复 ││ │ ││ ▼ ││ vphone-cli (本项目) ││ └── 三者融合 + 核心创新: ││ ① 混合固件 (iPhone iOS 用户空间 + cloudOS 引导链) ││ ② 动态补丁 (零硬编码偏移, 取代 vma2pwn 的静态地址) ││ ③ Metal GPU 加速 (AppleParavirtGPU 驱动移植) ││ ④ vsock 控制协议 + Guest Daemon 架构 │└─────────────────────────────────────────────────────────────────────┘0.3 从硬编码到动态发现——关键架构演进
早期原型(super-tart-vphone writeup 阶段)使用硬编码文件偏移进行补丁,例如:
# 早期方式: 硬编码偏移 (仅适用于特定固件版本 26.1/23B85)patch(0x9D10, 0xd503201f) # iBSS: noppatch(0x9D14, 0xd2800000) # iBSS: mov x0, #0patch(0x2476964, 0xd503201f) # kernel: 根快照检查 NOPpatch(0x23cfde4, 0xd503201f) # kernel: 密封完整性 NOPvphone-cli 将此演进为零硬编码偏移的动态分析系统:通过字符串锚定、ADRP 交叉引用索引、BL 频率分析等技术自动定位补丁点,确保跨固件版本兼容。这是整个项目最重要的工程化改进之一。
0.4 libirecovery 适配
标准 libimobiledevice 工具链不识别 vresearch101ap 硬件模型。项目需要修改 libirecovery 以添加 vresearch101ap 的设备描述符,使 idevicerestore 能够正确匹配恢复身份并完成 DFU 固件刷写。这是一个容易被忽视但不可或缺的前置步骤。
1. 系统全景
1.1 核心思想
将 iPhone iOS IPSW(完整用户空间)与 cloudOS IPSW(PCC 引导链 + 内核)合并为混合固件,对引导链施加动态二进制补丁绕过签名验证,使其在 macOS Virtualization.framework 的 vresearch1 虚拟硬件上启动完整的 iPhone iOS。
1.2 端到端数据流
┌─────────────────────────────────────────────────────────────────────┐│ IPSW 来源 ││ iPhone IPSW (iOS 26.1) cloudOS IPSW (PCC vresearch101ap) ││ ├── iOS 用户空间 ├── 引导链 (LLB/iBSS/iBEC/SPTM/TXM) ││ ├── Cryptex SystemOS/AppOS ├── 内核缓存 (vphone600) ││ └── 静态信任缓存 └── 设备树/SEP/GPU/ANE 固件 │└──────────────┬──────────────────────────┬──────────────────────────┘ │ fw_prepare.sh │ ▼ ▼┌─────────────────────────────────────────────────────────────────────┐│ 合并工作目录 ││ iPhone*_Restore/ ││ ├── kernelcache.* ← 来自 cloudOS ││ ├── Firmware/{all_flash,dfu,agx,ane,pmp}/* ← 来自 cloudOS ││ ├── 058-*-*.dmg + 058-*-*.trustcache ← 保留 iPhone 的 ││ └── BuildManifest-iPhone.plist ← 备份原始清单 │└──────────────────────────┬──────────────────────────────────────────┘ │ fw_manifest.py ▼┌─────────────────────────────────────────────────────────────────────┐│ 混合 BuildManifest.plist + Restore.plist ││ 单一 DFU Erase Install 身份 (20 个组件) ││ 跨 vresearch101ap / vphone600ap / iPhone 三个来源嫁接 │└──────────────────────────┬──────────────────────────────────────────┘ │ fw_patch.py ▼┌─────────────────────────────────────────────────────────────────────┐│ 补丁后固件 (6 组件, 41+ 处修改) ││ AVPBooter(1) + iBSS(2) + iBEC(3) + LLB(6) + TXM(1) + Kernel(25) │└──────────────────────────┬──────────────────────────────────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ restore (DFU) ramdisk_build vm_create │ ramdisk_send │ │ │ │ └───────┬────────┘ │ ▼ │ cfw_install.sh │ (7 阶段 SSH 安装) │ │ │ └────────┬───────────────┘ ▼ make boot ┌─────────────┐ │ vphone-cli │ ← Swift 6.0 + Virtualization.framework │ PV=3 虚拟机 │ │ vsock 控制 │ ⟷ vphoned (Guest Daemon) └─────────────┘1.3 技术栈总览
graph TB subgraph "宿主 (macOS Sequoia 15+)" CLI["vphone-cli<br/>Swift 6.0 + SwiftPM"] VF["Virtualization.framework<br/>(Private APIs via Dynamic)"] CLI --> VF end subgraph "固件工具链 (Python + Shell)" FW["fw_prepare.sh<br/>IPSW 下载/合并"] MF["fw_manifest.py<br/>混合清单生成"] FP["fw_patch.py<br/>引导链补丁"] RB["ramdisk_build.py<br/>SSH Ramdisk"] CI["cfw_install.sh<br/>CFW 安装"] PT["patchers/<br/>kernel.py | iboot.py<br/>txm.py | cfw.py"] FW --> MF --> FP --> RB --> CI FP --> PT CI --> PT end subgraph "虚拟机 (iOS 26)" GD["vphoned<br/>Objective-C Guest Daemon"] IOS["iPhone iOS 用户空间"] GD --> IOS end CLI -.->|"vsock:1337"| GD FP -.->|"Capstone + Keystone"| PT2. 固件组装流水线
2.1 六阶段流水线
graph LR A["1. vm_create<br/>创建 VM 目录"] --> B["2. fw_prepare<br/>下载/合并 IPSW"] B --> C["3. fw_patch<br/>补丁引导链"] C --> D["4. restore<br/>DFU 刷写"] D --> E["5. ramdisk<br/>SSH 启动"] E --> F["6. cfw_install<br/>安装 CFW"] F --> G["正常启动"] style A fill:#2e2e2e,stroke:#60a5fa,color:#e0e0e0 style B fill:#2e2e2e,stroke:#60a5fa,color:#e0e0e0 style C fill:#2e2e2e,stroke:#fbbf24,color:#e0e0e0 style D fill:#2e2e2e,stroke:#fbbf24,color:#e0e0e0 style E fill:#2e2e2e,stroke:#fbbf24,color:#e0e0e0 style F fill:#2e2e2e,stroke:#fbbf24,color:#e0e0e0 style G fill:#2e2e2e,stroke:#4ade80,color:#e0e0e02.2 VM 目录结构 (vm_create.sh)
vm/├── AVPBooter.vresearch1.bin ← 从 Virtualization.framework 资源复制├── AVPSEPBooter.vresearch1.bin ← 同上├── Disk.img ← 64 GB 稀疏镜像 (dd seek, APFS COW)├── SEPStorage ← 512 KB 全零, 模拟 SEP 安全存储├── machineIdentifier ← 首次启动时创建, 持久化 ECID├── nvram.bin ← 每次启动覆盖└── .vphoned.signed ← Guest daemon 签名二进制2.3 IPSW 合并策略 (fw_prepare.sh)
iPhone IPSW cloudOS IPSW┌────────────────┐ ┌────────────────┐│ iOS 系统镜像 │ │ 引导链 ││ Cryptex DMGs │ │ LLB/iBSS/iBEC ││ Trust Caches │ ◄─── 保留 ───── │ SPTM/TXM/SEP ││ BuildManifest │ (备份为 -iPhone) │ 内核缓存 ││ │ │ 设备树/GPU/ANE │└───────┬────────┘ └───────┬────────┘ │ │ └──────────────┬────────────────────┘ ▼ 合并后的 Restore 目录 (cloudOS 引导链覆盖 iPhone 对应位置) 使用 cp -cR (APFS clone, 零拷贝)2.4 Ramdisk 构建 (ramdisk_build.py)
8 步签名流水线,处理 iBSS → iBEC → SPTM → DeviceTree → SEP → TXM → Kernel → Ramdisk+Trustcache:
SHSH Blob ─→ 提取 IM4M (签名清单) │ ├─→ iBSS: 已补丁 IM4P → 用 IM4M 签名为 IMG4 ├─→ iBEC: 替换 boot-args (rd=md0) → 签名 ├─→ SPTM: 仅签名 ├─→ DeviceTree: 重标记为 rdtr → 签名 ├─→ SEP: 重标记为 rsep → 签名 ├─→ TXM release: 重新补丁(restore用) → lzfse 压缩 → 保留 PAYP → 签名 ├─→ Kernel: 重标记为 rkrn → 保留 PAYP → 签名 └─→ Ramdisk: 原始 cloudOS ramdisk DMG ↓ 扩展为 254 MB APFS ↓ 注入 SSH 工具 (ssh.tar.gz) ↓ ldid 重签所有 Mach-O (signcert.p12) ↓ trustcache 生成信任缓存 ↓ 签名PAYP 保留技术:某些 IM4P 文件尾部附带 PAYP (payload properties)。通过
rfind(b"PAYP")定位,追加到新 IM4P 末尾并修正 ASN.1 长度字段。
2.5 Cryptex 恢复失败——SSH Ramdisk 方案的根本原因
固件恢复(idevicerestore)完成后,虚拟机会自动重启,但此时会在 launchd 进程中发生 panic:因为 /usr/lib/libSystem.B.dylib 缺失。该库位于 Cryptex 分区的 dyld_shared_cache 中,而 Cryptex 分区在恢复过程中未能正确写入。
这是因为混合固件的 Cryptex DMG 来自 iPhone IPSW,但恢复流程本身由 cloudOS 的引导链驱动。cloudOS 的恢复逻辑与 iPhone Cryptex 的 AEA (Authenticated Encryption Archive) 格式之间存在不兼容,导致 Cryptex 数据无法在恢复阶段被正确安装。
解决方案是引入 SSH Ramdisk 作为中间步骤:在恢复完成后、首次正常启动前,通过 SSH Ramdisk 启动虚拟机,手动执行以下操作:
恢复后问题诊断: idevicerestore 完成 → 重启 → launchd panic 原因: /usr/lib/libSystem.B.dylib 缺失 ← dyld_shared_cache 不存在 ← Cryptex 分区未正确恢复 ← cloudOS 恢复逻辑不兼容 iPhone AEA CryptexSSH Ramdisk 修复流程: 1. APFS 快照重命名 (com.apple.os.update-* → orig-fs) → 允许后续修改根文件系统 2. AEA 解密 iPhone SystemOS Cryptex → 挂载 → SCP 传输到 /mnt1/System/Cryptexes/OS 3. 复制 iPhone AppOS Cryptex → /mnt1/System/Cryptexes/App 4. 创建 dyld 符号链接: /System/Library/Caches/com.apple.dyld → ../../../System/Cryptexes/OS/... /System/DriverKit/System/Library/dyld → ../../../../System/Cryptexes/OS/...这个问题直接解释了为什么整个流水线必须包含 ramdisk_build → ramdisk_send → cfw_install 三个额外阶段,而不能像标准 iOS 恢复那样一步到位。
2.6 CFW 安装 (cfw_install.sh)
通过 SSH ramdisk 写入设备文件系统,7 个阶段(幂等设计,可安全重跑):
SSH Ramdisk 运行中 (端口 2222) │ ┌──────────────────────────────┼──────────────────────────────┐ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1/7 Cryptex 安装 │ │ │ │ 解析 iPhone BuildManifest → AEA 解密 SystemOS │ │ │ │ → 复制 SystemOS + AppOS 到 /mnt1/System/Cryptexes │ │ │ │ → 创建 dyld 符号链接 │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 2/7 seputil 补丁 │ │ │ │ "/%s.gl" → "/AA.gl" (Gigalocker UUID 固定化) │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 3/7 GPU 驱动安装 │ │ │ │ AppleParavirtGPUMetalIOGPUFamily.bundle │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 4/7 iosbinpack64 安装 (越狱工具包) │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 5/7 launchd_cache_loader 补丁 │ │ │ │ NOP 条件分支 → 允许加载修改后的 launchd.plist │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 6/7 mobileactivationd 补丁 │ │ │ │ should_hactivate → 返回 YES (绕过激活锁) │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 7/7 LaunchDaemons 安装 │ │ │ │ 编译 vphoned → 安装到 /usr/bin/ │ │ │ │ 注入 bash/dropbear/trollvnc/vphoned plist │ │ │ │ 补丁 launchd.plist │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ APFS 快照管理: com.apple.os.update-* → orig-fs │ │ 所有补丁从 .bak 备份重新应用 (幂等) │ └─────────────────────────────────────────────────────────────┘3. 混合身份签名架构
3.1 问题
DFU 硬件报告为 vresearch101ap (BDID=0x90, ChipID=0xFE01),但需要运行 iPhone iOS 用户空间。Apple 的签名验证要求 BuildManifest 中的身份与硬件完全匹配。
3.2 三源嫁接方案
fw_manifest.py 生成的 BuildManifest 从三个不同身份中精选组件:
┌─────────────────────────────────────────────────────────────────┐│ 混合 Build Identity (单一 DFU Erase Install) ││ ││ ┌──── vresearch101ap RELEASE ────┐ ┌── vresearch101ap RESEARCH ──┐│ │ LLB (匹配 DFU 硬件) │ │ iBoot/TXM (宽松限制) ││ │ iBSS/iBEC (DFU 签名) │ │ KernelCache (research内核) ││ │ SPTM (安全监视器) │ └────────────────────────────┘│ │ RestoreRamDisk (cloudOS 恢复) ││ └────────────────────────────────┘│ ││ ┌──── vphone600ap RELEASE ────────┐ ┌──── iPhone ERASE ──────────┐│ │ DeviceTree (MKB dt=1, │ │ OS (iOS 系统镜像) ││ │ 无 keybag 启动) │ │ SystemVolume ││ │ SEP Firmware (匹配设备树) │ │ StaticTrustCache ││ └────────────────────────────────┘ │ SystemVolumeCanonicalMetadata││ └────────────────────────────┘└─────────────────────────────────────────────────────────────────┘3.3 Restore.plist 设备映射
DeviceMap 同时包含 iPhone 和 cloudOS 的设备条目,SupportedProductTypeIDs 合并两个来源的 DFU/Recovery ID,确保 idevicerestore 能匹配到正确的恢复身份。
选择逻辑:Info.Variant 部分匹配 "Erase Install (IPSW)" 同时排除 "Research"。
4. 动态二进制补丁系统
4.1 核心设计哲学
零硬编码偏移 —— 所有补丁通过动态分析定位,确保跨固件版本兼容:
graph TD A["字符串锚定<br/>在数据段找特征字符串"] --> B["ADRP 索引交叉引用<br/>O(1) 查找引用该字符串的代码"] C["BL 频率分析<br/>按调用次数识别关键函数"] --> D["补丁点定位"] E["唯一常量模式匹配<br/>Keystone 汇编 → 二进制搜索"] --> D F["结构化数据遍历<br/>PRELINK_INFO plist / mac_policy_conf"] --> D B --> D D --> G["应用补丁<br/>NOP / mov x0,#0 / 无条件跳转"] style A fill:#1a1a1a,stroke:#60a5fa,color:#e0e0e0 style B fill:#1a1a1a,stroke:#60a5fa,color:#e0e0e0 style C fill:#1a1a1a,stroke:#fbbf24,color:#e0e0e0 style D fill:#1a1a1a,stroke:#4ade80,color:#e0e0e0 style E fill:#1a1a1a,stroke:#fbbf24,color:#e0e0e0 style F fill:#1a1a1a,stroke:#f87171,color:#e0e0e0 style G fill:#1a1a1a,stroke:#4ade80,color:#e0e0e04.2 KernelPatcher 预处理架构
patchers/kernel.py 在应用任何补丁前构建三个全局索引,使后续查找达到 O(1) 或 O(n) 线性复杂度:
┌───────────────────────────────────────────────────────────────┐│ 预处理阶段 (一次性) ││ ││ 1. Mach-O 段解析 ││ 解析 __TEXT, __TEXT_EXEC, __PRELINK_TEXT 等段 ││ 确定 BASE_VA (0xFFFFFE0007004000) 和代码范围 ││ ││ 2. Kext 范围发现 ││ 解析 __PRELINK_INFO (XML plist) ││ 提取 APFS / Sandbox / AMFI 三个 kext 的 ││ _PrelinkExecutableLoadAddr → 嵌入 Mach-O → __text 范围 ││ ││ 3. ADRP 页索引 [page_addr → [(file_offset, dest_reg)]] ││ 遍历所有可执行段的每条 ADRP 指令 ││ 解码目标页地址 → 建立倒排索引 ││ 效果: 给定字符串 VA → O(1) 找到所有 ADRP 引用 ││ ││ 4. BL 调用索引 [target_offset → [caller_offsets]] ││ 遍历所有 BL 指令 → 解码跳转目标 ││ 建立 callee → callers 的倒排索引 ││ 效果: O(1) 查询任意函数的调用者数量 ││ ││ 5. _panic 发现 ││ 条件: callers > 2000 且前方 ADRP+ADD 指向 "@%s:%d" ││ 用于后续定位 panic 相关分支 │└───────────────────────────────────────────────────────────────┘4.3 引导链补丁总览 (fw_patch.py, 6 组件 41+ 处)
┌──────────┬────────┬─────────────────────────────────────────────────────┐│ 组件 │ 补丁数 │ 关键技术 │├──────────┼────────┼─────────────────────────────────────────────────────┤│ AVPBooter │ 1 │ 0x4447 ("DG") 常量锚定 → 返回值改 mov x0,#0 ││ iBSS │ 2 │ 串口 banner 替换 + Image4 回调 b.ne NOP ││ iBEC │ 3 │ 串口 + Image4 绕过 + boot-args ADRP 重编码 ││ LLB │ 6 │ 串口 + Image4 + boot-args + rootfs(3处) + panic ││ TXM │ 1 │ 0x2446 常量锚定 → 二叉搜索 BL → mov x0,#0 ││ Kernel │ 26 │ (见下方详细分类) │└──────────┴────────┴─────────────────────────────────────────────────────┘4.4 内核 26 处补丁分类
┌──────────────────────────────────────────────────────────────────────┐│ 内核补丁 (26 处) ││ ││ ┌─ APFS 文件系统 (6 处) ─────────────────────────────────────────┐ ││ │ #1 根快照检查 NOP (tbnz w8,#5 → NOP) │ ││ │ #2 密封完整性 panic 跳过 (BL _panic 前条件分支 NOP) │ ││ │ #3 rootvp 认证 panic 跳过 │ ││ │ #12 apfs_graft 根哈希验证 → mov w0,#0 │ ││ │ #13-14 rw 挂载 + 升级检查 (cmp x0,x0 / mov w0,#0) │ ││ │ #15 fsioc_graft Image4 验证 → mov w0,#0 │ ││ │ #16 handle_get_dev_by_role 权限门控绕过 (NOP cbz → allow) │ ││ │ ("com.apple.apfs.get-dev-by-role" entitlement gate) │ ││ │ 保障 /private/preboot / /private/xarts 挂载成功 │ ││ └────────────────────────────────────────────────────────────────┘ ││ ││ ┌─ 代码签名/启动约束 (4 处) ────────────────────────────────────┐ ││ │ #4-5 proc_check_launch_constraints → mov w0,#0; ret │ ││ │ #8 TXM CodeSignature 后验证 tbnz NOP │ ││ │ #9 AMFI postValidation → cmp w0,w0 (始终通过) │ ││ └────────────────────────────────────────────────────────────────┘ ││ ││ ┌─ 安全策略 (4 处) ─────────────────────────────────────────────┐ ││ │ #6-7 PE_i_can_has_debugger → mov x0,#1; ret (启用调试器) │ ││ │ #10-11 dyld_policy_internal → mov w0,#1 (允许任意 dyld) │ ││ └────────────────────────────────────────────────────────────────┘ ││ ││ ┌─ Sandbox MACF 钩子 (10 处, 5 个 hook × 2 条指令) ────────────┐ ││ │ #17-26 通过 mac_policy_conf 结构发现: │ ││ │ "Sandbox" + "Seatbelt sandbox policy" 字符串 │ ││ │ → __DATA_CONST chained fixup pointer │ ││ │ → ops 表 → 函数指针 → mov x0,#0; ret │ ││ │ │ ││ │ 禁用的 hook: │ ││ │ vnode_check_open / vnode_check_rename │ ││ │ mount_check_mount / mount_check_remount │ ││ │ vnode_check_access │ ││ └────────────────────────────────────────────────────────────────┘ │└──────────────────────────────────────────────────────────────────────┘4.5 IBootPatcher 通用模式 (iBSS/iBEC/LLB 共享)
iBSS iBEC LLB串口 banner 替换 ✓ ✓ ✓Image4 回调绕过 ✓ ✓ ✓自定义 boot-args ✓ ✓rootfs 绕过 (6处) ✓Panic 绕过 ✓Image4 回调绕过的通用模式:
搜索模式: b.ne <target>; mov x0, x22 (且前方有 cmp 指令)补丁: NOP b.ne + 置返回值为 04.6 CFW 用户空间补丁 (patchers/cfw.py)
┌─────────────────────────────────────────────────────────────────┐│ 7 个 CLI 子命令 ││ ││ cryptex-paths 解析 BuildManifest → Cryptex DMG 路径 ││ patch-seputil "/%s.gl" → "/AA.gl" (Gigalocker 固定化) ││ patch-launchd-cache 字符串锚 → ADRP xref → NOP 条件分支 ││ patch-mobileactivationd 符号表/ObjC 元数据 → mov x0,#1; ret ││ patch-launchd-jetsam 字符串锚 → 条件分支 → 无条件跳转 ││ inject-daemons plistlib 注入 LaunchDaemon 条目 ││ inject-dylib Mach-O LC_LOAD_DYLIB 注入 (FAT 感知) │└─────────────────────────────────────────────────────────────────┘mobileactivationd 补丁的双重回退策略: 策略 1: LC_SYMTAB 符号查找 ───────────────────────── 搜索 "should_hactivate" → 符号 VA → 文件偏移 策略 2: ObjC 元数据链 (符号表不可用时) ─────────────────────────────────────"should_hactivate\0" 选择器字符串 → __objc_selrefs 指针 → __objc_const 相对方法列表 → 计算 IMP 偏移5. Metal GPU 加速——核心技术突破
5.1 问题发现
固件恢复 + Cryptex 安装完成后的首次启动中,虚拟 iPhone 停留在黑色设置屏幕,无法继续(respring 循环)。通过 MetalTest 程序验证,MTLCreateSystemDefaultDevice() 返回 null——Metal 完全不可用。
然而 ioreg -l 显示内核已正确识别 AppleParavirtGPU 设备。问题出在用户空间缺少对应的 Metal 驱动库。
5.2 根因分析:Metal 驱动加载链
iOS 的 Metal 实现依赖 /System/Library/Extensions 中的设备特定驱动 bundle。以实体设备为例:
实体 iPhone (A10 芯片): MTLCreateSystemDefaultDevice() → IOKit 匹配 IOGPU 驱动 → 加载 /System/Library/Extensions/AGXMetalA10.bundle → 返回 MTLDevice 实例PCC 虚拟机 (macOS): MTLCreateSystemDefaultDevice() → IOKit 匹配 AppleParavirtGPU → 加载 /System/Library/Extensions/AppleParavirtGPUMetalIOGPUFamily.bundle → 返回 <AppleParavirtDevice> 实例虚拟 iPhone (问题状态): MTLCreateSystemDefaultDevice() → IOKit 匹配 AppleParavirtGPU ✓ (内核已识别) → /System/Library/Extensions/ 中无对应 bundle ✗ → 返回 nulliPhone IPSW 的 /System/Library/Extensions 中只包含物理 GPU 对应的驱动(如 AGXMetal 系列),不含虚拟化 GPU 驱动。而 cloudOS (PCC) 的 /System/Library/Extensions 中恰好包含 AppleParavirtGPUMetalIOGPUFamily.bundle。
5.3 解决方案:驱动移植 + dylib 逆向实现
┌──────────────────────────────────────────────────────────────────────┐│ Metal GPU 驱动移植方案 ││ ││ Step 1: 从 PCC 虚拟机提取驱动 bundle ││ /System/Library/Extensions/AppleParavirtGPUMetalIOGPUFamily.bundle││ (7 个文件) ││ ││ Step 2: 通过 SSH Ramdisk 注入到虚拟 iPhone ││ → /mnt1/System/Library/Extensions/ (cfw_install 阶段 3/7) ││ ││ Step 3: 逆向实现缺失的 dylib ││ 问题: bundle 内的 libAppleParavirtCompilerPluginIOGPUFamily.dylib ││ 引用了 PCC 虚拟机 dsc (dyld_shared_cache) 中的符号 ││ 但该 dylib 不存在于 iPhone 16 (iOS 26) 的 dsc 中 ││ ││ 方案: 从 PCC 的 dsc 中逆向分析该 dylib 的接口与实现 ││ → 独立重新实现 → 编译为 arm64 dylib ││ → 注入虚拟 iPhone ││ ││ 验证: MTLCreateSystemDefaultDevice() 返回 ││ <AppleParavirtDevice: 0x...> ││ name = Apple Paravirtual device │└──────────────────────────────────────────────────────────────────────┘这是整个项目最关键的技术突破之一:没有 Metal 加速,虚拟 iPhone 无法渲染 UI、无法通过 Setup 屏幕、SpringBoard 无法正常运行。Metal 支持是从"能启动"到"能使用"的分水岭。
5.4 SEP 配置的前置要求
在首次恢复阶段,如果 SEP (Secure Enclave Processor) 配置不当(SEPStorage 文件缺失或格式错误),会在恢复过程中触发 panic。正确配置需要:
SEPStorage:512 KB 全零文件,模拟 SEP 安全存储AVPSEPBooter.vresearch1.bin:从 Virtualization.framework 资源复制_VZSEPCoprocessorConfiguration:通过私有 API 正确初始化,绑定 storageURL + romBinaryURL
6. 越狱机制深度剖析
6.0 核心概念:虚拟化层越狱 vs 传统越狱
vphone 的越狱机制与传统物理设备越狱本质不同:
┌─────────────────────────────────────────────────────────────────┐│ 传统越狱 vs vphone 越狱对比 ││ ││ 传统越狱 (unc0ver / Taurine / checkra1n) ││ ────────────────────────────────────────── ││ • 依赖系统漏洞 (内核 exploit / bootrom bug) ││ • 版本绑定 (仅特定 iOS 版本可越狱) ││ • 运行时注入 (系统启动后执行漏洞利用) ││ • 签名冲突 (必须绕过已运行的 AMFI / Sandbox) ││ • 工具发布时滞 (等待研究者发现新漏洞) ││ ││ vphone 虚拟化层越狱 ││ ────────────────────────────────────── ││ • 无需系统漏洞 (固件级二进制补丁) ││ • 版本无关 (理论支持任意 iOS 版本) ││ • 刷写前修改 (DFU 恢复前完成所有补丁) ││ • 深度绕过 (修改 BootROM → TXM → Kernel 全链路) ││ • 自带工具链 (补丁工具随项目分发) │└─────────────────────────────────────────────────────────────────┘关键优势:由于在虚拟化环境中拥有对固件的完全控制权,可在 IPSW 刷写前直接修改引导链和内核二进制,绕过所有安全验证。这种"上帝视角"的修改方式是物理设备越狱永远无法企及的。
6.1 越狱补丁全景:分层架构
┌──────────────────────────────────────────────────────────────────┐│ 越狱补丁金字塔 (fw_patch_jb + cfw_install_jb) ││ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ 首次启动初始化 (vphone_jb_setup.sh) 9 步骤 │ ││ │ ──────────────────────────────────────────── │ ││ │ • /var/jb 符号链接 (launchdhook 运行时创建) │ ││ │ • prep_bootstrap.sh → procursus 完整激活 │ ││ │ • Sileo / apt / libkrw0-tfp0 / TrollStore 安装 │ ││ └─────────────────────────────────────────────────────────┘ ││ ▲ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ 用户空间 CFW JB (cfw_install_jb.sh) 5 阶段 │ ││ │ ──────────────────────────────────────────── │ ││ │ • JB-1: launchd jetsam 绕过 + /b 注入 │ ││ │ • JB-2: iosbinpack64 安装 │ ││ │ • JB-3: debugserver entitlements 补丁 (新增) │ ││ │ • JB-4: procursus bootstrap + BaseBin hooks 部署 │ ││ │ • JB-5: 首次启动 LaunchDaemon 部署 │ ││ └─────────────────────────────────────────────────────────┘ ││ ▲ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ 内核层 (KernelCache JB) 24 活跃方法 │ ││ │ ──────────────────────────────────────────── │ ││ │ • task_for_pid / 任务转换 (调试器附加) │ ││ │ • AMFI 信任缓存 / execve 绕过 (代码签名) │ ││ │ • Sandbox MACF hooks 禁用 (沙箱突破) │ ││ │ • IOUC MACF gate 绕过 (IOKit 访问) │ ││ │ • vm_fault / vm_protect (RWX 内存) │ ││ │ • syscall mask / cred_label hooks (特权提升) │ ││ │ • NVRAM 权限 / 共享区域映射 (系统修改) │ ││ └─────────────────────────────────────────────────────────┘ ││ ▲ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ TXM 层 (Trusted Execution Monitor) 6 逻辑方法 │ ││ │ ──────────────────────────────────────────── │ ││ │ • trustcache 哈希绕过 (base) │ ││ │ • selector24 强制 PASS (mov w0,#0xa1 + b epilogue) │ ││ │ • get-task-allow 强制启用 (selector 41) │ ││ │ • 调试器权限强制启用 (selector 42|29) │ ││ │ • 开发者模式强制启用 (init 阶段) │ ││ └─────────────────────────────────────────────────────────┘ ││ ▲ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ iBoot 层 (iBSS) 1 补丁 │ ││ │ ──────────────────────────────────────────── │ ││ │ • APNonce 生成绕过 (允许任意 SHSH blob) │ ││ └─────────────────────────────────────────────────────────┘ │└──────────────────────────────────────────────────────────────────┘总计: 1 (iBSS) + 6 (TXM) + 24 (Kernel) + 3 (CFW) = 34 逻辑补丁组 对应 84+ 条指令级修改 (含 shellcode 多指令块) 注: CLAUDE.md 中 "126 patches" 为指令级总计 (含 dev 层叠加), 与此处"逻辑方法数"口径不同6.2 TXM 越狱补丁:突破信任执行监视器
TXM (Trusted Execution Monitor) 是 iOS 26+ 引入的安全层,运行在 SPTM (Secure Page Table Monitor) 监管下的 GL1 (Guest Level 1) 特权级。所有代码签名验证、调试器权限检查都通过 TXM 选择器 (selector) 调度。
6.2.1 TXM 执行模型与返回路径约束
┌──────────────────────────────────────────────────────────────┐│ TXM 执行上下文 ││ ││ SPTM (GL2) ◄─────── svc #0 ─────────┐ ││ │ │ ││ │ selector dispatch │ ││ ▼ │ ││ TXM (GL1) │ ││ │ │ ││ │ 执行验证逻辑 │ ││ ▼ │ ││ 构造返回码 x16 ─────────────────────┘ ││ (例: 0x000000FD00000000) ││ ││ CRITICAL: TXM 函数不能通过 ret/retab 返回! ││ ───────────────────────────────────── ││ • RETAB → LR 指向 SPTM 地址空间 ││ • TXM 无权执行 SPTM 代码 (权限故障) ││ • 必须通过 svc #0 陷入 SPTM 完成返回 ││ ││ 返回链: func → bl <helper> → bl <stub> → b <trampoline> ││ trampoline: pacibsp; svc #0; retab │└──────────────────────────────────────────────────────────────┘关键限制:这种架构要求补丁不能改变控制流返回路径。早期尝试用 mov x0,#0; retab 替换函数入口全部失败(权限故障),最终采用手术刀式原地修改,保留完整的 svc #0 返回链。
6.2.2 六大 TXM 补丁详解
TXM 补丁分为两层:base 层(patchers/txm.py)在所有模式下运行,dev/JB 层(patchers/txm_dev.py)通过 patch_txm_dev() 叠加。
Layer 1: txm.py — trustcache 哈希绕过 (base, 所有模式共享)───────────────────────────────────────────────────────────────── 定位: mov w19, #0x2446 唯一常量 → 扫描函数体 mov w2, #0x14; bl <hash_cmp>; cbz w0; tbnz w0, #0x1f 补丁: bl <hash_cmp> → mov x0, #0 效果: cbz w0 始终跳转 match 分支 → 信任缓存查找永远成功Layer 2: txm_dev.py — 5 个追加补丁 (selector24/41/42 + devmode)───────────────────────────────────────────────────────────────── T-2 selector24 强制 PASS: 定位: 函数内唯一 "mov w0, #0xa1" (CS 失败错误码) 向前验证: LDR X1,[Xn,#0x38] / ADD X2 / BL / LDP 序列 向后定位: epilogue (ldp x29,x30 + retab) 补丁: body_start+0: mov w0, #0xa1 body_start+4: b <epilogue> 效果: 短路全部哈希/版本验证, 直接以 PASS 清栈返回 T-3 get-task-allow 强制 true (selector 41): 定位: "get-task-allow" 字符串 ADRP xref → BL + TBNZ w0,#0 补丁: BL → mov x0, #1 T-4 selector42|29 shellcode hook (5 个补丁): 定位: "com.apple.private.cs.debugger" xref → debugger gate 函数 BTI J; mov x0,x20; BL; mov x1,x21; mov x2,x22; BL fn; B stub 补丁: B → cave (NOP + mov x0,#1 + strb w0,[x20,#0x30] + mov x0,x20 + B back) 效果: 预设 manifest[0x30]=1, 防 sel42 先于 sel41 运行时 assert T-5 debugger entitlement 强制 true (selector 42): 定位: "com.apple.private.cs.debugger" xref BL + TBNZ w0,#0 + 前置 mov x2,#0 / mov x0,#0 补丁: BL → mov w0, #1 T-6 developer mode 强制启用 (init 阶段): 定位: "developer mode enabled due to system policy configuration" 向后扫描 TBNZ/CBNZ w9,#0 补丁: → NOP补丁依赖链:
T-6 (devmode ON) └─→ T-3 (sel41 需要 devmode 开启) └─→ T-4 (sel42 需要 manifest[0x30]=1) └─→ T-5 (sel42 debugger check)T-1 (base trustcache, 独立) T-2 (sel24 独立, CS 代码签名绕过)6.2.3 selector24 重新设计说明
当前方案(mov w0,#0xa1; b epilogue)相比早期 NOP 方案更为直接彻底:
Early (NOP-based, 已废弃): ldr x1,[x20,#0x38] → NOP ← 移除 hash_data_ptr 加载 bl hash_flags_extract → NOP ← 移除 hash_flags 初始化 ... 继续执行后续逻辑 ...if (hash_data_ptr != 0) == ((hash_flags & 2) >> 1):return 0x130A1 ← hash_flags=0 时某些 CS blob 仍可能命中Current (force-PASS, 彻底短路): [prologue: pacibsp; stp; ...; add x29,sp,#imm] mov w0, #0xa1 ← 注入: PASS 返回码 b <epilogue> ← 注入: 跳过全部验证逻辑 [原始函数体: 完全跳过] [epilogue: ldp x29,x30; retab (→ svc#0)]UUID Canary 验证:修改 TXM LC_UUID 末字节(如 655D → 655C),启动日志中出现匹配值则证明补丁后的 TXM 被正确加载。
6.3 内核越狱补丁:25 个 Mixin 的模块化设计
6.3.1 补丁架构总览
classKernelJBPatcher(# ── Group A: 核心门禁绕过 (4 活跃) ─────────────────────────── KernelJBPatchKcall10Mixin, # JB-05: 内核函数调用接口 (kcall10) KernelJBPatchIoucmacfMixin, # JB-10: IOUC MACF gate 绕过 (新增) KernelJBPatchHookCredLabelMixin, # JB-04: cred_label hook 注入 (shellcode) KernelJBPatchSyscallmaskMixin, # JB-07: syscall mask 绕过 (shellcode) KernelJBPatchCredLabelMixin, # JB-03: cred_label 更新绕过# ── Group B: 字符串/模式锚定 (16 活跃) ─────────────────────── KernelJBPatchThidCrashMixin, # JB-23: thid_should_crash=0 KernelJBPatchSecureRootMixin, # JB-16: io_secure_bsd_root 绕过 KernelJBPatchNvramMixin, # JB-19: NVRAM 权限绕过 KernelJBPatchSharedRegionMixin, # JB-20: 共享区域映射 KernelJBPatchLoadDylinkerMixin, # JB-17: dyld 加载策略 KernelJBPatchTaskForPidMixin, # JB-22: task_for_pid 允许 KernelJBPatchSpawnPersonaMixin, # JB-21: persona 验证绕过 KernelJBPatchBsdInitAuthMixin, # JB-14: _bsd_init rootauth gate 绕过 KernelJBPatchDounmountMixin, # JB-15: unmount 检查绕过 KernelJBPatchMacMountMixin, # JB-18: mount MACF 绕过 KernelJBPatchVmProtectMixin, # JB-25: RWX 内存允许 KernelJBPatchVmFaultMixin, # JB-24: cs_bypass gate NOP KernelJBPatchPortToMapMixin, # JB-13: 端口到 map 转换 KernelJBPatchProcPidinfoMixin, # JB-12: 进程信息查询绕过 KernelJBPatchProcSecurityMixin, # JB-11: 进程安全策略 KernelJBPatchPostValidationMixin, # JB-06: 后验证绕过# ── Group A (cont): 复合绕过 ────────────────────────────────── KernelJBPatchSandboxExtendedMixin, # JB-09: Sandbox MACF hooks 批量禁用 KernelJBPatchTaskConversionMixin, # JB-08: 任务转换检查绕过 KernelJBPatchAmfiExecveMixin, # JB-02: amfi_execve (Mixin 保留, 方法从调度中排除) KernelJBPatchAmfiTrustcacheMixin, # JB-01: AMFI 信任缓存绕过 KernelJBPatcherBase):""" 25 个 Mixin, 24 个活跃方法. JB-02 (patch_amfi_execve_kill_path) 被从 _GROUP_A_METHODS 调度计划中注释排除, 但 KernelJBPatchAmfiExecveMixin 仍保留在 MRO 中供单独调用. JB-03 (patch_cred_label_update_execve) 代码注释标注 "disabled/pending revalidation", 但仍在 _GROUP_C_METHODS 调度列表中, 实际会被执行. 每个 Mixin 可包含: • 1–40 条指令级补丁 (如 syscallmask 含 40 条 shellcode) • 单条 NOP / MOV / 分支重定向 • 多个代码洞穴 (code cave) 中的 shellcode 块 """执行调度(按组顺序):
Group A (核心门禁): amfi_trustcache → task_conversion → sandbox_extended → iouc_macfGroup B (模式锚定): post_validation → proc_security → proc_pidinfo → port_to_map bsd_init_auth → dounmount → secure_root → load_dylinker mac_mount → nvram → shared_region → spawn_persona task_for_pid → thid_crash → vm_fault → vm_protectGroup C (shellcode): cred_label_update → hook_cred_label → kcall10 → syscallmask6.3.2 代表性补丁深度分析
A. task_for_pid 绕过 (调试器附加核心)
# KernelJBPatchTaskForPidMixin.patch_task_for_pid()# 定位策略: 0 BL callers + 2x ldadda + movk #0xc8a2 + ldr/str w,[x,#0x490]# 原始逻辑 (伪代码):deftask_for_pid(task, flavor, who, target):# ... 鉴权检查 ...if task.flags & HARDENED: # ldr w,[task,#0x490]return EPERM # 强化进程禁止访问# ... 返回目标任务 ...# 补丁: NOP ldr w,[task,#0x490] 指令# → flags 寄存器保持 0 → HARDENED 检查恒为假 → 允许访问所有进程效果:LLDB 可附加到任意进程(包括系统守护进程),无需 get-task-allow entitlement。
B. AMFI 信任缓存绕过 (任意代码执行)
# KernelJBPatchAmfiTrustcacheMixin.patch_amfi_cdhash_in_trustcache()# 定位策略: "loadable trust cache" 字符串锚定 → ADRP xref → 函数分析# 原始逻辑:defamfi_cdhash_in_trustcache(cdhash, cdhash_len, flags):for tc in loaded_trustcaches:if cdhash in tc.hash_list:returnTruereturnFalse# 未在信任缓存中 → 拒绝执行# 补丁: 函数入口替换为 mov x0, #1; ret# → 所有二进制文件被视为"已信任" → 绕过代码签名验证效果:可执行未签名的二进制文件(如自编译工具、逆向调试器)。
C. Sandbox MACF Hooks 批量禁用 (沙箱突破)
# KernelJBPatchSandboxExtendedMixin.patch_sandbox_hooks_extended()# 定位策略: "Sandbox" + "Seatbelt" 字符串 → mac_policy_conf 结构# → ops 表 → 10 个 hook 函数指针 → 全部替换为 mov x0,#0; rethooks = ['vnode_check_open', # 文件打开检查'vnode_check_rename', # 文件重命名检查'mount_check_mount', # 挂载点检查'mount_check_remount', # 重新挂载检查'vnode_check_access', # 文件访问检查# ... 共 10 个]# 补丁: 每个 hook 函数 → mov x0, #0; ret (2 条指令, 共 20 处修改)效果:进程突破沙箱限制,可读写 /var、/private 等受保护目录,访问相机/麦克风等硬件。
D. IOUC MACF gate 绕过 (IOKit 访问) — 新增
# KernelJBPatchIoucmacfMixin.patch_iouc_failed_macf()# 定位策略: "IOUC %s failed MACF in process %s" 字符串 → ADRP xref# → 包含该字符串引用的函数内找 BL <macf_aggregator> + CBZ W0# 验证 macf_aggregator: ldr x10,[x10,#0x9e8] + blraa/blr x10 (slot load)# 原始逻辑:defIOUserClient_open(client, task): result = mac_iokit_check_open(...)if result == 0: # CBZ W0 → allow proceed()else: log("IOUC %s failed MACF in process %s")return deny# 补丁: CBZ W0 → B <allow> (无条件跳转 allow 分支)# → MACF 检查结果被忽略 → IOUserClient 始终允许打开效果:绕过 IOKit 访问控制,进程可打开任意 IOUserClient(包括受 MACF 保护的 GPU/神经引擎驱动)。
E. vm_protect RWX 内存 (代码注入支持)
# KernelJBPatchVmProtectMixin.patch_vm_map_protect()# 定位策略: "VM_PROT_COPY" 字符串 → 函数上下文 → 权限检查分支# 原始逻辑:defvm_map_protect(map, start, end, new_prot):if (new_prot & VM_PROT_WRITE) and (new_prot & VM_PROT_EXECUTE):return KERN_PROTECTION_FAILURE # W^X 策略: 禁止 RWX 内存# ... 应用保护 ...# 补丁: NOP 权限检查分支 → 允许创建可写+可执行内存效果:支持运行时代码生成(如 JavaScript JIT、Python ctypes)。
F. _bsd_init rootauth 绕过 (根文件系统认证)
# KernelJBPatchBsdInitAuthMixin.patch_bsd_init_auth()# 定位策略: 符号 "_bsd_init" (或 "rootvp not authenticated after mounting" xref)# → 函数内 "rootvp not authenticated after mounting" panic 路径# → panic 调用前的 cbnz w0/x0 (FSIOC_KERNEL_ROOTAUTH 失败分支)# → 验证: 前方有 bl (ioctl handler) + 后方有 bl imageboot_needed# 补丁: cbnz w0, panic_path → NOP# → 即使 FSIOC_KERNEL_ROOTAUTH 返回错误也继续启动6.3.3 Shellcode 注入型补丁
部分补丁需要注入全新代码逻辑(而非简单 NOP/修改),使用代码洞穴 (code cave) 技术:
# KernelJBPatchHookCredLabelMixin.patch_hook_cred_label_update_execve()步骤 1: 定位 Sandbox mac_policy_ops 表 (通过 "Sandbox" 字符串)步骤 2: 读取 ops[16] (hook_cred_label_update_execve 函数指针)步骤 3: 在 __TEXT_EXEC 中找到零填充区域 (UDF cave, ~180 字节)步骤 4: 注入 shellcode: ┌─────────────────────────────────────────────────────┐ │ mrs x8, tpidr_el1 ; 读取当前线程指针 │ │ stp x8, x0, [sp, #0x70] ; 构造 vfs_context 结构 │ │ add x1, sp, #0x70 ; x1 = &vfs_context │ │ bl vnode_getattr_addr ; 调用 vnode_getattr │ │ ... 40+ 条指令 ... ; 完整 hook 逻辑 │ │ b original_hook_addr ; 跳回原始 hook 继续执行 │ └─────────────────────────────────────────────────────┘步骤 5: 修改 ops[16] 指向 shellcode 入口关键技术:
内联 vfs_context 构造:避免符号依赖,通过 mrs tpidr_el1直接读取线程局部存储动态函数地址解析: vnode_getattr通过 "vnode_getattr" 字符串锚定 + ADRP xref 定位Code cave 分配:扫描 __TEXT_EXEC中的全零区域(对齐至 0x40 字节边界)
6.4 用户空间越狱:Procursus + BaseBin 双层架构
6.4.1 五阶段 CFW JB 安装流程
cfw_install_jb.sh 执行流程:Phase 0: 运行基础 CFW 安装 (7 阶段, 同非 JB 版本) CFW_SKIP_HALT=1 zsh cfw_install.sh ├─ Cryptex 安装 ├─ seputil 补丁 ├─ GPU 驱动注入 ├─ iosbinpack64 (基础工具包) ├─ launchd_cache_loader 补丁 ├─ mobileactivationd 补丁 └─ LaunchDaemons 安装═══════════════════════════════════════════════════════════Phase JB-1: launchd jetsam 绕过 + /b 注入┌────────────────────────────────────────────────────────┐│ 1. 备份 /sbin/launchd → /sbin/launchd.bak ││ 2. 提取并保存原始 entitlements (ldid -e) ││ (保留 spawn 权限, 避免 "operation not permitted") ││ 3. 注入 LC_LOAD_DYLIB: /b (短路径别名) ││ 原因: launchd Mach-O 头空间有限, 无法容纳 ││ /cores/launchdhook.dylib 完整路径; 使用 /b 替代 ││ 4. 补丁 jetsam guard (cfw.py patch-launchd-jetsam): ││ 定位: "com.apple.xpc.launchd.domain" 字符串 → 函数 ││ 查找: CBZ/CBNZ 分支判断 jetsam 状态 ││ 修改: CBZ → B (无条件跳转, 跳过 panic) ││ 5. 以原始 entitlements 重签名 + SCP 上传 │└────────────────────────────────────────────────────────┘Phase JB-2: iosbinpack64 安装 + dev overlay (本地 tar)┌────────────────────────────────────────────────────────┐│ 1. SCP iosbinpack64.tar → /mnt1 ││ 2. tar --preserve-permissions -xf 解压到设备 ││ 3. apply_dev_overlay: 替换本地 iosbinpack64.tar 中的 ││ rpcserver_ios (cfw_dev/rpcserver_ios 存在时执行) ││ 注: 修改的是本地 tar 副本, 当次已解压的设备文件 ││ 不受影响, 下次运行 JB-2 时才会应用到设备 │└────────────────────────────────────────────────────────┘Phase JB-3: debugserver entitlements 补丁┌────────────────────────────────────────────────────────┐│ 1. SCP 取回 /mnt1/usr/libexec/debugserver ││ 2. 提取原始 entitlements (ldid -e) ││ 3. 移除 seatbelt-profiles (沙盒约束) ││ 4. 注入 task_for_pid-allow: YES ││ 5. 重签名 + 上传回 /mnt1/usr/libexec/debugserver │└────────────────────────────────────────────────────────┘Phase JB-4: Procursus Bootstrap 安装 + BaseBin Hooks 部署┌────────────────────────────────────────────────────────┐│ Bootstrap 安装: ││ 1. zstd -d bootstrap-iphoneos-arm64.tar.zst ││ 2. 挂载 /dev/disk1s5 → /mnt5 (Preboot 卷) ││ 3. 获取 boot_manifest_hash (96 字符 SHA-384) ││ 4. 创建 /mnt5/<hash>/jb-vphone/ 目录 ││ 5. tar --preserve-permissions -xf → jb-vphone/ ││ 6. 重组目录: mv var/ → procursus/ ││ mv procursus/jb/* → procursus/ ││ 7. 上传 Sileo 2.5.1 deb (如存在) ││ 注: /var/jb 符号链接在首次正常启动时由 ││ launchdhook.dylib 创建 (Data 卷加密, ramdisk ││ 下不可挂载, 无法在此阶段创建) ││ ││ BaseBin Hooks 部署: ││ 1. 清理并重建 /mnt1/cores/ ││ 2. ldid 重签名所有 dylib (signcert.p12) ││ 3. SCP 上传到 /mnt1/cores/ (0755) ││ dylib 清单: ││ systemhook.dylib (全局 hook, 所有进程加载) ││ launchdhook.dylib (launchd 专用 hook) ││ libellekit.dylib (hook 引擎) ││ 4. 安装 /b 短别名: ││ cp launchdhook.dylib → /mnt1/b (launchd 加载用) │└────────────────────────────────────────────────────────┘Phase JB-5: 首次启动 LaunchDaemon 部署┌────────────────────────────────────────────────────────┐│ 1. SCP vphone_jb_setup.sh → /mnt1/cores/ ││ 2. SCP com.vphone.jb-setup.plist → ││ /mnt1/System/Library/LaunchDaemons/ ││ 3. 注入 com.vphone.jb-setup 到 launchd.plist ││ (确保 launchd 在首次正常启动时自动运行) │└────────────────────────────────────────────────────────┘Bootstrap 内容物:
/mnt5/<hash>/jb-vphone/procursus/ ├── bin/ # 核心工具 (dpkg, apt, bash, etc.) ├── sbin/ # 系统工具 (ldid, substrate, etc.) ├── lib/ # 运行时库 (libapt-pkg, libsubstrate) ├── Library/ # Cydia Substrate 框架 └── var/ # dpkg 数据库6.4.2 launchd 注入机制详解
// launchd 加载流程 (iOS 26)dyld 初始化 → 解析 Mach-O 头 → 读取 LC_LOAD_DYLIB 命令 (注入的 /b, 即 launchdhook.dylib 短别名) → dlopen("/b") → launchdhook 构造函数执行 → hook launchd 内部函数 (如 _xpc_domain_init, _job_start) → 拦截所有守护进程启动 → 注入 systemhook.dylib 到子进程环境 → 创建 /var/jb → /private/preboot/<hash>/jb-vphone/procursus 符号链接 (Data 卷在 ramdisk 阶段加密不可访问, 必须在首次正常启动时创建)// jetsam guard 绕过 (必须, 否则 launchd 会 panic)// 原始逻辑: 检测到未授权 dylib 加载 → 触发 jetsam panic// 补丁后: CBZ → B, 跳过 panic 分支 → launchd 正常运行为什么使用 /b 短别名:launchd 二进制在 LC_CODE_SIGNATURE 被剥离后,Mach-O 加载命令区域空间有限(通常只剩几十字节)。/cores/launchdhook.dylib(27 字节路径)无法容纳,而 /b(2 字节)能完美适配,同时在 /mnt1/b 处放置同名文件。
为什么必须保留原始 entitlements:launchd 持有 spawn、XPC 等特权 entitlement,重签时若使用空白 entitlement 会导致各守护进程的 spawn 操作返回 "operation not permitted"。通过 ldid -e 提取后原样回填,确保重签后权限不变。
6.4.3 Procursus vs Elucubratus 技术选型
┌──────────────┬─────────────────────┬────────────────────────┐│ │ Procursus │ Elucubratus │├──────────────┼─────────────────────┼────────────────────────┤│ 维护者 │ CoolStar / Sileo │ Sam Bingner / BigBoss ││ 包格式 │ Modern deb (zst压缩)│ Legacy deb (xz/gzip) ││ 架构 │ arm64 only │ arm64 + armv7 ││ 依赖管理 │ APT 2.x │ APT 1.x ││ Hook 引擎 │ ElleKit (推荐) │ Cydia Substrate ││ 兼容性 │ iOS 14+ │ iOS 6+ ││ 更新频率 │ 高 (活跃开发) │ 低 (维护模式) ││ Sileo 支持 │ 原生 │ 需第三方移植 │└──────────────┴─────────────────────┴────────────────────────┘vphone 选择 Procursus 的原因: • 原生支持 iOS 26 (最新 APT/dpkg) • 更好的 arm64 优化 (无 armv7 历史包袱) • 活跃的社区支持 (问题快速修复) • Sileo GUI 无缝集成 (vs Cydia 需手动移植)6.4.4 BaseBin Hooks 工作原理
┌─────────────────────────────────────────────────────────────┐│ BaseBin Hook 调用链 ││ ││ 进程启动 ││ │ ││ ▼ ││ dyld 预加载 systemhook.dylib (通过环境变量) ││ │ ││ ▼ ││ systemhook 构造函数 ││ ├─→ dlopen("libellekit.dylib") (hook 引擎) ││ ├─→ 读取 /cores/hooks.plist (hook 配置) ││ └─→ ellekit_hook(target_func, replacement_func, ...) ││ ││ 目标函数调用 ││ │ ││ ▼ ││ [trampoline] → replacement_func (自定义逻辑) ││ │ ├─ 前置处理 (日志/权限修改) ││ │ ├─ call_original() (可选调用原函数) ││ │ └─ 后置处理 (返回值篡改) ││ │ ││ └─→ 原始函数 (或跳过) │└─────────────────────────────────────────────────────────────┘ElleKit vs Cydia Substrate 对比: ElleKit (现代化, vphone 使用): • PAC/BTI 兼容 (iOS 14+ 必须) • 支持 Swift/ObjC/C++ 函数 • 内联 hook (直接修改函数头) • 轻量级 (单一 dylib) Cydia Substrate (传统): • 不完全支持 PAC (iOS 14+ 问题多) • 主要支持 ObjC 消息 • MSHookFunction API (需额外 libhooker) • 重量级 (多个依赖库)6.4.5 首次启动 JB 初始化 (vphone_jb_setup.sh)
Procursus bootstrap 安装在 Preboot 卷(只读挂载,ramdisk 阶段写入),但 Data 卷(/var)在 ramdisk 阶段处于加密状态,无法访问。因此 procursus 的完整激活必须推迟到首次正常启动后由 LaunchDaemon 完成:
LaunchDaemon: com.vphone.jb-setup 程序: /cores/vphone_jb_setup.sh 运行时机: 首次正常启动 (launchd 加载后) 幂等保护: /var/mobile/.vphone_jb_setup_done 标记文件9 步初始化流程 ([0/8] ~ [8/8]):───────────────────────────────────────────────────────────── 0/8 替换 procursus launchctl procursus 的 launchctl 缺少 _launch_active_user_switch 符号 → 将原始 launchctl 保存为 launchctl.procursus → ln -sf iosbinpack64/bin/launchctl → JB/usr/bin/launchctl 和 JB/bin/launchctl (dpkg postinst/prerm 脚本兼容性所需) 1/8 创建 /var/jb 符号链接 /private/var/jb → /private/preboot/<hash>/jb-vphone/procursus 2/8 修复 mobile Library 权限 chown -R 501:501 /var/jb/var/mobile/Library (0755) 3/8 运行 prep_bootstrap.sh procursus 内置的首次激活脚本 (执行后自删除) 4/8 创建 JB 标记文件 /var/jb/.procursus_strapped /var/jb/.installed_dopamine 5/8 安装 Sileo dpkg -i org.coolstar.sileo_2.5.1_iphoneos-arm64.deb uicache -a (刷新图标) 6/8 APT 初始化 添加 Havoc.app 源 apt-get update apt-get install libkrw0-tfp0 (内核读写桥接库) apt-get upgrade 7/8 安装 TrollStore Lite apt-get install com.opa334.trollstorelite uicache -a 8/8 SSH Shell 配置 生成 /var/root/.bashrc + .bash_profile (source /var/jb/etc/profile, 加载完整 JB PATH)───────────────────────────────────────────────────────────── 完成后写入 done marker → 后续重启时跳过 (幂等)监控方式:首次启动后通过 vphoned 文件浏览器(或 SSH)查看:
tail -f /var/log/vphone_jb_setup.log6.5 越狱能力矩阵与应用场景
┌──────────────────────────┬──────────────┬────────────────────────────────┐│ 能力 │ 补丁来源 │ 典型应用场景 │├──────────────────────────┼──────────────┼────────────────────────────────┤│ 任意代码执行 │ K-AMFI │ 运行自编译工具/逆向二进制 ││ 调试器附加 │ K-task │ LLDB/Frida 附加系统进程 ││ 文件系统读写 │ K-Sandbox │ 修改系统文件/访问隐私数据 ││ IOKit 全面访问 │ K-IOUC │ GPU/ANE 驱动直接访问 ││ RWX 内存 │ K-VM │ JIT 编译器 (V8/JavaScriptCore) ││ 内核函数调用 │ K-kcall │ 直接调用内核 API ││ Root 权限持久化 │ CFW-JB │ SSH/VNC 守护进程 ││ 包管理系统 │ Procursus │ apt/dpkg 安装越狱插件 ││ 全局 Hook 注入 │ BaseBin │ Tweak 开发 (类 Cydia) ││ 绕过代码签名 (TXM 层) │ TXM-sel24 │ 加载未签名 dylib ││ 强制开发者模式 │ TXM-sel41/42 │ 绕过 DeveloperDiskImage 需求 ││ LLDB debugserver 直连 │ JB-3 │ 附加到任意进程无需 task-allow ││ TrollStore / APT │ vphone_jb │ 安装已签名 IPA / apt 包管理 │└──────────────────────────┴──────────────┴────────────────────────────────┘K-XXX: 内核补丁 (kernel_jb.py)TXM-XXX: TXM 补丁 (txm_dev.py, 在 fw_patch_jb.py 中通过 patch_txm_dev 调用)CFW-JB: SSH Ramdisk 阶段安装 (cfw_install_jb.sh)JB-N: CFW JB 安装阶段vphone_jb: 首次启动初始化 (vphone_jb_setup.sh LaunchDaemon)实战案例:
逆向工程工作流
make boot (启动越狱 VM)→ ssh root@localhost -p 22222 (alpine)→ apt install frida-server cycript class-dump→ frida -U -n SpringBoard -l hook.js→ 实时 hook SpringBoard 函数调用Tweak 开发测试
编写 Tweak.x (Logos 语法)→ 本地编译 .dylib→ scp 到 VM /Library/MobileSubstrate/DynamicLibraries/→ killall -9 SpringBoard (重启 UI)→ Tweak 自动加载 (通过 systemhook)安全研究沙箱
运行恶意样本 → 完整文件系统访问权限→ 内核级监控 (通过 kcall hook)→ 无风险 (VM 快照隔离)
6.6 越狱稳定性保障机制
1. 幂等设计 (Idempotent Patching) 所有补丁可重复应用, 检测已补丁标记后跳过:if remote_file_exists("/mnt1/sbin/launchd.bak"): skip_patch() # 已补丁, 跳过2. 原始文件备份 launchd → launchd.bak (保留原始 entitlements 用于重签) seputil → seputil.bak mobileactivationd → mobileactivationd.bak3. SSH Ramdisk 恢复能力 补丁失败 → 重启进 ramdisk → 从 .bak 恢复 rm /mnt1/sbin/launchd; cp launchd.bak launchd4. 动态补丁验证 每个补丁方法返回 True/False 失败补丁打印详细错误 → 不影响其他补丁继续执行 KernelJBPatcher 按计时分组执行, 慢于 10s 的方法打印警告5. 首次启动 done marker /var/mobile/.vphone_jb_setup_done 存在则跳过 vphone_jb_setup.sh 重置方式: 删除标记文件 → 下次重启重新执行 9 步初始化6. 日志追踪 所有 SSH 命令通过 ssh_cmd() 包装 → 自动重试 3 次 网络断开 → 自动重连机制 (3s 延迟) 首次启动 JB 初始化全程记录到 /var/log/vphone_jb_setup.log7. Swift 虚拟机运行时
7.1 应用启动流程
sequenceDiagram participant M as main.swift participant CLI as VPhoneCLI participant AD as VPhoneAppDelegate participant VM as VPhoneVirtualMachine participant HW as VPhoneHardwareModel participant CTL as VPhoneControl participant WC as VPhoneWindowController M->>CLI: parseOrExit() (同步, 在 runloop 前) M->>AD: NSApplication.delegate = AppDelegate(cli) M->>M: app.run() (进入事件循环) AD->>AD: applicationDidFinishLaunching AD->>AD: 设置 SIGINT handler (DispatchSource) AD->>AD: Task { @MainActor startVirtualMachine() } AD->>VM: VPhoneVirtualMachine(options:) VM->>HW: createModel() [PV=3, BDID=0x90, ISA=2] VM->>VM: 配置 15+ 个子系统 VM->>VM: validate() + start(forceDFU:) alt GUI 模式 (非 DFU) AD->>CTL: VPhoneControl() → connect(device:) CTL-->>AD: onConnect(caps) AD->>WC: 创建窗口 + 工具栏 AD->>AD: 根据 caps 启用位置/IPA 功能 end7.2 VM 配置架构
VPhoneVirtualMachine.init(options:) 构造器中完成全部 15+ 个子系统的配置:
VZVirtualMachineConfiguration├── 平台配置 (VZMacPlatformConfiguration)│ ├── VPhoneHardwareModel ← PV=3, BDID=0x90, ISA=2 (私有 API)│ ├── VZMacMachineIdentifier ← 持久化到磁盘 (稳定 ECID)│ └── VZMacAuxiliaryStorage ← NVRAM, boot-args 通过私有 API 设置│ └── boot-args: "serial=3 debug=0x104c04"│├── 引导加载器 (VZMacOSBootLoader)│ └── _setROMURL() ← 私有 API 设置自定义 ROM│├── CPU: max(用户指定, minimumAllowed)├── 内存: max(用户指定, minimumAllowed)│├── 图形: VZMacGraphicsDeviceConfiguration (单显示器)├── 音频: VZVirtioSoundDevice (输入流 + 输出流)│├── 存储: VZVirtioBlockDevice + DiskImageAttachment (读写)├── 网络: VZVirtioNetworkDevice + NAT│├── 串口: PL011 UART (私有 API)│ ├── stdin → Pipe → VM 串口输入 (后台线程转发)│ └── VM 串口输出 → Pipe → stdout│├── 触摸屏: _VZUSBTouchScreenConfiguration (私有 API)├── 键盘: VZUSBKeyboardConfiguration│├── Vsock: VZVirtioSocketDevice (端口 1337 → VPhoneControl)│├── GDB 调试桩 (私有 API, 系统分配端口)│└── SEP 协处理器 (私有 API) ├── SEP Storage + SEP ROM └── 独立 GDB 调试桩7.3 私有 API 访问模式
所有私有 API 通过 Dynamic 库以运行时消息分发调用,无需 ObjC 桥接:
Dynamic 库 (mhdhejazi/Dynamic) 运行时动态方法调用 │ ┌───────────────────┼───────────────────────┐ │ │ │ ▼ ▼ ▼ 类实例化 属性设置 方法调用 Dynamic._VZ...() Dynamic(obj) Dynamic(obj) ._setProp(val) .method(args) 使用场景: ┌────────────────────────────────────────────────────────┐ │ _VZMacHardwareModelDescriptor → PV=3 硬件模型 │ │ _setROMURL → 自定义 ROM │ │ _VZPL011SerialPortConfiguration → PL011 串口 │ │ _VZUSBTouchScreenConfiguration → USB 触摸屏 │ │ _setDebugStub / _VZGDBDebugStubConfiguration → GDB │ │ _VZSEPCoprocessorConfiguration → SEP 协处理器 │ │ _setForceDFU / _setStopInIBoot → 启动模式控制 │ │ _setDataValue (NVRAM) → boot-args 设置 │ └────────────────────────────────────────────────────────┘ 前置条件: macOS 15+ (Sequoia) + SIP 禁用 + AMFI 禁用 Entitlements: (entitlements & 0x12) != 0 bit 1: com.apple.private.virtualization bit 4: com.apple.private.virtualization.security-research8. Host-Guest 控制协议
8.1 协议规范
传输层: VirtIO Socket (vsock), 端口 1337帧格式: [uint32 BE 长度][UTF-8 JSON 载荷]最大消息: 4 MB消息字段:"v" : 协议版本 (当前 = 1)"t" : 消息类型 (字符串)"id" : 请求 ID (十六进制, 响应中回显)8.2 连接握手与自动更新
sequenceDiagram participant H as Host (VPhoneControl) participant G as Guest (vphoned) H->>H: 加载 .vphoned.signed, 计算 SHA-256 H->>G: vsock connect(port: 1337) H->>G: {"v":1, "t":"hello", "bin_hash":"<sha256>"} G->>G: 计算自身 SHA-256, 比对 G->>H: {"v":1, "t":"hello", "name":"vphoned", "caps":[...], "need_update":true/false} alt need_update = true H->>G: {"t":"update", "size": N} H->>G: [N 字节原始二进制数据] (非 JSON 帧, 内联传输) G->>G: 写入 /var/root/Library/Caches/vphoned G->>H: {"t":"ok"} G->>G: exit(0) → launchd 重启 → execv 新二进制 Note over H,G: 3 秒后自动重连 end H->>H: startReadLoop() + onConnect(caps)8.3 消息类型一览
┌────────────────┬───────────┬──────────────────────────────────────────┐│ 消息类型 │ 方向 │ 说明 │├────────────────┼───────────┼──────────────────────────────────────────┤│ hello │ 双向 │ 握手 (含 bin_hash, caps, need_update) ││ update │ Host→Guest│ 头帧 + 原始二进制流 (上限 10 MB) ││ hid │ Host→Guest│ HID 按键 (page/usage/down) ││ ping / pong │ 双向 │ 心跳探活 ││ version │ 请求/响应 │ Guest 版本查询 ││ devmode │ 请求/响应 │ 开发者模式状态/启用 (XPC) ││ location │ Host→Guest│ GPS 注入 (lat/lon/alt/accuracy/speed) ││ location_stop │ Host→Guest│ 停止位置模拟 ││ file_list │ 请求/响应 │ 列目录 ││ file_get │ 请求/响应 │ 下载文件 (响应含内联二进制) ││ file_put │ Host→Guest│ 上传文件 (头帧 + 二进制流) ││ file_mkdir │ Host→Guest│ 创建目录 ││ file_delete │ Host→Guest│ 删除文件/目录 ││ file_rename │ Host→Guest│ 重命名 ││ ipa_install │ 请求/响应 │ IPA/TIPA 安装 (path/cert_path/registration → vphoned_install MCMAppContainer) ││ ok / err │ Guest→Host│ 通用成功/错误响应 │└────────────────┴───────────┴──────────────────────────────────────────┘8.4 请求-响应模型
Host 端 (VPhoneControl.swift)sendRequest(_:) │ 分配唯一 hex id │ withCheckedThrowingContinuation ▼pendingRequests[id] = continuation ← NSLock 保护 │ │ 写入 [uint32 长度][JSON{v,t,id,...}] ▼startReadLoop (后台线程) │ 循环读取帧 → JSON 解析 │ 取出 id → 查找 pendingRequests[id] │ resume(returning: response) ▼调用者 await 获得响应断连处理: 读循环结束 → disconnect() → fail 所有 pending requests (VPhoneError.disconnected) → 通知 onDisconnect → 如果之前已连接 → 3 秒后自动重连9. Guest Daemon 架构
9.1 vphoned 总览
Objective-C 编写的 LaunchDaemon,运行在 iOS 虚拟机内部:
vphoned (Objective-C)├── vphoned.m 主循环: vsock 监听, 消息分发, 自动更新├── vphoned_protocol.{h,m} 帧协议: 长度前缀 JSON, 4MB 限制├── vphoned_hid.{h,m} HID 按键注入 (IOHIDEvent)├── vphoned_devmode.{h,m} 开发者模式 (XPC)├── vphoned_location.{h,m} GPS 模拟 (CLLocationManager)├── vphoned_files.{h,m} 文件操作 (list/get/put/mkdir/delete/rename)├── vphoned_install.{h,m} IPA/TIPA 安装 (ipa_install: MCMAppContainer + LSApplicationWorkspace)├── unarchive.{h,m} libarchive 解压封装 (IPA/TIPA ZIP 解包)├── vendor/libarchive/ vendored libarchive 静态库├── vphoned.plist LaunchDaemon 配置├── entitlements.plist 权限声明└── signcert.p12 签名证书9.2 自动更新热替换
main() 启动 │ ▼检查 /var/root/Library/Caches/vphoned 是否存在 ├── 存在 → execv() 切换到缓存二进制 (热替换) └── 不存在 → 继续正常启动 │ ▼AF_VSOCK 监听端口 1337 │ ▼ accept()握手: 比对 bin_hash ├── 不匹配 → 接收新二进制 → 写入缓存 → exit(0) │ launchd 自动重启 → execv 新版本 └── 匹配 → 进入消息循环9.3 IPA 安装流水线
┌─────────────────────────────────────────────────────────────┐│ Host: VPhoneControl.installIPA(localURL:) ││ 1. 读取本地 IPA / TIPA → 内存 ││ 2. 创建远端目录 /var/mobile/Documents/vphone-installs/ ││ 3. uploadFile → <remoteDir>/<UUID>-<filename>.ipa ││ 4. 读取本地 signcert.p12 → 上传为 <UUID>-signcert.p12 ││ 5. 发送 ipa_install 命令 ││ {"t":"ipa_install", "path":..., "cert_path":..., ││ "registration":"User"} ││ ││ 错误处理: Guest 返回 "unknown type: ipa_install" ││ → 抛出错误 (提示用户更新 Guest daemon) ││ → 不降级到旧接口 │└──────────────────────────┬──────────────────────────────────┘ │ vsock ▼┌─────────────────────────────────────────────────────────────┐│ Guest: vphoned_install.m — vp_handle_custom_install() ││ ││ 前置检查: ││ vp_custom_installer_available() ││ → MCMAppContainer + LSApplicationWorkspace 均可用? ││ vp_find_ldid_path() → Guest 侧 ldid 可执行文件位置 ││ ││ Stage 1: Extract (vendored libarchive) ││ vp_extract_package_to_directory() ││ libarchive 解压 ZIP → /tmp/<UUID>/ ││ (路径遍历防护 + NULL 路径名守卫) ││ ││ Stage 2: Install (MCM + LS) ││ vp_install_app_from_package() ││ ├─ 签名: ldid -S (Guest 侧重签) ││ │ cert_path 存在时使用上传的 p12 证书 ││ ├─ MCMAppContainer: 分配 App 沙箱容器 ││ │ containerWithIdentifier:createIfNecessary: ││ ├─ 注册: LSApplicationWorkspace ││ │ registerApplicationDictionary: ││ └─ 清理: 删除临时目录 + IPA + 证书文件 ││ ││ 返回: {"t":"ok", "msg":"Installed via built-in installer ││ as User: <bundleId>"} │└─────────────────────────────────────────────────────────────┘能力检测:握手响应 caps 数组中包含 "ipa_install" 时,Host 才展示安装入口。若 Guest daemon 过旧不支持此消息类型,返回错误并提示用户重连更新 daemon。
10. GUI 与交互系统
10.1 窗口与输入架构
┌────────────────────────────────────────────────────────────┐│ NSApplication ││ ├── VPhoneAppDelegate (中央编排器) ││ │ 持有: VM, Control, WindowCtrl, MenuCtrl, ││ │ FileWindowCtrl, LocationProvider ││ │ ││ ├── VPhoneWindowController ││ │ ├── NSWindow (VM 屏幕尺寸 / 缩放因子) ││ │ │ └── 宽高比锁定, capturesSystemKeys ││ │ ├── NSToolbar [Home 按钮] ││ │ └── 状态轮询 (2s 定时器 → subtitle 更新) ││ │ ││ ├── VPhoneVirtualMachineView (继承 VZVirtualMachineView) ││ │ ├── 鼠标事件 → 触摸注入 (macOS 15: NSClassFromString ││ │ │ + KVC 手工构造触摸对象; macOS 16+: 原生支持) ││ │ ├── 坐标归一化: 鼠标位置 → [0,1] 标准化 ││ │ ├── 边缘检测 (32px 阈值): iOS 手势模拟 ││ │ │ 上滑=Home栏, 下拉=通知中心, 左/右=控制中心 ││ │ ├── 右键 → Home 键 ││ │ └── Cmd+H → Home 键 ││ │ ││ └── VPhoneMenuController ││ ├── Keys: Home / Power / Vol Up / Vol Down ││ ├── Type: 剪贴板 → 逐字符键入 ││ ├── Connect: File Browser / DevMode / Ping / Version ││ ├── Install: IPA/TIPA 安装 ││ ├── Location: 主机 GPS → Guest 同步 (toggle) ││ └── Record: 屏幕录制 (toggle) │└────────────────────────────────────────────────────────────┘10.2 macOS 版本兼容性与触屏交互差异
虚拟 iPhone 的触屏交互在不同 macOS 版本间存在显著差异,这是一个影响用户体验的关键兼容性问题:
┌────────────────────────────────────────────────────────────────────┐│ macOS 版本兼容性矩阵 ││ ││ macOS 15 (Sequoia): ││ ├── 触屏: VZVirtualMachineView 不原生支持触摸事件 ││ │ → 必须重写鼠标事件函数 (mouseDown/mouseDragged/mouseUp) ││ │ → 通过 NSClassFromString + KVC 手工构造 _VZTouch 对象 ││ │ → 鼠标坐标归一化到 [0,1] 后注入为触摸事件 ││ ├── 边缘手势: 32px 阈值检测 ││ │ 上边缘下拉 = 通知中心, 下边缘上滑 = Home 栏 ││ │ 左/右边缘 = 控制中心 ││ └── 已验证: Apple M3 16GB, Sequoia 15.7.4 ││ ││ macOS 16 (Tahoe): ││ ├── 触屏: VZVirtualMachineView 原生支持触摸事件 ││ │ → _VZMultiTouchEvent 私有 API 可直接使用 ││ │ → 代码中通过 #available(macOS 16, *) 条件编译 ││ └── 已验证: Apple M1 Pro 32GB, Tahoe 26.3 ││ ││ 兼容性要求: ││ ├── 必须: Apple Silicon Mac (ARM64) ││ ├── 必须: SIP 禁用 + AMFI 禁用 ││ ├── 必须: 支持 pccvre 的目标设备 ││ └── entitlements: com.apple.private.virtualization ││ + com.apple.private.virtualization.security-research│└────────────────────────────────────────────────────────────────────┘10.3 文件浏览器 (SwiftUI + AppKit 混合)
graph TB subgraph "AppKit 层" FWC["VPhoneFileWindowController<br/>NSWindow 700x500"] end subgraph "SwiftUI 层" FBV["VPhoneFileBrowserView<br/>Table + Toolbar + ContextMenu<br/>搜索 / 拖放 / 进度遮罩"] end subgraph "ViewModel 层" FBM["VPhoneFileBrowserModel<br/>@Observable @MainActor<br/>files / pathHistory / transfer"] end subgraph "数据模型" RF["VPhoneRemoteFile<br/>struct, Identifiable<br/>name / type / size / perm / mtime"] end subgraph "通信层" CTL["VPhoneControl<br/>vsock RPC"] end FWC -->|"NSHostingView"| FBV FBV -->|"绑定"| FBM FBM -->|"解析"| RF FBM -->|"异步调用"| CTL CTL -->|"vsock:1337"| GD["vphoned<br/>file_list / file_get / file_put"]功能特性:
Table 视图:图标 / 名称 / 权限(等宽) / 大小 / 修改时间,支持排序和多选 导航:路径历史栈 + 面包屑导航 + 前进后退 文件操作:上传 / 下载(递归) / 新建文件夹 / 删除 / 重命名 传输进度:全屏模糊遮罩 + 进度条 + 文件名 + 字节计数 拖放支持:Finder → 窗口直接上传
10.4 位置转发
Mac 主机 CLLocationManager │ requestAlwaysAuthorization() │ startUpdatingLocation() ▼LocationDelegateProxy (避免 @MainActor 隔离冲突) │ didUpdateLocations ▼VPhoneLocationProvider.forward(location:) │ 缓存 lastLocation │ 检查 control.isConnected ▼VPhoneControl.sendLocation( latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course) │ vsock {"t":"location", ...} ▼vphoned_location.m → CLLocationManager 注入11. 构建系统
11.1 Makefile 目标依赖图
graph TB subgraph "安装" SM["setup_machine<br/>全自动安装"] ST["setup_tools<br/>工具链安装"] end subgraph "构建" B["build<br/>Swift compile + codesign"] BU["bundle<br/>.app 打包"] VD["vphoned<br/>iOS arm64 交叉编译"] end subgraph "固件流水线" FP["fw_prepare"] FPA["fw_patch"] FPJ["fw_patch_jb"] end subgraph "恢复/Ramdisk" RS["restore_get_shsh"] RE["restore"] RB["ramdisk_build"] RD["ramdisk_send"] end subgraph "CFW" CI["cfw_install"] CJ["cfw_install_jb"] end subgraph "VM 管理" VN["vm_new"] BO["boot"] BD["boot_dfu"] end SM --> ST BU --> B BO --> BU BO --> VD BD --> B FP --> FPA FPA --> FPJ style SM fill:#1a1a1a,stroke:#4ade80,color:#e0e0e0 style BO fill:#1a1a1a,stroke:#60a5fa,color:#e0e0e0 style FPA fill:#1a1a1a,stroke:#fbbf24,color:#e0e0e011.2 关键变量
VM_DIR | vm | |
CPU | 8 | |
MEMORY | 4096 | |
DISK_SIZE | 64 | |
CFW_INPUT | cfw_input | |
BOOT_ARGS | --install-ipa) |
11.3 SwiftPM 依赖
swift-argument-parser | ||
Dynamic |
系统框架:Virtualization / AppKit / SwiftUI / CoreLocation / AVFoundation
Python 工具链:capstone (反汇编) / keystone-engine (汇编) / pyimg4 (IM4P 处理)
12. 技术风险与未来挑战
12.1 Apple 组件移除风险
整个项目的基石是 cloudOS (PCC) 固件中包含的 vphone600ap 组件。这些组件可能是:
Apple 有意为之:计划向安全研究者提供虚拟 iPhone 环境(类似已有的 PCC 研究 VM) 意外泄露:类似 2021 年 iOS 15 beta 中 DEVELOPMENT/KASAN 内核被包含约 4 个月的先例
如果 Apple 在未来 cloudOS 版本中移除这些组件,项目将冻结在最后一个可用固件版本上,无法跟随 iOS 新版本更新。
12.2 私有 API 稳定性
项目深度依赖 Virtualization.framework 的 10+ 个私有 API(通过 Dynamic 库运行时调用):
风险等级评估: 高风险 (API 签名或语义可能变化): _VZMacHardwareModelDescriptor → 硬件模型创建的核心 _setROMURL → 自定义 BootROM 的唯一途径 _VZSEPCoprocessorConfiguration → SEP 模拟的唯一途径 _setForceDFU / _setStopInIBoot → DFU 模式启动控制 中风险 (API 较稳定但仍为私有): _VZPL011SerialPortConfiguration → 串口调试 _VZUSBTouchScreenConfiguration → 触摸输入 (macOS 16 已部分公开) _VZGDBDebugStubConfiguration → 内核调试 低风险 (趋向公开): VZVirtualMachineView 触摸支持 → macOS 16 已原生支持每次 macOS 大版本升级都可能导致私有 API 变更。项目的 Dynamic 库调用模式虽然避免了编译时绑定,但运行时仍可能因方法签名变化而崩溃。
12.3 Entitlement 与安全策略
运行 vphone-cli 需要 禁用 SIP + AMFI,这是因为:
需要 com.apple.private.virtualizationentitlement(正常情况下仅 Apple 自身二进制持有)需要 com.apple.private.virtualization.security-researchentitlement自签名二进制在 AMFI 启用时无法加载这些受限 entitlement
Apple 可能在未来通过更严格的 entitlement 验证(如要求 PPL 保护的签名链)进一步限制此类用途,即使 SIP/AMFI 被禁用也无法运行。
12.4 固件版本耦合
尽管动态补丁系统实现了"零硬编码偏移",但仍存在隐式耦合:
已知耦合点: ├── ADRP+ADD 指令模式假设 │ 如果 Apple 编译器切换到不同的寻址模式 (如 ADRP+LDR) │ → ADRP 索引将无法定位目标 │ ├── 字符串锚点依赖 │ 如 "@%s:%d" (panic格式)、"Sandbox" (MACF策略名) │ 如果字符串被修改或移除 → 补丁定位失败 │ ├── 结构体布局假设 │ mac_policy_conf 结构的字段偏移 │ Mach-O 段名称和布局 │ 如果内核结构体布局变化 → 解析出错 │ └── Cryptex AEA 加密格式 依赖 ipsw 工具解密 AEA → 如果 Apple 更换加密方案 → Cryptex 提取流程中断12.5 GPU 驱动的逆向维护成本
Metal GPU 加速依赖从 PCC dsc 逆向实现的 libAppleParavirtCompilerPluginIOGPUFamily.dylib。每次 iOS/cloudOS 版本更新可能带来:
Metal shader 编译器接口变化 GPU 命令提交协议版本升级 驱动 bundle 内部依赖变化
这意味着该 dylib 可能需要随每个主要版本重新逆向和适配,是长期维护成本最高的单一组件。
12.6 IPSW 版本匹配约束
混合固件要求 iPhone IPSW 和 cloudOS IPSW 的构建版本号 (Build Number) 必须匹配(如都为 23B85)。这是因为:
内核与用户空间的 syscall 接口必须版本一致 Cryptex DMG 中的 dyld_shared_cache 与内核期望的 ABI 必须匹配 设备树中的属性与对应固件组件必须兼容
如果 Apple 在某个版本中仅更新 iPhone IPSW 而不同步更新 cloudOS IPSW(反之亦然),将出现无法构建有效混合固件的空窗期。
附录 A: 启动链数据流
AVPBooter (ROM, 补丁: DGST 绕过) │ ▼LLB (cloudOS, 补丁: 串口+Image4+boot-args+rootfs+panic) │ ▼iBSS (cloudOS, 补丁: 串口+Image4) ← DFU 入口 │ ▼iBEC (cloudOS, 补丁: 串口+Image4+boot-args) │ ▼SPTM (cloudOS, 无补丁) + TXM (cloudOS, 补丁: 信任缓存绕过) │ ▼KernelCache (cloudOS vphone600, 补丁: 26 处) │ ├──── 正常启动 ────→ iOS 用户空间 (iPhone, CFW 补丁) │ ├── vphoned (vsock daemon) │ ├── dropbear (SSH :22222) │ ├── trollvnc (VNC :5901) │ └── bash (控制台) │ └──── Ramdisk 启动 ──→ SSH Ramdisk (cloudOS) └── 用于 cfw_install 写入文件系统附录 B: 安全绕过点汇总
┌──────────────────────────┬────────────────────────────────────────────────┐│ 安全机制 │ 绕过方式 │├──────────────────────────┼────────────────────────────────────────────────┤│ Image4 签名验证 │ iBSS/iBEC/LLB: Image4 回调 NOP ││ DGST 验证 │ AVPBooter: mov x0, #0 ││ APNonce 生成 (JB) │ iBSS JB: tbz/tbnz w0,#0 → b (跳过 nonce 生成) ││ TXM 信任缓存 │ TXM base: 哈希比较 BL → mov x0, #0 ││ TXM CS 代码签名验证 │ TXM sel24: mov w0,#0xa1; b epilogue (PASS 短路)││ get-task-allow entitlement│ TXM sel41: BL → mov x0, #1 ││ debugger entitlement │ TXM sel42|29: shellcode + BL → mov w0, #1 ││ 开发者模式 │ TXM init: TBNZ w9,#0 → NOP ││ APFS 密封卷 │ Kernel: 根快照/密封/认证 3 处 NOP ││ APFS 根哈希 │ Kernel: apfs_graft validate → mov w0, #0 ││ 启动约束 │ Kernel: launch_constraints → mov w0,#0; ret ││ 代码签名后验证 │ Kernel: TXM CS + AMFI → NOP / cmp w0,w0 ││ AMFI 信任缓存 (JB) │ Kernel: amfi_cdhash_in_trustcache → mov x0,#1; ret ││ IOUC MACF gate (JB) │ Kernel: CBZ W0 → B allow (IOKit open 绕过) ││ dyld 策略 │ Kernel: check_dyld_policy → mov w0, #1 ││ 调试器检测 │ Kernel: PE_i_can_has_debugger → mov x0, #1 ││ Sandbox MACF 钩子 │ Kernel JB: 10 个 hook → mov x0, #0; ret ││ 读写挂载 │ Kernel: mount_upgrade_checks 绕过 ││ task_for_pid (JB) │ Kernel JB: NOP ldr w,[task,#0x490] ││ vm_protect W^X (JB) │ Kernel JB: NOP 权限检查分支 → RWX 内存 ││ rootvp auth (JB) │ Kernel JB: _bsd_init cbnz → NOP ││ launchd 缓存验证 │ CFW: launchd_cache_loader NOP ││ 设备激活锁 │ CFW: mobileactivationd → 返回 YES ││ Jetsam Panic 保护 (JB) │ CFW JB-1: launchd 条件分支 → 无条件跳转 ││ debugserver 沙盒 (JB) │ CFW JB-3: 移除 seatbelt-profiles + task-allow ││ Gigalocker UUID │ CFW: seputil "/%s.gl" → "/AA.gl" │└──────────────────────────┴────────────────────────────────────────────────┘
夜雨聆风