VM 运行时:窗口、输入与控制面

虚拟 iPhone 启动后的日常操作指南——窗口交互、触摸/键盘/硬件键注入,以及经 vphone.sock 的一行 JSON 控制面用法。

VM 运行时:窗口、输入与控制面

本文面向日常使用:VM 启动后如何与其交互、如何用 HostControl socket 做脚本化控制,以及两条底层控制通道的基本概念。

启动与窗口

vphone-cli boot 启动后宿主进程完成整条链路:读取 VM manifest 组装 VZVirtualMachineConfiguration(PV=3 私有硬件模型)→ validate() → 启动虚拟机 → 连接与 guest 内 vphoned 的各条 vsock 通道 → 建立可交互窗口。

vphone-cli boot --config vm/config.plist --variant dev
vphone-cli boot --config vm/config.plist --resume    # 从 .vz-state 恢复

窗口行为要点:

  • 窗口尺寸 = 屏幕像素 ÷ screenScale,纵横比锁定,不可自由拉伸。
  • toolbar 只有一个 Home 按钮(走 HID 注入);右键点击窗口任意位置或按 Cmd+H 也发 Home。
  • 标题栏显示连接状态:[connected] / [disconnected],副标题显示 ECID 与 guest IP。
  • 关掉最后一个窗口即退出进程并关机 VM。
  • .ipa / .tipa 文件直接拖到窗口上即可安装到 guest。

无头模式

普通 boot 加 --no-graphics 进入无头模式:不建窗口与菜单,但 HostControl socket 照常监听,截图直接读虚拟机的显示输出,触摸注入、电池同步等服务全部保留。DFU 启动(--dfu)天然无头并额外裁掉控制面。

输入路径一览

输入类型 路径 备注
触摸 vphoned guest 侧 HID 注入 iOS major < 26 的 base 默认路径
触摸(回退) VZ 私有多点触控设备 26.x base 的原生路径
硬件键 vphoned HID Home / 电源 / 音量 / Spotlight(Cmd+Space)
打字 _VZKeyboard.sendKeyEvents 仅 ASCII(US 布局)
Unicode 文本 自定义键盘扩展 所有非 less 变体默认安装(可关),见「自动化」篇
Touch ID BiometricKit 手指监听 单击 = Home,双击 = App Switcher
拖拽安装 vsock install_ipa 拖 IPA 到窗口
位置/电池 CoreLocation / IOKit 转发 经 vphoned 注入 guest

触摸在窗口边缘 32pt 内会转换为边缘手势(swipe aim)。无头模式下触摸注入优先走 guest HID,能力不足时自动回退 VZ multitouch。

控制面:两条通道

运行时控制经宿主侧 VPhoneGuestControl / VPhoneHostControl 两个组件暴露,外部用户只需接触后者。

通道 端点 用途
HostControl socket <bundle>/vphone.sock(AF_UNIX,0600) 自动化/脚本驱动 VM 的唯一入口
vphoned 控制通道 host → guest vsock 1337 对 guest 的全部运行时控制(内部实现)
SSH 桥 127.0.0.1:<sshPort> ↔ vsock 2222 ssh -p <sshPort> root@localhost 进 guest
端口转发 host listener ↔ vsock 1339 把 guest 内 TCP/unix 服务映射到 host;远程 VM 经节点 agent 的 TCP proxy 中继到控制端本机(见「集群」篇)

socket 路径解析顺序:--socket 参数 → VPHONE_HOST_CONTROL_SOCKET 环境变量 → vm/vphone.sock

HostControl socket 用法

请求为"一行 JSON,换行结尾",响应也是一行 JSON(必含 ok 字段)。默认每个命令执行后附带一张压缩截图(base64 JPEG,可设 "screen":false 关闭;"delay" 控制截图前等待毫秒数)。

# 推送 vphoned 更新(详见「vphoned」篇)
echo '{"t":"vphoned_update"}' | nc -U "$(VM_DIR)/vphone.sock"

# 触摸注入
echo '{"t":"tap","x":645,"y":400}' | nc -U "$(VM_DIR)/vphone.sock"
echo '{"t":"swipe","x1":645,"y1":2000,"x2":645,"y2":800,"ms":300}' | nc -U "$(VM_DIR)/vphone.sock"
echo '{"t":"long_press","x":645,"y":400,"ms":600}' | nc -U "$(VM_DIR)/vphone.sock"

# 硬件键与文本
echo '{"t":"key","name":"home"}' | nc -U "$(VM_DIR)/vphone.sock"
echo '{"t":"type","text":"hello"}' | nc -U "$(VM_DIR)/vphone.sock"

# 截图与应用管理
echo '{"t":"screenshot"}' | nc -U "$(VM_DIR)/vphone.sock"
echo '{"t":"app_launch","bundle_id":"com.apple.Preferences"}' | nc -U "$(VM_DIR)/vphone.sock"
echo '{"t":"open_url","url":"https://example.com"}' | nc -U "$(VM_DIR)/vphone.sock"

常用命令族速查(坐标为 1290×2796 像素空间,左上角原点):

命令示例
输入 tap / swipe / long_press / key / type / keyboard_delete / keyboard_clear
应用 app_list / app_launch / app_terminate / app_foreground / open_url / install_ipa
剪贴板/设置 clipboard_get / clipboard_set / settings_get / settings_set
文件 file_list / file_download / file_upload / file_delete / file_rename
位置/电池/相机 location_set / battery_set / camera_source / touchid
网络观测 network_status / network_flows / network_set_direct_rules
录制 record_start/stop(输入事件 JSONL)、record_screen_start/stop(.mov 录屏)
挂起/恢复 vm_save_state / vm_resume / vm_suspend(配合 boot --resume
vphoned ping / version / vphoned_update
推流 screen_stream(mjpeg/h264 帧推送,唯一推模式命令)
转发 forward_add / forward_remove / forward_list

guest 缺少某项能力时,响应会附 capability 字段提示;通常意味着需要 make vphoned 重建并推送更新后再试。

安全边界

  • vphone.sock 权限 0600,服务端用 getpeereid 校验对端 UID 必须与本进程一致;并发客户端上限 8,超限返回 busy
  • 命令走显式 allowlist;截图等落盘操作用 O_CREAT|O_EXCL|O_NOFOLLOW 防覆盖与 symlink 攻击。
  • guest 侧零 TCP 监听——SSH 与端口转发全部经 vsock,guest 内 App 扫端口不可见。

挂起与恢复

echo '{"t":"vm_suspend"}' | nc -U vm/vphone.sock   # 保存状态并关机
vphone-cli boot --config vm/config.plist --resume   # 秒级恢复到现场

.vz-state 保存 CPU/内存/设备状态(不含磁盘),格式与宿主机绑定且不随 VM 导出离开本机;恢复失败(如宿主 macOS 升级)会自动降级为冷启动。