# WeChat 自动回复项目 — 完整状态报告 > 日期:2026-05-23 > 设备:Pixel 10 Pro Fold > 微信版本:8.0.72 > 环境:Termux + LSPosed (Zygisk) + Frida Server 17.9.10 --- ## 1. 项目目标 在 Pixel 手机上实现微信自动回复守护进程: - **单聊消息** → 自动回复 - **群聊 @我** → 自动回复 - **群聊引用我的消息** → 自动回复 - 运行在 Termux 环境,作为持久 daemon --- ## 2. 已验证可行的部分 ### 2.1 发送消息(完全可用) **方案**:LSPosed 模块 hook `ExtContentProviderBase` 绕过认证,通过 broadcast 触发内部 NetSceneSendMsg API。 **状态**:✅ 已验证,可稳定发送文本消息到任意联系人/群聊。 **触发方式**: ```bash adb shell "am broadcast -a com.hermes.wechat.SEND_MSG --es toUser 'filehelper' --es content 'hello'" ``` **技术细节**: - Hook `ExtContentProviderBase.h()` → 返回 1(绕过权限检查) - Hook `ExtContentProviderBase.b()` → 返回 true(绕过登录检查) - Hook `AESUtil` → 跳过 contact ID 加密 - NetSceneSendMsg 调用链:`u01.r1.a(toUser)` → builder → `builder.e(content)` → `builder.h(0)` → `builder.a()` → `m1.a()`(主线程提交) --- ## 3. 已尝试但不可行的方案 ### 3.1 直接查询 SQLite 数据库(成功 — 已验证) **方案**:通过 `sqlcipher` CLI 直接查询 `EnMicroMsg.db` 的 message 表。 **结果**:✅ 已验证,可直接解密并查询 **关键参数**: - SQLCipher key: `e03ce81` - `PRAGMA cipher_compatibility = 1`(WCDB 兼容模式) - `PRAGMA cipher_page_size = 1024` - 无需复制 WAL(sqlcipher 可直接打开运行中的 DB,但可能遇到 `database is locked`) **使用方式**(在 auto-reply.js 中通过 `AndroidWechatTrackerSQLCipher`): ```bash sqlcipher /data/data/com.tencent.mm/MicroMsg/.../EnMicroMsg.db < ${lastId} AND isSend = 0 AND type IN (1, 10002) ORDER BY msgId ASC; EOF ``` **结论**:SQLCipher 解密成功,已成为主消息检测方案(替代 LSPosed 日志 tail)。 --- ### 3.2 Frida Hook WCDB Java 层 insert(失败) **方案**:Hook `com.tencent.wcdb.database.SQLiteDatabase` 的 `insert()` / `insertWithOnConflict()` / `replace()` / `replaceOrThrow()` 方法。 **结果**:❌ 未捕获到任何 message 表写入 **原因**: - WeChat 8.0.72 的消息写入**没有走这些 Java 层方法** - 可能使用了 WCDB 的 C++ 底层 API(如 `SQLiteStatement`)直接操作 B-tree - 或消息写入发生在 `:push` 子进程而非主进程 --- ### 3.3 Frida Hook Native sqlite3_exec / sqlite3_prepare_v2(失败) **方案**:Hook `libWCDB.so` 中导出的 `sqlite3_exec`(offset 0x29a310)和 `sqlite3_prepare_v2`(offset 0x29a5d0)。 **结果**:❌ 未捕获到任何 message 相关 SQL **原因**: - 虽然 `sqlite3_key` hook 成功并捕获了 key,但 `sqlite3_exec` 在消息收发时**没有被调用** - WeChat 可能使用 WCDB 内部的 C++ API(`Statement::execute()` 等),绕过了标准 C API --- ### 3.4 Frida Hook Notification.Builder(失败) **方案**:Hook `android.app.Notification$Builder.setContentText()` 和 `setContentTitle()` 捕获通知内容。 **结果**:❌ 未触发 **原因**: - 测试时发送消息到 File Transfer Assistant,该场景**不产生通知** - 实际收到外部消息时可能触发,但未验证 - 即使能捕获,通知内容通常被截断,不包含完整消息和群聊信息 --- ### 3.5 ContentProvider 外部查询(失败) **方案**:通过 `adb shell content query --uri content://com.tencent.mm.plugin.ext.message/` 查询消息。 **结果**:❌ Permission Denial **原因**: - Android 系统层的权限检查无法通过 LSPosed 绕过 - 需要 `com.tencent.mm.ext.permission.READ` 权限,adb shell (uid=2000) 没有该权限 --- ### 3.6 WAL 文件监控(失败) **方案**:监控 `EnMicroMsg.db-wal` 文件大小变化来检测新消息。 **结果**:❌ WAL 文件不存在 **原因**: - WeChat/WCDB 可能使用 DELETE 日志模式而非 WAL 模式 - 或 WAL 已被 checkpoint 合并 --- ### 3.7 LSPosed Hook MsgInfo String 方法(失败) **方案**:Hook `com.tencent.mm.storage.f9`(MsgInfo)的所有单参数 String 方法。 **结果**:❌ 方法在消息收发时均不触发 **原因**: - WeChat 8.0.72 的消息处理流程可能已变更,不再通过这些 Java 方法设置字段 - 消息数据可能通过 JNI / 反射 / protobuf 直接构造 --- ## 4. 待验证 / 正在尝试的方案 ### 4.1 LSPosed Hook WCDB insert(当前方向) **方案**:修改 LSPosed 模块,hook `com.tencent.wcdb.database.SQLiteDatabase.insert()`,当表名包含 "message" 时提取 ContentValues 并写入共享文件 `/sdcard/hermes/wechat_msgs.jsonl`。 **状态**:🔄 代码已修改,但**未重新编译 APK** **阻塞**:本地无 Java Runtime,无法编译 APK。需要用户在手机上(Android Studio)或找有 Java 环境的地方编译。 **备注**:此方案在 Frida 中测试时未捕获到写入,但 LSPosed 的 hook 时机和进程覆盖范围与 Frida 不同,仍有验证价值。 --- ### 4.2 Frida Hook :push 进程(待尝试) **方案**:微信消息推送和存储可能发生在 `:push` 子进程(PID 18531)。尝试在 push 进程中 hook WCDB 或 sqlite3 方法。 **状态**:⏳ 待尝试 **风险**:push 进程可能频繁重启,Frida 需要持续 attach。 --- ### 4.3 Android AccessibilityService(备选方案) **方案**:实现无障碍服务,监听微信消息列表界面的内容变化。当收到新消息时,微信 UI 会更新消息列表,AccessibilityService 可以捕获这些文本。 **状态**:⏳ 待评估 **优点**: - 不依赖 hook,稳定性高 - 可以获取发送者昵称、消息内容、群聊名称 **缺点**: - 需要用户手动授权无障碍服务权限 - 需要微信在前台或消息列表可见(或锁屏通知) - 实现较复杂,需要解析 AccessibilityNodeInfo 树 --- ### 4.4 dumpsys notification 轮询(备选方案) **方案**:定期执行 `adb shell dumpsys notification` 提取微信通知内容。 **状态**:⏳ 待验证 **问题**: - 当前 `dumpsys notification` 输出中未找到微信通知的具体内容(标题、文本) - 可能需要 `--noredact` 权限或其他参数 - 通知内容通常被截断,不适合群聊场景 --- ### 4.5 Frida Native Hook sqlite3BtreeInsert(高难度) **方案**:Hook `libWCDB.so` 中更底层的 `sqlite3BtreeInsert` 或 `sqlite3VdbeExec` 函数。 **状态**:⏳ 待研究 **难点**: - 这些函数未导出,需要动态定位 - 签名和参数随 SQLite 版本变化 - 即使捕获到,也需要解析 B-tree 页数据,极为复杂 --- ## 5. 当前状态总结 | 功能 | 状态 | 备注 | |------|------|------| | 发送消息 | ✅ 可用 | LSPosed broadcast(备用) | | 发送消息(Frida) | ✅ 可用 | Frida daemon 队列注入(主推) | | 消息追踪 | ✅ 可用 | SQLCipher DB 轮询(主推) | | 数据库解密 | ✅ 可用 | sqlcipher CLI + WCDB 兼容参数 | | 自动回复 daemon | ✅ 可用 | auto-reply.js 主流程已跑通 | | 息屏唤醒 | ✅ 可用 | notification-list 兜底 + 自动亮屏解锁 | **当前主推方案**:Node.js plugin (`auto-reply.js`) - 消息检测:`AndroidWechatTrackerSQLCipher`(sqlcipher 轮询) - 回复生成:`callHermesApi()` → `hermes-agent` OpenAI-compatible API - 消息发送:`sendTextMessageFrida()`(Frida daemon 队列) - 息屏兜底:`pollNotifications()`(通知监听 → 唤醒微信) --- ## 6. 下一步计划 ### 已完成(当前主流程) 1. **SQLCipher DB 轮询** — `AndroidWechatTrackerSQLCipher` 已实现并验证,替代 LSPosed 日志 tail 2. **Frida Daemon 发送** — `sendTextMessageFrida` 已实现并验证,替代 am broadcast(绕过 Android 10+ 后台限制) 3. **hermes-agent API** — `auto-reply.js` 已切换到 OpenAI-compatible `/v1/chat/completions` 4. **息屏自动唤醒** — 通知监听 + `wakeWechat()` 点亮/解锁/启动微信流程已验证 ### 待优化 1. **息屏发送稳定性** — 当前息屏时 Frida daemon 可能因微信进程被冻结而失效,需要进一步验证 2. **群聊 @识别精度** — 依赖昵称匹配,考虑加入 wxid 匹配 3. **Frida daemon 持久化** — 目前需要手动注入,考虑 Termux:Boot 自动注入 4. **消息去重** — 息屏唤醒后 tracker 可能捕获到已处理过的消息,需优化 state 管理 5. **本地 LLM 部署** — 减少 API 延迟和网络依赖 --- ## 7. 关键发现记录 ### 7.1 SQLCipher Key - 捕获到的 key:`e03ce81`(7 字节) - 对应数据库路径:`/data/user/0/com.tencent.mm/MicroMsg/653cdc2320abb089e149a3f7a7683fcf/EnMicroMsg.db` - 该 key 无法通过 sqlcipher CLI 或 Frida 直接调用解密 ### 7.2 libWCDB.so 导出函数偏移 - `sqlite3_open`:0x29fe1c - `sqlite3_open_v2`:0x2a03f8 - `sqlite3_key`:0x2687d8 - `sqlite3_key_v2`:0x26888c - `sqlite3_exec`:0x29a310 - `sqlite3_prepare_v2`:0x29a5d0 - `sqlite3_close`:0x29fbcc - `sqlite3_errmsg`:0x29efd8 ### 7.3 NetSceneSendMsg 调用链(WeChat 8.0.72) ``` u01.r1.a(toUser) // 创建 builder builder.e(content) // 设置消息内容 builder.h(0) // 设置 subtype builder.a() // 构建 NetSceneSendMsg 对象 m1.a() // 在主线程提交到 NetSceneQueue ``` ### 7.4 进程结构 - 主进程:`com.tencent.mm`(PID 18340)— UI + 消息发送 - Push 进程:`com.tencent.mm:push`(PID 18531)— 消息接收 + 推送 - AppBrand 进程:`com.tencent.mm:appbrand0/1` --- ## 8. 待决策事项 1. **LSPosed 模块重新编译**:用户是否有 Android Studio 环境或可以安装 JDK? 2. **AccessibilityService 方案**:用户是否接受手动授权无障碍权限? 3. **:push 进程 hook**:用户是否愿意让 Frida 持续运行在后台? 4. **降级方案**:如果以上均不可行,是否接受仅检测"有新消息"而不获取内容的简化版?