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