iOS 真机 JS 注入 · 第 3 课
第 2 课把答案框成了一句话:要一个面向 iOS 17 新架构、又轻量稳定的执行口子。
pymobiledevice3 webinspector js-shell --userspace 正好两条全中。这一课拆开它,
你会看到它赢在两个设计上:① 自己带一条"不用 root、藏在进程内"的隧道(对治死胡同三的"重且脆"),
② 用苹果原生的调试协议把 JS 直接送到第 5 层(对治死胡同一的"架构没跟上")。
pymobiledevice3 webinspector js-shell --userspace
它其实是两件事拼起来的:--userspace 解决"怎么合法进设备"(第 2 层那道关卡),
webinspector js-shell 解决"进去之后怎么把 JS 送到网页"(第 3→5 层)。分开讲。
--userspace —— 自己在进程里修了一条隧道回忆第 1 课:iOS 17 后必须先有一条 IPv6 隧道才能过关。死胡同三就是卡在"建隧道 / 起会话"这步—— 因为默认那条隧道很重:
默认隧道要在你电脑上创建一个内核级网卡(TUN/TAP 接口), 这需要 root/sudo 权限,还常常要一个单独常驻的特权守护进程(tunneld)在旁边伺候。[1] 环节多、权限高、依赖重——真机上任何一环掉链子,整条会话就吊死(这正是死胡同三)。
--userspace 换了个完全不同的活法:
它用一套纯 Python 写的网络栈(PyTCP),在 js-shell 自己这个进程内部
把 TCP/IP 全跑起来——不建内核网卡、不要 root、不需要单独的 tunneld 守护进程。[1]
等于工具自带一条"软隧道",即插即用。
这里有一个特别妙、也特别关键的设计——
这条隧道只活在 js-shell 这一个进程里,
你机器上别的程序都用不了它(比如 lldb 连不进来)。[1]
听起来像缺点,但对我们是正好:我们要执行 JS 的 js-shell 本身就住在这个进程里,
它不需要跟任何外部程序共享。自带、自用、自洽——环节最少,所以最稳。
对照第 2 课的分类:死胡同三是"链路太重太脆",--userspace 恰恰是把链路砍到最短、依赖砍到最少的那个版本。同一个方向,换了条轻的走法。
webinspector js-shell —— 用苹果原生协议把 JS 送进网页进了设备,怎么把 JS 塞进第 5 层的网页运行时?靠 WebKit Inspector Protocol WebKit 检查器协议—— 这是苹果自己给 Safari/WKWebView 调试用的原生协议。因为是原生的,它天然活在 iOS 17 的新世界里, 不像 iwdp 那样架在已经拆掉的老门上。[2]
Target.targetCreated 事件,把当前能调试的网页(tab)一个个发现出来;Runtime.evaluate——把你那行 JS 丢进它的 JS 运行时执行,再把结果带回来。[2]你会发现 Runtime.evaluate 这名字和第 2 课 CDP 里的一模一样——"求值"这个动作是共通的,
区别只在:CDP 那条路(死胡同二)用的命令实现有 bug,而 js-shell 直接走苹果原生协议这条更贴近系统的路。
还有个省事的前置:js-shell 只要求设备开着 Web Inspector 开关即可,不需要额外打开"远程自动化(Remote Automation)"。[2]
它不是没成本,只是成本恰好落在我们不在乎的地方:
——这就是工程判断:没有"最好"的工具,只有"代价落在你不在乎处"的工具。
原理通了,但"能通"不等于"顺手"。第 4 课进实操:真正跑起来时我踩的三个坑——
--url 参数把页面导航去了 404、js-shell 必须要真终端(TTY)、多网页时的方向键"选页"菜单。
这几个坑,是"原理对但差点用不出来"的典型。
📖 参考来源:pymobiledevice3 — iOS 17 Tunnels 指南 (userspace 隧道的官方说明,含代价[1]);协议细节见 DeepWiki — Inspector Session & JS Evaluation[2]。