Commit 645c4d30dde49c9564539bda8ca950657cdebf7d
1 parent
09e6a868
research(wechat): 确认微信主进程 frida 注入不安全,两次真实故障,此路线到此为止
第二次尝试(spawn 后立即 resume,隔 0.8s 再 attach)连 hook 都没装成功 (frida 自身 JVMTI attach 报 access violation),但微信依然被搞挂(这次 表现为界面卡死而非"无法连接服务器")。两次事故一次 hook 装成功一次没装成功, 排除了"hook 代码本身有 bug",说明是"往微信主进程冷启动过程里做 frida attach"这个动作本身在这台设备这个微信版本上就不安全,不是时序调参能解决的。 建议:不再对主进程做任何 frida spawn/attach 变体;appbrand 子进程的 spawn-gating hook 依然安全可复用;退回半自动方案(人工点发布)性价比 明显更高。
Showing
2 changed files
with
57 additions
and
21 deletions
wechat_send_internal/BRANDSERVICE_PUBLISH_RESEARCH.md
| ... | ... | @@ -192,21 +192,50 @@ API 用)。 |
| 192 | 192 | (具体是哪一步冲突的没有细查,代价太高不值得再试来定位)。**不要在没有更谨慎方案 |
| 193 | 193 | 之前重复"spawn 主进程 + 挂钩子"这个操作。** |
| 194 | 194 | |
| 195 | -### 3.7 下一步(还没做,需要用户决策后再动) | |
| 196 | - | |
| 197 | -在主进程里稳定 hook `Ljs1/m;->c()` 的思路都不便宜: | |
| 198 | -- 主进程存量 attach(不 force-stop,不 spawn)大概率会撞上文档最早记录的"存量进程 | |
| 199 | - 类枚举超时"问题(主进程加载的类比 appbrand 子进程多得多,超时风险更高)。 | |
| 200 | -- 换个更早/更安全的注入时机(比如 spawn 后立即 resume、隔几百 ms 再单独 attach, | |
| 201 | - 不在 spawn-paused 状态下装钩子)也许能避开这次的冲突,但没验证过,仍有再搞坏一次 | |
| 202 | - 微信启动的风险。 | |
| 203 | -- 退回"半自动"方案(人工点发布,只自动生成图片+文案,不追求端到端全自动)此时 | |
| 204 | - 性价比在明显上升——今天已经:让用户配合走了 6 轮真机发布流程 + 1 次微信主进程 | |
| 205 | - 连接故障(虽然自愈了)。 | |
| 206 | - | |
| 207 | -三个原始脚本(`hook_lite_transfer.js`/`_bundled.js`, `multi_spawn_hook.py`, | |
| 208 | -`hook_transfer_sink.js`/`_bundled.js`, `spawn_mm_and_hook.py`)都已验证/写好可复用, | |
| 209 | -如果决定继续这条路线,工具链是现成的,缺的是一个更安全的主进程注入时机。 | |
| 195 | +### 3.7 第二次事故:换个注入时机(spawn 后立即 resume,隔 0.8s 再 attach)依然搞坏微信 | |
| 196 | + | |
| 197 | +`spawn_mm_and_hook.py` v2:不在 spawn-paused 状态下注入,而是 spawn 后立刻 | |
| 198 | +`resume()`,隔 0.8 秒再对已经在跑的 pid 做正常 `device.attach()`。这次连 hook 本身 | |
| 199 | +都没装成功——`Java.perform` 内部 attach JVMTI 环境时直接报 | |
| 200 | +`Error: access violation accessing 0x0`(frida 自己的 JVMTI attach 逻辑挂了, | |
| 201 | +时机太早,ART 运行时还没就绪),我们的 hook 代码根本没跑到。 | |
| 202 | + | |
| 203 | +**但即便 hook 没装成功,微信依旧被搞坏了**——用户反馈"微信点不动"(界面卡死/无响应), | |
| 204 | +跟第一次的"无法连接服务器"症状不同,但同样需要 `am force-stop` 全杀 + 手动重新打开 | |
| 205 | +才恢复正常。 | |
| 206 | + | |
| 207 | +**这排除了"是我们 hook 代码本身有 bug"这个解释**——两次事故一次 hook 装成功了 | |
| 208 | +(§3.6,之后微信显示网络错误),一次 hook 压根没装上就已经 native 报错(这次, | |
| 209 | +微信直接卡死)。共同点只有"往微信主进程冷启动过程里做 frida attach 这个动作本身"。 | |
| 210 | +不管注入时机多晚、hook 代码写不写东西,只要在主进程刚起来那段时间用 frida 碰它, | |
| 211 | +就有实质性概率把微信弄挂,且两次表现还不一样(无法连接服务器 / 界面卡死), | |
| 212 | +说明不是某个特定时序窗口的问题,而是这个操作本身对这台设备上这个微信版本 | |
| 213 | +就是不安全的。 | |
| 214 | + | |
| 215 | +### 3.8 结论:主进程注入这条路线到此为止 | |
| 216 | + | |
| 217 | +**不建议再尝试对微信主进程做任何形式的 frida spawn/attach**(不管是 spawn-paused | |
| 218 | +时机、resume 后延迟 attach,还是理论上的其它变体)。今天累计:6 轮真机手动发布 + | |
| 219 | +2 次主进程注入导致微信实际不可用(每次都要 force-stop 全杀 + 手动重开才恢复, | |
| 220 | +过程中用户体验是真实受影响的,不是纯理论风险)。风险已经从"可能浪费时间"上升到 | |
| 221 | +"实际两次打断用户在用的微信",性价比不再支持继续在真机上试验变体。 | |
| 222 | + | |
| 223 | +`appbrand` 子进程的 spawn-gating hook(`multi_spawn_hook.py`,用 | |
| 224 | +`hook_lite_transfer_bundled.js`)本身依然是安全的、已验证的技术(多次验证只影响 | |
| 225 | +子进程,不影响主进程/登录态),可以留着给其它子进程场景复用;`Ljs1/m;->c()` | |
| 226 | +是不是真正 sink 也依然成立(反编译分析站得住),只是"怎么安全地够到跑在主进程里 | |
| 227 | +的它"这个问题目前没有答案。 | |
| 228 | + | |
| 229 | +如果以后要再碰这条线,思路上可以考虑的方向(都没验证,下次再评估要不要试): | |
| 230 | +- 不整个重启微信主进程,而是想办法在**已经在跑、且刚从后台切到前台**的主进程上 | |
| 231 | + 做非 spawn 式的常规 attach(拿掉"重启"这个变量,只保留"attach"这个变量, | |
| 232 | + 分开验证到底是哪一步不安全)——但存量进程类枚举超时的老问题还在,且同样有 | |
| 233 | + "只是 attach 就把微信弄挂"的风险,不能假设更安全。 | |
| 234 | +- 完全放弃 hook 微信主进程,改成分析 MMTLS 外层协议本身能不能在不进程内注入的 | |
| 235 | + 情况下拿到明文(例如找有没有已知的 MMTLS 密钥导出/降级漏洞——目前没有调研过, | |
| 236 | + 真实存在的可能性未知,可能是死路)。 | |
| 237 | +- **退回"半自动"方案**:人工点发布,工具只负责自动生成图片+文案,不追求端到端 | |
| 238 | + 全自动。今天的两次真实故障之后,这个选项的性价比明显更高了,建议优先考虑。 | |
| 210 | 239 | |
| 211 | 240 | --- |
| 212 | 241 | ... | ... |
wechat_send_internal/spawn_mm_and_hook.py
| ... | ... | @@ -2,8 +2,12 @@ |
| 2 | 2 | # 贴图号发布 hook 的真正入口点是 Ljs1/m;->c(TransferRequestInfo, callback)(见 |
| 3 | 3 | # BRANDSERVICE_PUBLISH_RESEARCH.md §3.5),大概率跑在微信主进程(长驻,不是 |
| 4 | 4 | # spawn-gating 能抓的子进程)。长驻进程 Java.perform 枚举存量类会超时,唯一办法是 |
| 5 | -# force-stop 整个微信、用 frida 重新 spawn 它,在类加载膨胀之前就把钩子挂上 | |
| 6 | -# (跟之前抓 appbrand 子进程用的是同一个技巧,只是这次 spawn 的是整个 App)。 | |
| 5 | +# force-stop 整个微信、用 frida 重新 spawn 它,在类加载膨胀之前就把钩子挂上。 | |
| 6 | +# | |
| 7 | +# v1(spawn-paused 时就 attach+hook+resume)实测会把微信主进程冷启动搞坏 | |
| 8 | +# ("无法连接服务器",见 §3.6)。v2 改成:spawn 后立刻 resume(不在暂停态注入), | |
| 9 | +# 隔 ATTACH_DELAY_SEC 再正常 attach——避开暂停态注入这个可疑触发点,但没验证过 | |
| 10 | +# 是否真的安全,也没验证过延迟这么久还能不能赶在类加载膨胀前挂上钩子。 | |
| 7 | 11 | # |
| 8 | 12 | # 用法: source .venv/bin/activate && python3 spawn_mm_and_hook.py |
| 9 | 13 | import subprocess |
| ... | ... | @@ -13,6 +17,7 @@ import frida |
| 13 | 17 | |
| 14 | 18 | PACKAGE = "com.tencent.mm" |
| 15 | 19 | SCRIPT_PATH = "../wechat_send_internal/hook_transfer_sink_bundled.js" |
| 20 | +ATTACH_DELAY_SEC = 0.8 | |
| 16 | 21 | |
| 17 | 22 | |
| 18 | 23 | def on_message(message, data): |
| ... | ... | @@ -35,14 +40,16 @@ def main(): |
| 35 | 40 | |
| 36 | 41 | print(f"[*] spawning {PACKAGE} (paused) ...") |
| 37 | 42 | pid = device.spawn([PACKAGE]) |
| 38 | - print(f"[+] spawned pid={pid}, attaching...") | |
| 43 | + print(f"[+] spawned pid={pid}, resuming immediately (no injection while paused)...") | |
| 44 | + device.resume(pid) | |
| 45 | + print(f"[*] waiting {ATTACH_DELAY_SEC}s before attaching...") | |
| 46 | + time.sleep(ATTACH_DELAY_SEC) | |
| 47 | + print(f"[*] attaching to pid={pid} ...") | |
| 39 | 48 | session = device.attach(pid) |
| 40 | 49 | script = session.create_script(source) |
| 41 | 50 | script.on("message", on_message) |
| 42 | 51 | script.load() |
| 43 | - print("[+] script loaded, resuming...") | |
| 44 | - device.resume(pid) | |
| 45 | - print(f"[*] {PACKAGE} resumed (pid={pid}). Reopen WeChat on the phone and redo the flow.") | |
| 52 | + print(f"[+] script loaded on running pid={pid}. Reopen WeChat on the phone and redo the flow.") | |
| 46 | 53 | |
| 47 | 54 | try: |
| 48 | 55 | while True: | ... | ... |