iOS 真机 JS 注入 · 第 5 课(终)
能执行 JS 只是手段。真正的问题是:妙记 iOS 那个网页容器,到底接没接 common.getSettings 这个原生接口?
难点在于——这是个异步调用(发出去要等 native 回话),而 js-shell 每次执行是独立会话,
没法在一次里坐等一个可能永远不回来的回调。这里有两件事:用"跨会话读全局变量"两步走破这个局,
以及为什么对照实验才是把"超时"变成"铁证"的关键。
桥调用长这样:网页喊一句"native 帮我查下配置",然后挂个回调等 native 回喊。问题来了:
更麻烦的是逻辑层面:"超时"本身证明不了什么。页面慢、我参数写错、网络抖动——都会超时。 光看到"没回来",没法断定"是容器的锅"。我需要的是能排除自身嫌疑的证据。
我发现一个能用的事实:同一个网页上的全局变量(window.xxx),在 js-shell 的多次会话之间是保留的。
这就把"发"和"读"解耦了——我不必在一枪里既发又等,可以:
window.__PROBE_R__ = {state:'waiting'};给回调挂钩,native 一回话就把它改成 done + 数据。发完这枪就退出。window.__PROBE_R__.state。是 done=回话了;还是 waiting=这几秒里 native 压根没回。第二枪读到的还是 waiting——就是
"native 不响应这个接口"的铁证:不是没等够(都过了几秒、独立一枪不存在我死等挂住的问题),是它真的一声不吭。
只有一个 waiting 还不够硬——万一是我探针写错了呢?所以同一个探针,我打两个地方:
done,配置数据拿到。证明探针本身是对的。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——协议同构,换个发送函数而已。)
到这里,整条证据链完整、自洽:
📖 一手源:完整探针脚本与表达式在飞书「版本 Tag 真机验证全流程与调试脚本」(节点 Z7zGwGZJziKqOrkfDzGcF2vpnJf),团队可直接复用。