iOS 真机 JS 注入 · 第 5 课(终)

拿到铁证:怎么证明"它根本不回话"

能执行 JS 只是手段。真正的问题是:妙记 iOS 那个网页容器,到底接没接 common.getSettings 这个原生接口? 难点在于——这是个异步调用(发出去要等 native 回话),而 js-shell 每次执行是独立会话, 没法在一次里坐等一个可能永远不回来的回调。这里有两件事:用"跨会话读全局变量"两步走破这个局, 以及为什么对照实验才是把"超时"变成"铁证"的关键。

为什么这题不好做

桥调用长这样:网页喊一句"native 帮我查下配置",然后挂个回调等 native 回喊。问题来了:

两个约束打架

更麻烦的是逻辑层面:"超时"本身证明不了什么。页面慢、我参数写错、网络抖动——都会超时。 光看到"没回来",没法断定"是容器的锅"。我需要的是能排除自身嫌疑的证据。

破局关键一:页面全局变量,跨会话还在

我发现一个能用的事实:同一个网页上的全局变量(window.xxx),在 js-shell 的多次会话之间是保留的。 这就把"发"和"读"解耦了——我不必在一枪里既发又等,可以:

1
第一枪:发请求 + 埋一个"信箱" 把状态存进 window.__PROBE_R__ = {state:'waiting'};给回调挂钩,native 一回话就把它改成 done + 数据。发完这枪就退出。
2
第二枪(隔几秒):回来读信箱 重新进 js-shell,只读 window.__PROBE_R__.state。是 done=回话了;还是 waiting=这几秒里 native 压根没回。

第二枪读到的还是 waiting——就是 "native 不响应这个接口"的铁证:不是没等够(都过了几秒、独立一枪不存在我死等挂住的问题),是它真的一声不吭。

破局关键二:对照实验,把"超时"钉成"铁证"

只有一个 waiting 还不够硬——万一是我探针写错了呢?所以同一个探针,我打两个地方

打在飞书视频会议·会中的 iOS 容器 同一段探针 → 秒回 done,配置数据拿到。证明探针本身是对的
打在妙记·iOS 容器 同一段探针 → 永远 waiting。(Android 上妙记同一接口约 8ms 就回。)

一个回、一个不回,探针、账号、设备、接口全一样,唯一变量就是"哪个容器"。这就把嫌疑从我身上彻底摘干净, 锁定到"妙记 iOS 这个容器没注册 common.getSettings"。这,就是要交给客户端团队的那句话背后的实锤。

用到的桥协议(写文档备查)

iOS 侧这座桥的两个方向:

// 发:网页 → native
webkit.messageHandlers.invokeNative.postMessage(
  JSON.stringify({ apiName:'common.getSettings', data:{...}, callbackID:'唯一ID' })
);
// 收:native → 网页(我们 hook 它来截获回话)
window.LarkWebViewJavaScriptBridge.nativeCallBack = function (resp) { ... }

要点:callbackID 用唯一值,这样我们截获的是自己这一枪的回话、不会和 App 自身的调用串味。 (Android 侧发送口是 window.Lark_Bridge.invokeNative(...),收口同样是 nativeCallBack——协议同构,换个发送函数而已。)

把五节课串成一条能复述的证据链

到这里,整条证据链完整、自洽:

1
难在哪iOS 17 把真机调试底层从 lockdownd 换成 RemoteXPC,要 IPv6 隧道 + 更严加密。
2
常规路为什么全废两类死因:老工具没跟上(iwdp)/ 新工具不成熟·链路太脆(cdp、隧道会话)。
3
这条路为什么行js-shell --userspace:自带进程内轻隧道(无 root)+ 苹果原生 WebKit 调试协议。
4
怎么跑顺pty 伪终端补"真终端"、箭头字符补"按键"、先备份 URL 防导航翻车。
5
怎么拿到结论异步桥两步走探针 + 对照实验 → 实锤"妙记 iOS 容器不回 common.getSettings"。

相关材料

📖 一手源:完整探针脚本与表达式在飞书「版本 Tag 真机验证全流程与调试脚本」(节点 Z7zGwGZJziKqOrkfDzGcF2vpnJf),团队可直接复用。


第 5 课 / 共 6 课 · 课程:iOS 真机 JS 注入