Files
notiMessage/docs/HOOK_GUIDE.md
Mars 125dfe583b feat: Telegram 双通道抓取、PC 调试台与 Hook 后台转发修复
v2.2.1 更新说明:

- 新增 xposed-module(Telegram/微信/SQLite Hook),双 APK + LSPosed 作用域

- HookMessageReceiver 后台直接 DebugForwarder + goAsync,修复 notiMessage 退后台丢消息

- MessageLogStore 日志持久化;AppConfig 调试/上传开关;PC 调试台 debug-server

- 健康检查去掉联网限制;通知/Hook 通道增加诊断日志

- 安装脚本 install-full/configure-lsposed/start-debug-server;文档 CHANGELOG + HOOK_GUIDE
2026-07-02 16:50:12 +08:00

11 KiB
Raw Blame History

消息抓取架构与 Hook 扩展指南

本文说明 notiMessage 的双通道抓取机制、Telegram 的实现方式,以及日后接入其他 App 的步骤。


1. 总体架构

notiMessage 使用 两条独立通道 抓取消息,互为补充:

┌─────────────────────────────────────────────────────────────────┐
│                        目标 App如 Telegram                    │
└───────────────┬─────────────────────────────┬───────────────────┘
                │                             │
     后台/锁屏弹系统通知              前台打开 App、消息入库/解码
                │                             │
                ▼                             ▼
   NotificationListenerService          Xposed Hook 模块
   (NotificationService)              (xposed-module)
                │                             │
                │                             │ Broadcast
                │                             ▼
                │                    HookMessageReceiver
                │                      │ goAsync()
                │                      ├─ MessageLogStore立即写入
                │                      ├─ DebugForwarder直接转发 PC
                │                      └─ submitFromHook仅正式上传模式
                │                             │
                └──────────────┬──────────────┘
                               ▼
              NotificationService.submitNotification()(通知通道)
                               │
              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
      MessageLogStore    DebugForwarder    (可选)正式后端上传
      App 本地日志)    PC 调试台)      AppConfig.ENABLE_SERVER_UPLOAD
通道 触发条件 代码入口 日志前缀
通知监听 目标 App 在后台且弹出系统通知 NotificationService.onNotificationPosted() 无前缀
Hook 目标 App 进程收到新消息(通常在前台) HookMessageReceiver → 直接 DebugForwarder [Hook/xposed_telegram]

重要:仅开通知监听时,目标 App 在前台通常 不会 产生系统通知,因此抓不到消息。要覆盖前台场景,必须启用 Xposed Hook。


2. Telegram 实现方式

2.1 包名与作用域

Pixel 6 等设备上 Telegram 常见包名:

安装来源 包名
Play / 官网 APK org.telegram.messenger
部分渠道 / Web 版 org.telegram.messenger.web

LSPosed 作用域需勾选 目标 Telegram 包名 + notiMessage 主 App

默认作用域见 xposed-module/src/main/res/values/arrays.xml

2.2 为何不 Hook SQLite

Telegram 消息存在 SQLite 的 data 字段(加密二进制 TL 序列化),不是明文 content/text

通用 SqliteMessageHook 只能读明文列,对 Telegram 无效。因此单独实现 TelegramMessageHook

2.3 Hook 点

主路径Hook NotificationCenter.postNotificationName(int, Object[])

  • 读取静态字段 NotificationCenter.didReceiveNewMessages 作为事件 ID
  • id == didReceiveNewMessages 时,从参数里的 List<MessageObject> 取新消息
  • 只处理 非 outgoingmessageOwner.out == false)的消息

兜底路径Hook MessageObject 全部构造函数(主路径失败时启用)

源码位置:xposed-module/.../hook/TelegramMessageHook.java

2.4 字段提取逻辑

字段 提取方式 用途
群名/会话名 MessagesController.getPeerTitle(dialogId)MessageObject.getName()getChat().title 分组标题、转发 group
发送者 MessageObject.getFromName() 群内消息前缀 发送者: 内容
正文 messageTextmessageOwner.messagecaption 文本内容
图片+说明 识别 [图片] 等媒体标签 + 合并 caption 避免只显示「图片」丢失说明
dialogId MessageObject.getDialogId() 去重键 dialogId:messageId

注意:不要把 messageOwner.from_idTLRPC$TL_peerUser 对象)直接 toString() 当标题,会出现 TLRPC$TL_peerUser@xxxx

2.5 转发到主 App

Hook 模块通过 HookForwarder 发送广播:

Action: com.miraclegarden.smsmessage.action.HOOK_MESSAGE
Package: com.miraclegarden.smsmessage
Extras: packageName, title, content, timestamp, source=xposed_telegram

主 App 的 HookMessageReceiver 接收后:

  1. 检查该 packageName 是否在监听列表(App.getMessageByNotiList
  2. 写本地日志(MessageLogStore,立即持久化)
  3. 直接转发 PCDebugForwarder,不依赖 startService,后台可用)
  4. 若开启正式上传(ENABLE_SERVER_UPLOAD),再经 submitFromHook 交给 NotificationService

3. 主 App 相关模块

文件 职责
NotificationService.java 通知监听 + 统一提交入口
HookMessageReceiver.java 接收 Xposed 广播
MessageLogStore.java 日志持久化(后台也能保留历史)
DebugForwarder.java POST 到 PC 调试服务
AppConfig.java ENABLE_DEBUG_FORWARDENABLE_SERVER_UPLOAD 开关

PC 调试台

# 启动本地服务(含 adb reverse
powershell -ExecutionPolicy Bypass -File scripts\start-debug-server.ps1
# 浏览器打开 http://127.0.0.1:8765 ,按群/会话分组展示

4. 日后接入其他 App 的步骤

4.1 先判断用哪种 Hook 策略

目标 App 前台消息是否需要抓取?
├─ 否 → 仅通知监听即可,在 App「监听设置」里添加包名
└─ 是 → 需要 Xposed Hook
         │
         ├─ 消息以明文写入 SQLitecontent/text/body 等)?
         │   └─ 是 → 可复用 SqliteMessageHook默认兜底
         │
         ├─ 微信?
         │   └─ 是 → 已有 WeChatMessageHookHook WCDB message 表)
         │
         └─ 其他Telegram、WhatsApp、银行 App 等)
             └─ 需编写 **专用 Hook 类**

4.2 新增专用 Hook 的标准流程

以新 App com.example.chat 为例:

步骤 1分析目标 App

  1. adb shell pm path <包名> 拉出 APK
  2. jadx / dexdump 找消息解码后的类(类似 Telegram 的 MessageObject
  3. 确认:消息明文出现在哪个字段、哪个时机(构造函数 / 事件总线 / 通知回调)

步骤 2编写 Hook 类

xposed-module/src/main/java/.../hook/ 新建 ExampleChatMessageHook.java

public final class ExampleChatMessageHook {
    public static void install(XC_LoadPackage.LoadPackageParam lpparam) {
        // 1. findClass + findAndHookMethod / hookAllConstructors
        // 2. 提取 title群名、content正文、过滤 outgoing/系统消息
        // 3. 去重messageId + dialogId
        // 4. HookForwarder.forward(context, lpparam.packageName, title, content, "xposed_example");
    }
}

HookBridge.java 增加 source 常量,例如 SOURCE_XPOSED_EXAMPLE = "xposed_example"

步骤 3注册到 MainHook

编辑 MainHook.java

if ("com.example.chat".equals(lpparam.packageName)) {
    ExampleChatMessageHook.install(lpparam);
    return;
}

步骤 4更新 LSPosed 作用域

编辑 xposed-module/src/main/res/values/arrays.xml

<item>com.example.chat</item>

步骤 5主 App 添加监听

在 notiMessage「监听设置」中添加该 App或通过 shared_prefs/server.xmlsaveNotiList)。

bankInfoId 本地调试可用 local:<包名>

步骤 6构建、安装、验证

# 完整安装(主 App + Xposed + LSPosed 作用域)
powershell -ExecutionPolicy Bypass -File scripts\install-full.ps1

验证清单:

  • LSPosed 中模块已启用,作用域勾选 目标 App + notiMessage
  • 强制停止并重启目标 App让 Hook 重新加载)
  • notiMessage 监听控制台「开始监听」
  • 后台场景:收消息 → 通知通道有日志
  • 前台场景:收消息 → [Hook/xxx] 日志 + PC 调试台有数据
  • adb logcat | grep notiMessageHook 可见 installed for <包名>

4.3 复用通用 SqliteMessageHook 的条件

SqliteMessageHook 会在 MainHook 中作为 默认兜底 安装(非微信、非 Telegram 时)。

适用 App 特征:

  • 使用标准 SQLiteDatabase / FrameworkSQLiteDatabase
  • 表名含 message / messages
  • ContentValues 中有明文列:contenttextbodymsg

不适用Telegram、WhatsApp、Signal 等端到端加密或二进制 data 列的 App。

4.4 已有专用 Hook 参考

App Hook 目标
微信 WeChatMessageHook com.tencent.wcdb.database.SQLiteDatabase insertmessage
Telegram TelegramMessageHook NotificationCenter.didReceiveNewMessages + MessageObject
其他 SqliteMessageHook 通用 SQLite insert 兜底

5. 构建与安装

脚本 用途
scripts/build-debug.ps1 构建主 App + Xposed 双 APK
scripts/install-debug.ps1 仅安装双 APK
scripts/install-full.ps1 推荐:构建 + 双 APK + LSPosed 作用域 + adb reverse + 电池白名单
scripts/start-debug-server.ps1 启动 PC 调试台

注意Android Studio 点 Run 只安装 :app 主模块,不会安装 Xposed 模块。完整功能必须用 install-full.ps1 或手动安装两个 APK。


6. 常见问题

现象 原因 处理
前台有消息App 无日志 仅通知通道Hook 未生效 检查 LSPosed 作用域、重启目标 App
标题显示 TLRPC$... 误把对象当字符串 getPeerTitle 等 API见 TelegramMessageHook
图片消息只有「图片」 说明在 caption 字段 合并 caption + 媒体标签
PC 有数据App 无历史 旧版日志未持久化 已用 MessageLogStore 修复,更新主 App
Hook 日志前缀 [Hook/...] Xposed 未注入 确认 Magisk + LSPosed + 模块启用

7. 相关文件索引

xposed-module/
├── src/main/assets/xposed_init          # 入口 MainHook
├── src/main/java/.../MainHook.java      # 包名路由
├── src/main/java/.../HookForwarder.java # 广播转发
├── src/main/java/.../hook/
│   ├── TelegramMessageHook.java
│   ├── WeChatMessageHook.java
│   └── SqliteMessageHook.java
└── src/main/res/values/arrays.xml       # xposed_scope 默认作用域

app/
├── src/main/java/.../service/
│   ├── NotificationService.java
│   └── HookMessageReceiver.java
├── src/main/java/.../MessageLogStore.java
├── src/main/java/.../network/DebugForwarder.java
└── src/main/java/.../AppConfig.java

debug-server/server.py                   # PC 调试台
scripts/install-full.ps1                 # 一键完整部署