hermes_task.md 6.79 KB

任务:修通 WeChat Xposed 内部发送接口

🔒 安全约束(不得违反)

只能用 filehelper(文件传输助手)测试,绝对不能给真实联系人发消息。 测试命令固定格式:

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 在用户点「发送」按钮时抓调用栈:

# 在 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 分析脚本

# 在 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 步:编译 + 安装 + 测试

# 修改 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 是发送入口)可能都是错的。需要从真实调用栈重新定位。


输出期望

完成后,以下命令应该能让文件传输助手收到消息:

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 端同步。