在电脑上列出一台 iPhone、截一张图或者读取一段 syslog,看起来只是几条命令。真正走进实现后,会遇到 usbmuxd、配对记录、lockdown、TLS、AFC、DTX,以及 iOS 17 之后越来越重要的 CoreDevice、RSD 和 RemoteXPC。
rust-ios-device 尝试用 Rust 把这些层次组织成一套可以复用的工具链:底层是 ios-core,上层提供 ios CLI、Python 模块与 C ABI。当前版本为 0.1.8,能力面已经不小,但项目仍明确标记为实验性。
两条主要连接路径
iOS 设备服务大致可以分成经典路径和 CoreDevice 路径。
经典服务通常从 usbmuxd 开始:
host → usbmuxd → device lockdown port → StartService → service portusbmuxd 负责发现设备,并把主机上的连接转发到 USB 设备端口。lockdown 则处理配对记录、会话建立、TLS 和服务启动。AFC 文件访问、syslog、截图、崩溃报告、SpringBoard 与 diagnostics relay 等能力都可以建立在这条路径上。
iOS 17+ 的 CoreDevice 服务通常需要另一条链路:
host → usbmuxd 或 remote pairing → CoreDeviceProxy / CDTunnel → RSD 服务发现 → RemoteXPC 或原始服务这里的难点不只是“再加一个协议”。隧道建立后,还要通过 Remote Service Discovery 确认设备真正暴露了哪些服务,再通过 HTTP/2 与 XPC 编码和服务通信。不同系统版本、配对状态、Developer Mode 和设备服务面,都会影响最终可用能力。
因此 CLI 不会只根据 iOS 版本猜测服务一定存在。遇到 CoreDevice fileservice 等功能时,可以先检查实际服务目录:
ios rsd services --allios rsd check com.apple.coredevice.fileservice.controlios file --coredevice --domain temporary ls /如果设备没有同时暴露所需的 control/data 服务,工具会报告明确的缺失服务错误,而不是悄悄换一个名字或落到语义不同的实现上。
用分层隔离协议复杂度
ios-core 内部按照职责继续拆分:
proto处理 plist、AFC、DTX、XPC 等线格式和编解码;mux与 usbmuxd 通信,负责设备枚举、监听和端口转发;lockdown管理配对、会话、TLS 与经典服务启动;tunnel实现 CDTunnel 握手以及用户态或内核态的数据转发;xpc实现 RSD、HTTP/2 和 RemoteXPC 传输;services提供面向具体能力的客户端。
这套分层的价值在于,高层命令不需要重复处理连接细节。CLI 的 screenshot、syslog 或 apps 命令关心的是任务,底层则根据设备与服务选择合适的连接路径。
同时,内部协议模块不会全部作为稳定 API 暴露。支持给库用户的类型会在 crate 根部重新导出,内部组织仍可以随着协议理解的深入继续调整。对于一个公开 API 尚未稳定的协议项目,这种边界能减少过早承诺。
一个核心,三种使用方式
整个 workspace 有四个主要 crate:
| Crate | 面向对象 | 作用 |
|---|---|---|
ios-core | Rust 开发者 | 设备发现、配对、服务访问、隧道与高层 API |
ios-cli | 终端与自动化脚本 | 提供 ios 命令和 54+ 子命令 |
ios-py | Python 生态 | 通过 PyO3 暴露设备列表与隧道工作流 |
ios-ffi | 其他语言 | 输出动态库、静态库与 ios_rs.h |
它们共用同一份核心实现。这比在 Rust、Python 和 C 侧分别维护一套设备协议更重要:修复握手、配对或隧道问题时,各入口可以一起受益。
CLI 默认输出 JSON,方便被脚本继续处理:
ios listios infoios -u <UDID> lockdown get --key ProductVersionios screenshot --output screenshot.png需要给人阅读时可以传入 --no-json。设备参数省略时使用发现列表中的第一台设备,也可以通过 -u 或 IOS_UDID 固定目标。这些看似普通的 CLI 约定,对持续集成和多设备环境很重要。
Feature flag 也是架构边界
ios-core 默认不启用具体服务,库使用者可以只选需要的能力:
[dependencies]ios-core = { version = "0.1.8", features = ["afc", "syslog"] }仓库同时提供 classic、developer、management、ios17 和 full 等分组。CLI 为了覆盖完整命令面使用 full,而嵌入其他应用的库通常应该选择更窄的集合。
这既减少不必要的依赖和编译范围,也迫使服务模块保持相对清晰的边界。对于包含大量设备服务的项目,feature 设计不只是优化包体积,也是一张可以被 Cargo 检查的能力地图。
为什么同时支持用户态与内核态隧道
CoreDevice 隧道可以通过内核 TUN 接口建立,但创建网络接口通常需要管理员或 root 权限。为了让普通用户和 Python 工具也能使用,项目还提供基于 smoltcp 的用户态转发。
# 启动单设备用户态隧道ios tunnel start --userspace
# 启动本地隧道管理服务ios tunnel serve --userspace --host 127.0.0.1 --port 49151用户态模式暴露一个本地 TCP 代理。客户端先发送目标 IPv6 地址和端口,再开始转发数据。Python 绑定进一步提供 asyncio_proxy() 上下文,让现有的 asyncio.open_connection() 可以在作用域内通过这条隧道路由。
内核模式更接近系统网络接口,用户态模式则降低权限和集成门槛。两者并存,不是重复功能,而是为桌面工具、自动化脚本和开发环境提供不同的取舍。
对不稳定设备环境保持诚实
真实设备工具的测试矩阵很难穷举:主机可能是 Windows、macOS 或 Linux,设备系统版本不同,信任、配对、监督和 Developer Mode 状态也不同,部分服务还依赖开发者磁盘镜像。
因此当前项目强调两件事:
第一,错误信息尽量指向缺失的前置条件,例如服务未暴露、设备未配对或权限不足;第二,对会改变设备状态的命令保持明确警告。erase、restore、监督配置、描述文件安装与定位修改等操作,应优先在测试设备上验证。
pair record 和监督证书同样属于敏感凭据,不应提交到仓库或写进共享日志。协议工具越接近设备管理能力,越需要把安全边界当作正常功能,而不是文档末尾的一句免责说明。
最后
rust-ios-device 现在已经能覆盖从设备发现、文件访问和日志,到 Instruments、TestManager、WebInspector 与 iOS 17+ 隧道的一条很长链路。但比命令数量更值得记录的,是它如何把复杂度放回合适的位置:协议层理解字节,传输层理解连接,服务层理解任务,CLI 和语言绑定只组合公开能力。
项目距离稳定 API 还有路要走。也正因为如此,先守住这些层次,会比急着把所有内部模块公开更有价值。