系统总体架构

深入剖析 vphone-cli 的四层进程域、构建期与运行期两条主线,以及工程化设计

系统总体架构

vphone-cli 是基于 Apple Virtualization.framework(PV=3 私有 entitlement)的虚拟 iPhone 研究工具:在 macOS 宿主机上完成固件准备 → 启动链补丁 → DFU restore → CFW 安装 → 启动运行 → 运行时控制的完整闭环。本文是理解全局的地图——先看四个进程域如何拼成系统,再分清「构建期」与「运行期」两条主线,最后拆解三个让这套系统得以产品化的工程决策。

全景架构:四个进程域

系统由四个进程域组成:宿主机上的三个前端、宿主机上的特权与辅助进程、Guest 域内的虚拟 iPhone,以及驱动自动化的外部控制客户端。宿主与 guest 之间只经 vsock 通信(1337 控制通道 + 2222/1338/1339 三条数据通道),外部世界与宿主之间只有 vphone.sock 一个入口——极简的边界是后文所有设计决策的起点。

图上三条最重要的连线:manager/agent → 无头 CLI 子进程(VM 永远跑在独立进程里)、CLI ⇄ vphoned 的 vsock:1337(宿主对 guest 的全部运行时控制)、客户端 → vphone.sock(外部世界的唯一入口)。

域一:宿主前端——三个入口,一组库

前端 形态 定位
vphone-cli 宿主应用(签名 + 私有 entitlements) CLI + 菜单栏 UI;boot 后直接持有 VZVirtualMachine,提供窗口、输入、录屏与 HostControl 服务
vphone-manager SwiftUI 桌面 GUI(无私有 entitlements) 多 VM 集中管理;VM 运行交给 spawn 的无头 CLI 子进程,创建管线在进程内驱动
vphone-agent 无头用户级 daemon 集群节点侧:以 Bearer token 认证的 HTTP API 暴露本机 VM 库与生命周期操作

三个前端不是三套实现,而是同一组 SwiftPM 库的三个入口:

共享库 职责
VPhoneCore VM bundle / manifest / ~/.vphone 库、restore 与 host-mount 原语、子进程工具
VPhoneCreate vm create 端到端编排(VPhoneCreateOrchestrator,结构化事件与协作式取消)
FirmwarePatcher 全部固件补丁与 CFW 安装器(fw prepare / fw patch / cfw install 的引擎)
VPhoneManagerCore Manager 与 agent 共享的纯逻辑层(不依赖 SwiftUI):VM 生命周期、库发现、远程节点客户端
VPhoneNetworking 网络配置与流量遥测客户端
VPhoneLogging 统一日志库
VPhoneHostControlTransport HostControl 客户端传输抽象——本地 unix socket 与远程 agent HTTP 代理同一套接口

收益直白:CLI 上验证过的建机与控制语义,GUI 与集群直接继承,行为不会分叉;逻辑沉在库层,单测与回归天然覆盖三个前端。

域二:特权与辅助进程

com.vphone.helper(root LaunchDaemon) —— cfw installvm create 需要在 VM 关机状态下把 Disk.img 挂到宿主机写文件、并翻转 APFS boot snapshot,这些是 root 操作。helper 经一次性的 make helper_install 安装,之后全程免密:

  • XPC 只接受签名 identifier 为 com.vphone.cli / com.vphone.manager 的连接;
  • 动词面严格受限(挂载 / 卸载 / 属主收尾 / 重跑 cfw install 共五个动词),没有任意命令执行;
  • 设备必须匹配 /dev/diskNsM 且不属于启动盘,挂载点必须落在白名单根内;
  • 没装 helper 时可 sudo 直跑等价的 root 路径(研究机兜底)。

pymobiledevice3_bridge.py(DFU restore 桥) —— 全管线唯一保留的 Python 子进程,只做一件事:封装 pymobiledevice3 完成 DFU 下的 SHSH 抓取与固件刷入。其余阶段全部是 Swift in-process。

network-proxy(Go sidecar) —— VM 的网络出口:guest 的 NAT 流量经它透明代理到上游(SOCKS5 / HTTP),同时产出流量遥测,供宿主观测 guest 的网络行为。

域三:Guest 域——虚拟 iPhone 与 vphoned

VM 本体是 Virtualization.framework 以 PV=3 私有硬件模型启动的虚拟 iPhone——这也解释了宿主为何必须放宽 SIP/AMFI:只有带私有 entitlement 的签名二进制才被允许这样使用 Hypervisor。guest 内的常驻执行端是 vphoned

  • ObjC LaunchDaemon,伪装成 Apple 系统 daemon 常驻(launchd KeepAlive 保活);
  • 监听 vsock:1337,按 14 个命令族处理宿主请求:输入注入(HID / 触摸)、文件传输、应用与 IPA 安装、账户 profile、keychain、剪贴板、系统设置、位置模拟、自更新等;
  • 另持三条独立数据通道:2222 SSH 桥(inetd 模式拉起 dropbear,guest 内零 TCP sshd 暴露)、1338 虚拟相机帧中转、1339 端口转发 relay;
  • 支持免重启热更新:握手比对二进制 SHA-256 → 宿主推送新版 → launchd 重启生效,是固件级改动之外的主要迭代通道。

域四:外部控制客户端

外部世界对 VM 的一切操作收敛到一个入口:<bundle>/vphone.sock(AF_UNIX、0600、校验对端 UID)。无论你是 automation/ 的 AI E2E 框架、MCP 工具链,还是一行 nc -U 脚本,看到的都是同一个「一行 JSON 进 / 一行 JSON + 内联截图出」协议——与菜单栏 UI 的人工操作共用完全相同的 VPhoneGuestControl 通道。

主线一:构建期——把一台 VM 做出来

理解本仓库最高效的方式,是区分构建期(把一台 VM 做出来)与运行期(把 VM 用起来)。构建期由 vphone-cli vm create 驱动(make rebuild_dev 与 Manager 创建向导都是同一引擎的入口),按固定顺序走完六个阶段:

阶段 做什么 关键承担者
1. fw prepare 下载并解包 iPhone 与 cloudOS 两份 IPSW,cloudOS 启动链合并进 iPhone 固件树,生成 hybrid BuildManifest FirmwarePatcher · Prepare
2. fw patch 按变体对 dfu(iBSS/iBEC)、all_flash(LLB/DeviceTree)、txm、kernelcache 原地打补丁 FirmwarePipeline + 各组件 patcher
3. DFU restore VM 以 DFU 模式启动,抓取 SHSH,把混合固件刷入后关机;系统落在 Disk.imgSEPStorage pymobiledevice3_bridge.py 子进程
4. cfw install 关机状态下经 helper 把 Disk.img 挂到宿主,变体安装器写入 CFW 文件(vphoned、dylib、配置等) CFWHostMounter + com.vphone.helper
5. snapshot 翻转 改名具名 root snapshot,让下次引导挂载被修改过的 live 卷(与阶段 4 同属一道工序) ApfsSnapRename(root 上下文)
6. first boot 首启注入初始化命令后关机;再次启动并分析启动结果,判定成败、落盘固件信息 boot analysis

要点:

  • 编排器是库不是脚本VPhoneCreateOrchestrator 是独立的 VPhoneCreate library target,CLI 直接驱动,vphone-manager 在进程内驱动同一引擎(消费结构化事件、支持取消);需要 VM 实例与 root 权限的阶段仍由签名的 vphone-cli 二进制承担,六个阶段中只有 DFU restore 依赖 Python 子进程。
  • 变体决定补丁强度less / regular / dev / jb / exp 五档安全绕过逐级递增(less 最大程度保留 iOS 缓解措施,expjb 超集并含反 VM 检测研究补丁),详见「固件变体全解」。
  • 每个补丁都有账可查:写回时生成 PatchRecord(offset、前后字节、前后反汇编),汇总进 VM bundle 的 patch_report.json,可审计、可回归比对。

实操向导见「创建你的第一台虚拟机」;补丁细节见「固件补丁管线」。

主线二:运行期——把 VM 用起来

vm launch 之后,人工点击与 AI 自动化走完全相同的控制链:

三个值得注意的设计:

  1. 每步操作回传内联截图。默认每条命令执行后附带一张压缩截图("screen":false 可关,"delay" 控制截图前等待的毫秒数)。对自动化这等于「操作即可见」——点完立刻拿到视觉反馈,不需要额外的截图轮询。
  2. 人工与自动化同一条通道。菜单栏按钮与 nc -U 发出的 JSON 走同一个 VPhoneGuestControl;GUI 能做的自动化都能做,行为不会分叉。
  3. 能力协商与热更新。握手时 vphoned 上报能力列表(caps),宿主按能力决定输入路径与菜单可用性;make vphoned 构建后经 vphoned_update 推送,guest 侧改动免重启生效。

安全边界同样收敛:socket 0600 且校验对端 UID,命令走显式 allowlist,guest 内零 TCP 监听(SSH 与端口转发全走 vsock,guest 内 App 扫端口不可见)。

产品化与工程化:三个「为什么」

架构图上「为什么这么连」比「连了什么」更值得读。以下三个决策分别回答了「怎么把研究工具做成产品」「怎么让补丁工程可靠」「怎么让固件数据可持续维护」。

为什么 Manager 和 Agent 都不直接持有 VZVirtualMachine?

两者共用同一个模型:spawn 签名的 vphone-cli boot --no-graphics 无头子进程跑 VM,自己只经 HostControl socket 控制。

  • 特权面收敛:PV=3 私有 entitlement 只存在于 vphone-cli 一个签名二进制里;Manager 与 agent 不碰 Virtualization.framework、不带私有 entitlements,因此能以常规签名分发安装(agent 甚至可以 Developer ID 分发),不必为整个 GUI 打开特权后门。
  • 崩溃隔离:VM 跑在独立子进程里,GUI 崩溃、退出、升级都不波及运行中的 VM——Manager 退出可选「Keep Them Running」让 VM 存活;agent 重启后经 lsof 重新发现托管 VM,子进程无感存活。
  • 生命周期同源:Manager 与 agent 共同链接 VPhoneManagerCoreVMSupervisor(preflight 预检、SIGINT→grace→SIGKILL 停机信号升级、lsof 运行态发现),一套语义两端复用,不会漂移出两套行为。
  • 能力不打折:无头 boot 只裁掉窗口,HostControl 照常监听——截图、触摸注入、录屏、挂起恢复全部可用。
  • 本地 / 远程同构:控制面统一是 HostControl,本地走 unix socket、远程走 agent 的 HTTP 代理,Manager 用同一套视图与命令封装无缝切换。

为什么 patcher 是 Swift 库而不是 Python 脚本?

固件补丁早期多由 Python 脚本承担;如今 FirmwarePatcher 是 SwiftPM library target,管线主体全程 in-process。

  • 类型安全:Swift 6 strict concurrency 下,PatchRecord / PatchResult / PatchReport 把「每个补丁必须记录 offset 与前后状态」固化成类型,而不是散落在脚本的 print 里。
  • 二进制基础设施可复用:Capstone 反汇编负责补丁点定位(按 mnemonic / 操作数 / 控制流的语义匹配,禁止硬编码偏移与预汇编字节),Keystone 负责替换指令现编(asm(...) / NOP / MOV_W0_0),全部补丁器共享同一套地基。
  • 统一错误处理PatcherErrorpatchSiteNotFound / multipleMatchesFound / patchVerificationFailed 等)把「锚点要么唯一命中、要么响亮失败」变成硬约束——绝不静默跳过或打错位置继续跑。
  • 无跨语言边界:CLI 与 Manager 直接调用同一引擎(vm create 的结构化进度事件即由库层回调产出);全管线 Python 只剩 restore 桥这一个纯第三方库封装。

为什么固件配对由单一 JSON 驱动?

iPhone IPSW 与 cloudOS IPSW 的配对关系全部收敛在 config/firmware_catalog.json

  • 配对是数据不是逻辑:新增或更新固件配对只改这个 JSON,零代码改动、零重新构建;fw prepare 默认取其中 defaultPairing 作为固件源。
  • 单一数据源,三端一致:该文件随发布 App 打包进 Contents/Resources/config/,CLI、Manager 的 Firmware 面板、agent 读到的是同一份——不存在「GUI 能下载的镜像 CLI 找不到」这类漂移。
  • 容错回退:catalog 缺失时回退内置字面量,旧 bundle 不会因此直接坏掉。

深入阅读

想深入 阅读
建机全流程实操 「创建你的第一台虚拟机」
补丁管线与变体 「固件补丁管线」「固件变体全解」
运行时控制与命令族速查 「VM 运行时:窗口、输入与控制面」
guest 守护进程 「guest 守护进程 vphoned」
GUI 与集群 「vphone-manager 多 VM 管理 GUI」「集群管理与 vphone-agent」
网络代理与遥测 「网络与代理」
自动化与 E2E 「自动化」
逐模块开发文档 仓库 docs/ 目录(总入口 docs/architecture.md