# 任务:修通 WeChat Xposed 内部发送接口 ## 🔒 安全约束(不得违反) **只能用 `filehelper`(文件传输助手)测试,绝对不能给真实联系人发消息。** 测试命令固定格式: ```bash su -c 'am broadcast -a com.hermes.wechat.SEND_MSG_INTERNAL --es to "filehelper" --es text "测试"' ``` --- ## 目标 让 LSPosed 模块 `com.example.wechathook` 能通过 broadcast 真正把消息发进微信 message 数据库(EnMicroMsg.db),在文件传输助手收到消息。 设备:Pixel 10 Pro Fold,Android 16,WeChat 8.0.74,LSPosed JingMatrix Vector v1.11.0。 --- ## 工作目录 ``` /sdcard/wechathook/ WeChatHook.java ← 当前源码(已是最新版) termux_build.sh ← Termux 上编译+安装脚本 analyze_wechathook.py ← DEX 字节码分析工具(Python,读 Tinker patch) XposedBridgeApi.jar ← 编译依赖 android.jar ← 编译依赖(API 36) d8.jar ← DEX 转换工具 wechathook_current.apk ← 当前安装版本 ``` --- ## 当前状态(已做了什么、失败在哪) ### 已实现 模块已注入所有 WeChat 进程(main / push / appbrand0 / appbrand1)。每个进程里: 1. 注册了 `com.hermes.wechat.SEND_MSG_INTERNAL` broadcast receiver 2. 挂钩了 `w11.r0` 的 4 种构造器((String,String,int,int,long) 等) 3. 挂钩了 `w11.r0.H(String)`(发送 dispatch 方法) 4. 挂钩了 `gm0.j1.g(km0.c)`(DI 内核初始化检测) 5. 挂钩了 WCDB `insertWithOnConflict`(诊断用) ### 关键发现(失败原因) **发现 1:`w11.r0` 构造器永远不被 WeChat 自己调用。** 用户在 UI 点发送时,`w11.r0` 的构造器 hook 从未触发过。说明 WeChat 8.0.74(Tinker patch 版)的文字发送路径**根本不经过 `w11.r0` 这个类**,或走的是完全不同的入口(native / 另一个类名)。 **发现 2:WCDB Java hook 从未触发。** `com.tencent.wcdb.database.SQLiteDatabase.insertWithOnConflict` 在发消息时从未被调用。说明消息写库走的是**纯 native 路径**(JNI),绕开了 Java 层的 WCDB wrapper。 **发现 3:`gm0.j1.g(km0.c)` hook 注册成功但从不触发。** WeChat 正常运行(包括收发消息)期间,`gm0.j1.g()` 这个我们以为是 DI 初始化点的方法**从未被调用过**。说明 WeChat 用了另一条路初始化 DI 内核(或者混淆后方法名已变)。 **发现 4:我们调用 `w11.r0(String,String,int,int,long)` 时:** - main 进程:`NullPointerException`(某个 Context 对象是 null) - 其他进程:`iz5.b: Kernel not initialized by MMApplication!`(`gm0.j1.i()` 抛出,说明 `gm0.j1.m` 静态字段是 null) --- ## 需要你做什么 ### 第 1 步:用 Frida 找 WeChat 真正的发送路径(最重要) 我们不知道 WeChat 8.0.74 Tinker 版实际用哪个 Java 方法发文字消息。用 Frida 在用户点「发送」按钮时抓调用栈: ```python # 在 Mac 端 Frida bridge 调用,或者直接在设备上用 frida-ps / frida-trace # 目标:com.tencent.mm(主进程) # 找到发送文字消息时调用的 Java 方法 ``` 具体建议:hook `com.tencent.wcdb.database.SQLiteDatabase` 的 native 层,或者用 Frida 的 Java.enumerateMethods 搜含 "sendMsg" / "sendTextMsg" 关键词的方法,在用户点发送时看 logcat。 如果 Frida 不方便,另一条路是用 Xposed hook `android.os.MessageQueue.next()` + 过滤调用栈,找 WeChat 发消息时进入的第一个 WeChat 内部类。 ### 第 2 步:运行 DEX 分析脚本 ```bash # 在 Termux 里(需要 root) python3 /sdcard/wechathook/analyze_wechathook.py ``` 这个脚本会从 Tinker patch 的 classes12.dex 里提取: - `gm0.j1` 的所有字段和方法签名 - `w11.r0` 的所有构造器签名 - `gm0.j1.i()` 的字节码(关键调用) - `gm0.j1.g()` 的字节码(看它究竟做了什么) 看输出里 `gm0.j1.i()` 的字节码,找它检查的是哪个字段(sget-object 指令);找谁会真正设置那个字段(sput-object 指令在哪个方法里)。 ### 第 3 步:根据发现修改 WeChatHook.java 修改路径两选一: **路径 A(推荐):换用 WeChat 真实的发送方法** 如果 Frida 或字节码分析找到了真实发送入口(比如 `xxx.sendTextMessage(talker, text)` 这种),直接在 hook 里反射调用它,完全绕开 `w11.r0`。 **路径 B:修复 DI 初始化** 找到真正初始化 `gm0.j1.m` 的方法(替代 `gm0.j1.g()`),hook 那个方法,在它完成后触发发送。 ### 第 4 步:编译 + 安装 + 测试 ```bash # 修改 WeChatHook.java 后,在 Termux 里: cd /sdcard/wechathook bash termux_build.sh # 脚本会自动:编译 → DEX → 签名 → pm install -r → 更新 LSPosed DB # 完成后重启 WeChat(脚本末尾会提示命令) # 然后测试(只用 filehelper): su -c 'am broadcast -a com.hermes.wechat.SEND_MSG_INTERNAL --es to "filehelper" --es text "测试123"' # 看 LSPosed 日志确认有没有走到 Send OK: su -c 'logcat -d -s WeChatHook:* WeChatHookEarly:* | tail -50' ``` --- ## 关键类名参考(WeChat 8.0.74 Tinker patch,classes12.dex) | 类 | 作用 | |---|---| | `gm0.j1` | DI 容器单例;`i()→Lgm0/j1;` 取单例;`m` 是静态字段 | | `km0.c` | WeChat kernel/application 对象,传给 `gm0.j1.g()` | | `gm0.b0` | mCoreStorage,从 `gm0.j1.u()` 取 | | `w11.r0` | 疑似 NetSceneSendMsg,但 WeChat 自己不走这里 | | `iz5.b` | 异常类:Kernel not initialized by MMApplication! | | `fs.g` | DI enum;`a[0]` 是当前实现 | | `fs.k2` | DI 实现枚举;`e` 是目标值 | Tinker patch APK(root 权限才能访问): ``` /data/user/0/com.tencent.mm/tinker/patch-cf87f687/dex/tinker_classN.apk ``` 包含 classes2.dex ~ classes17.dex,关键类在 classes12.dex。 --- ## 当前 WeChatHook.java 的核心逻辑(方便你快速理解) ``` broadcast 到达 → trySendWithRetry(talker, text, 0, procName) → isDIReady()? 调 gm0.j1.i() 看是否抛异常 → 如果不 ready:挂 pending,等 gm0.j1.g() hook 触发(但 g() 从没触发过!) → 如果 ready:sendViaR0(talker, text) → new w11.r0(talker, text, 1, 0, ts) ← 在所有进程都失败 → r0.H("") ← 从没执行到 ``` **关键 bug**:整条路的前提假设(`gm0.j1.g()` 是初始化点,`w11.r0` 是发送入口)可能都是错的。需要从真实调用栈重新定位。 --- ## 输出期望 完成后,以下命令应该能让文件传输助手收到消息: ```bash su -c 'am broadcast -a com.hermes.wechat.SEND_MSG_INTERNAL --es to "filehelper" --es text "hello from hermes"' ``` LSPosed 日志里应该看到 `[WeChatHook] Send OK to=filehelper`。 把修改后的 `WeChatHook.java` 回写到 `/sdcard/wechathook/WeChatHook.java`,方便 Mac 端同步。