feat: database-backed message model with multi-account key cache
- Add Windows 4.1.x Config.Cipher pointer-chain key extraction with cross-chunk
MemoryPatternScanner and bounded blob XOR decoding; keys still require per-DB
page-1 HMAC verification before caching.
- Cache keys by account fingerprint + database relative path; db scan --save
skips memory scanning entirely once all discovered databases are cached.
- Add DbMessage model (localId, serverId, type, timestamp, sender, display name,
avatar, isSelf) read read-only from Msg_{md5(chat)} via SQLCipher with hex/zstd
content decoding and contact.db name resolution.
- CLI: db messages / db contacts / db schema with masked defaults; include the
earlier chat send --session wiring. 97 Core tests pass; real-machine validated.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# 完整消息模型:只读数据库消息读取与多账号密钥缓存(2026-09-06)
|
||||
|
||||
## 目标与边界
|
||||
|
||||
- 本阶段补齐「完整消息模型」:稳定消息 ID、发送者、时间戳、收发方向等 UIA 无法提供的字段,全部来自只读 SQLCipher 数据库。
|
||||
- 不写数据库、不注入、不修改微信进程、不做登录绕过;密钥只通过页面 1 HMAC 校验后缓存。
|
||||
- 一个机器多账号:密钥按「账号指纹 + 库相对路径」缓存,已缓存账号后续 `db scan` 直接跳过内存扫描。
|
||||
|
||||
## 密钥提取(Windows 微信 4.1.13.63)
|
||||
|
||||
- 旧式明文十六进制扫描(ASCII/UTF-16)在该版本找不到可验证密钥。
|
||||
- 新增 Windows 4.1.x 的 `Config.Cipher` 只读指针链扫描:
|
||||
- 全内存找 `com.Tencent.WCDB.Config.Cipher` 字符串地址;
|
||||
- 用字符串地址+长度组成的 16 字节对再次全内存定位对象节点(`节点基址 = 配对地址 - 0x10`);
|
||||
- 沿节点 `+0x28 → 配置指针 +0x88 → 数据指针 +0x8 / 长度 +0x10` 读回最多 1024 字节的加密配置块;
|
||||
- 固定 33 字节 XOR 掩码解码,提取 `x'<64~192 hex>'` 字面量,候选密钥要求 ≥15 个不同字节且非全 0/全 FF;
|
||||
- 最终仍以每个目标库页面 1 HMAC 校验为准。
|
||||
- `MemoryPatternScanner` 处理跨分块/跨区边界匹配;所有读出的进程内存缓冲均清零。
|
||||
- 扫描结果:5 个进程、19,309 个候选、149,800 次校验 → 20 个数据库全部 `PageHmacVerified`,来源进程 8912。
|
||||
|
||||
## 多账号缓存
|
||||
|
||||
- `db scan --save` 先加载密钥缓存,计算「发现库 − 已缓存库」;无待办则 `scanned=false` 直接跳过(processCount/candidateCount=0)。
|
||||
- 第二次 `db scan --save` 验证:`scanned=false`,缓存仍 20 个库,无需重扫。
|
||||
- 缓存键大小写不敏感(Windows 路径);新账号/新库出现时才触发增量扫描。
|
||||
|
||||
## 消息模型与读取
|
||||
|
||||
- 消息表名:`Msg_{md5(chat_wxid)}`;在 `message/message_*.db`、`message/biz_message_*.db` 中定位。
|
||||
- 只读查询列:`local_id, server_id, local_type, create_time, hex(message_content), WCDB_CT_message_content, real_sender_id`,`LEFT JOIN name2id` 得发送者 wxid。
|
||||
- 内容解码:hex → UTF-8;压缩标记且带 zstd 魔数 `28 b5 2f fd` 时用 zstd 解压(ZstdSharp.Port,Core 内跨平台)。
|
||||
- 发送者显示名/头像:`contact/contact.db` 的 `contact` 表按 wxid 批量查 `remark → nick_name → username` 与 `small_head_url`。
|
||||
- 收发方向:账号根目录名 `<wxid>_<random>` 前缀匹配发送者即 `IsSelf=true`(AccountRootPath 指向 `db_storage`,其父目录名才是账号目录)。
|
||||
- CLI:`db messages --account <fp> --chat <wxid> [--limit] [--include-content]`、`db contacts`、`db schema`;默认脱敏,`--include-content` 才显示名称/内容/头像。
|
||||
|
||||
## 真机验收
|
||||
|
||||
- Windows 10.0.19044,微信 4.1.13.63,原交互会话 1(已解锁),唯一密钥缓存。
|
||||
- 群聊名解析:`db contacts --contains 测试` 命中 `53271859539@chatroom`,昵称「消息测试专用群组」。
|
||||
- 消息读取:`db messages --chat 53271859539@chatroom --limit 6 --include-content` 返回 6 条:
|
||||
- 每条含 `local_id`(7–12)、`server_id`、`type=1`、RFC3339 时间戳、发送者、`IsSelf`、显示名、头像、内容。
|
||||
- 内容正是此前本机发送的测试标记(`wx-agent-isolation-*`、`wx-agent-repeat-*`、`wx-agent-m6-listener-*`),证明 hex/zstd 解码正确。
|
||||
- 本机发送消息 `IsSelf=true`、`senderName=Rogee`;发送者名/头像经 contact.db 解析。
|
||||
- Core:97/97 测试通过;Release 全量构建与 self-contained 发布 0 警告 0 错误。
|
||||
- 密钥文件位于 `%LOCALAPPDATA%\WxAgent\database-keys.json`,ACL 仅当前用户;CLI 输出不含密钥。
|
||||
|
||||
## 尚未完成(本阶段之外)
|
||||
|
||||
- 通用发送能力、导航能力、合并聊天记录展开仍按顺序推进。
|
||||
- 消息模型与 UIA 事件流的合并(实时新消息方向归属)不在本阶段。
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"count": 3,
|
||||
"contacts": [
|
||||
{
|
||||
"username": "gh_dc239843078a",
|
||||
"nickName": "微信公众平台测试号",
|
||||
"remark": "",
|
||||
"avatarUrl": "https://wx.qlogo.cn/mmhead/574VdhMFwaFfLImg5A1CEvy3u7jvATicibOlBqb9a5FgKISWE85CAGhlPTibpU3rfMLGLRFmmovzpc/132"
|
||||
},
|
||||
{
|
||||
"username": "18730202243@chatroom",
|
||||
"nickName": "云阵测试",
|
||||
"remark": "",
|
||||
"avatarUrl": "https://wx.qlogo.cn/mmcrhead/K6CEv0Hv9DczUu4XxF8icuZibYCickibJfpCXduwlWxadSHtnZaEgn5OKOb1h0XUKpibpkdvYW2JUSbykhFqia1FBh94QdYGRS5tuX/0"
|
||||
},
|
||||
{
|
||||
"username": "53271859539@chatroom",
|
||||
"nickName": "消息测试专用群组",
|
||||
"remark": "",
|
||||
"avatarUrl": "https://wx.qlogo.cn/mmcrhead/RK2M1rZicPTL4icMaj0lSTwn7rxN2dO7DN3ubxb2zTMhZD7wfEe2vAfcoGoD3TajaWXlcIPBA7ibHo/0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"chatId": "53271859539@chatroom",
|
||||
"count": 6,
|
||||
"messages": [
|
||||
{
|
||||
"LocalId": 12,
|
||||
"ServerId": 3948630831403203690,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-06T02:59:00.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "wx-agent-isolation-b141279dd42c4a0889db0fee566eed13"
|
||||
},
|
||||
{
|
||||
"LocalId": 11,
|
||||
"ServerId": 5851996799725995532,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-06T02:58:54.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "wx-agent-repeat-cdd63033b3a7461faa7d50ae983a1136"
|
||||
},
|
||||
{
|
||||
"LocalId": 10,
|
||||
"ServerId": 5200515901135764260,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-06T02:58:52.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "wx-agent-repeat-cdd63033b3a7461faa7d50ae983a1136"
|
||||
},
|
||||
{
|
||||
"LocalId": 9,
|
||||
"ServerId": 4464958670805689554,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-06T02:58:42.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "wx-agent-m6-listener-de9a21ab58f84218b281742c652b46ce"
|
||||
},
|
||||
{
|
||||
"LocalId": 8,
|
||||
"ServerId": 1416057757091682053,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-05T17:36:15.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "wx-agent-m6-listener-62b19f3a1a63490ba0f2c6a47cc6e9ab"
|
||||
},
|
||||
{
|
||||
"LocalId": 7,
|
||||
"ServerId": 4961661980547358864,
|
||||
"Type": 1,
|
||||
"timestamp": "2026-09-04T15:00:15.0000000+00:00",
|
||||
"sender": "yanghao880130",
|
||||
"IsSelf": true,
|
||||
"senderName": "Rogee",
|
||||
"senderAvatarUrl": "https://wx.qlogo.cn/mmhead/ver_1/kPXMiaO9asy0mdTLsl3xibfcOw7K4VExiaIbQs5ytR43bViakVCgEeR5G6h3icHSombLUzS3DGpMkLKRycyzIiakh0picIj32E1SPb8JHJ939Syxx5ic4PPoPkvj6KOtNGP9qWuH0h49lCbH6qz34nP8evrjxQ/132",
|
||||
"content": "@所有人 HELLO AL"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"scanned": false,
|
||||
"processCount": 0,
|
||||
"candidateCount": 0,
|
||||
"verificationAttempts": 0,
|
||||
"verifiedDatabaseCount": 20,
|
||||
"savedTo": "C:\\Users\\Rogee\\AppData\\Local\\WxAgent\\database-keys.json",
|
||||
"accounts": [
|
||||
{
|
||||
"AccountRootFingerprint": "a2e8a1eaab7fd5fbd3806525c0d9cce750c28f4efb8e7cfcabc9e3e8660e0277",
|
||||
"WechatVersion": "4.1.13.63",
|
||||
"databases": [
|
||||
{
|
||||
"RelativePath": "bizchat/bizchat.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9099054+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "chatbot/chatbot_message.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9094985+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "contact/contact.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9096969+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "contact/contact_fts.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9094345+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "emoticon/emoticon.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9095737+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "favorite/favorite.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9098057+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "favorite/favorite_fts.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9097808+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "general/general.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9096221+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "hardlink/hardlink.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9097495+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "head_image/head_image.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9093256+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/biz_message_0.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9098307+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/media_0.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9095265+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/message_0.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.909675+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/message_fts.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9095985+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/message_resource.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9095507+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "message/weclaw.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9098845+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "session/session.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9094761+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "sns/sns.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9097239+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "solitaire/solitaire.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.9098598+00:00"
|
||||
},
|
||||
{
|
||||
"RelativePath": "third_app_icon/third_app_icon.db",
|
||||
"SourceProcessId": 8912,
|
||||
"Confidence": "PageHmacVerified",
|
||||
"VerifiedAt": "2026-09-06T05:17:37.909648+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user