2026-07-29 微信 4.1 数据库密钥攻克复盘
背景与目标
本职工作(甬金改扩建项目)有 8 个微信群是信息主战场,靠人肉盯。目标:让 AI 每天自动读取 8 群新消息,按 P0/P1/P2 分级摘要,列入当日清单。前提:能读到微信本地数据库(WCDB/SQLCipher4 加密)。
技术栈:Windows、微信 4.1.11.25(Weixin.dll 184MB)、cdb(WinDbg 命令行)、Python(PE 静态分析、PBKDF2/HMAC 验证、SQLCipher 解密)。
本期工作清单(全面盘点):
| 工作线 | 详略 |
|---|---|
| ① 微信 4.1 密钥攻克(主线) | 详写 |
| ② 群消息自动读取流程(read_groups.py + 定时任务) | 详写 |
| ③ Kimi Code 额度自查工具(kimi_quota_check.py) | 略写 |
| ④ 方向修正:禁用 pull.py、废弃中转站 | 详写(人机协作重点) |
| ⑤ 方案文档同步更新 | 一句话 |
前情:为什么走到解密这条路
7-28 调研结论(当时判定"非注入自动读取路线已穷尽"):
- chatlog / PyWxDump / 留痕:被微信官方律师函,作者删库;
- wechat-dump-rs(4.0 解密):GitHub 全站封锁,fork 也被清空;
- wxauto(UI 自动化):不支持微信 4.0+;
- 注入方案(wx_key DLL):支持 4.1 但有封号风险,工作微信,本人 7-29 否决;
- 内存扫描:4.1 起不再缓存明文密钥,老工具全失效;
- cdb 断 bcrypt!BCryptDeriveKeyPBKDF2(仿 macOS 断 CommonCrypto 的思路):实测零命中——4.1 Windows 自实现 PBKDF2,不走系统 CNG。
7-29 早定稿:解密路线搁置,启用"中转站"保底(本人手动拖文件进文件夹,AI 定时巡检)。
过程回顾(7-29 下午,共 8 轮 cdb)
转折点:本人两条指令
- "什么叫做中转文件夹?还要我自己去把这些信息都放到那个什么中转文件夹里去吗?如果要这个操作,我还需要你干嘛?"——中转站方案被点破反人性,废弃。
- "昨天那种模拟鼠标操作微信的方式,我明令禁止了。解密路线(cdb 断点抓密钥)没攻克,今天继续攻克啊。"——pull.py(UI 拖选复制,7-28 已跑通)同日禁用,方向只剩解密一条。
尝试 1:锚点字符串定位(find_bp_win.py)
Linux 版工具的套路:找 WCDB 特征字符串 com.Tencent.WCDB.Config.Cipher 的代码引用,顺藤摸瓜到密钥处理函数。手工解析 Weixin.dll PE 头(没装 pefile,struct 直接解),锚点在文件偏移 0x8445468(.rdata,映射差 +0x1800)→ RVA 0x8446c68。全 .text 扫 RIP 相对 LEA(48 8D /r,mod=00 rm=101),锚点只有 1 处引用 Weixin+146d7c9——但上下文是 lea rcx,[.data槽]; lea rdx,[键名]; call 注册函数,是配置注册代码,passphrase 不在这。
尝试 2:二级定位,抓配置槽(find_bp_win2.py + cdb_win1)
注册时 rcx = Cipher 配置槽(.data RVA 0xa913958)。扫该槽引用得 4 处(注册 / getter / setter / 变更通知循环)。cdb 托管启动微信下断,命中 A×35、C×111,拿到 32 字节随机值 V1(跨运行稳定,像密钥)。
验证全部失败:V1 当 passphrase,PBKDF2(sha512,6 种迭代次数)× 3 种 HMAC 页布局 × hex/base64/反转/sha256 变换,对 message_0.db 页 1 全部 HMAC 不符。结论:V1 是 WCDB 内部配置参数,不是 passphrase(后被腾讯云社区文章证实)。这是最容易走错的弯路:抓到一个"长得像密钥"的东西,不等于抓到密钥。
尝试 3:硬件断点追 V1 消费点(cdb_win5/6)
ba r4 硬件断点追 V1 被谁读——只追到 memcpy 和 free,没走到加密代码;cdb 脚本块耗尽退出,连带杀掉微信(非反调试,自己玩脱的)。
尝试 4:BCryptGenerateSymmetricKey(cdb_win7)
猜 AES 密钥导入 CNG 句柄时下断——零命中。4.1 连 AES 都是自实现,密钥根本不进 bcrypt 句柄。至此"系统函数断点"这条路彻底排除。
尝试 5:全内存直扫 enc_key(scan_enckey.py)
numpy 预过滤,扫 1.5GB 进程内存约 2000 万个 32 字节候选,逐个 HMAC 验证——无果。佐证公开结论:4.1 派生后的 enc_key 不常驻内存。只剩"在密钥传递/使用的瞬间抓"这一条路。
尝试 6(出货):断"使用点"而非"配置点"(find_bp_win3.py + cdb_win8)
思路反转:不守配置,守使用。SQLCipher 真正用密钥的地方一定会处理 cipher pragma。对一批 .rdata 字符串("aes-256-cbc" ×4、pragma 簇、kdf_iter、passphrase、PBKDF2)做 XREF,定位 4 个断点:Weixin+50fb80f / +50fbd09 / +50fbd54 / +5102de1。
cdb 托管启动,登录后打开数据库时 PRAGMA 点命中约 794 次,每次 dump 寄存器+栈。extract_candidates.py 双模式穷举 dump 里每个 4 字节对齐的 32 字节窗口(既当 passphrase 走 PBKDF2-256000 验证,也直接当 enc_key 验证,高熵预过滤)——段 18 的候选作为 passphrase,PBKDF2-HMAC-SHA512(256000 轮)→ enc_key → message_0.db 页 1 HMAC 校验通过。
收尾:解密与验证
- 24/24 个库 HMAC 全过、全部解密到
.wechat_local/db/(decrypt_all_local.py); - SQL 验证:message_0.db 389 张表(380 张 Msg_* 消息表),抽样 5 张合计 6853 行;contact 表 9579 行;
- 调试器摘除,微信正常重启。
关键踩坑
- 坑 1:假设驱动的断点。现象:bcrypt 断点零命中。根因:照搬 macOS 思路假设 Windows 也调系统密码库,实际 4.1 自实现 PBKDF2/AES。教训:断点前先验证假设(一次零命中就该转向,不该试两轮)。
- 坑 2:配置值 ≠ 密钥。现象:V1 是 32 字节高熵随机值,跨运行稳定,极像密钥,几百种组合验证全失败。根因:WCDB 把 cipher 配置参数和 passphrase 分开存。教训:HMAC 校验是唯一的裁判,没通过校验的"像密钥的东西"一文不值。
- 坑 3:cdb 脚本块耗尽。现象:win5/6 追 V1 时 cdb 退出杀微信。根因:断点命中动作里命令太多。教训:命中动作要精简,批量 dump 靠多次命中积累。
- 坑 4(昨天的):"路线已穷尽"结论下早了。7-28 判定非注入路线穷尽,实际只差一个思路转换(配置点→使用点)。教训:"穷尽"要列出已排除的具体路径,发现还有未试路径就不能说穷尽。
最终方案(已落地)
- 静态定位:find_bp_win3.py 对 cipher pragma 字符串做 XREF → 断点 RVA(绑定 4.1.11.25);
- 抓取:cdb 托管启动微信,登录开库瞬间命中,dump 现场(cdb_script8.txt);
- 验证:extract_candidates.py 双模式穷举 + PBKDF2-HMAC-SHA512(256000)+ 页 1 HMAC——通过才算数;
- 读取:read_groups.py = 重解密实时库(合并 WAL 帧,逐帧解密只应用到最后 commit)+ read_state.json 增量 →
inbox/wechat/<日期>/<群名>.md; - 调度:Windows 计划任务 WeChatGroupPull_1210 / 1747,每工作日两跑;
- 容灾:passphrase 随重新登录轮换 → recapture_passphrase.py 一键重抓(组件全实测,整链路未实测,已如实标注);微信升级 → find_bp_win3.py 改路径重定位。
做得好的地方
- 验证先行:每一轮候选都过 HMAC 校验,宁可否掉 V1 这样的"漂亮候选",不自欺。符合章程铁律一。
- 弯路全部存档:recapture.md"已知坑"一节写死三条(bcrypt 无效、内存扫不到、配置值≠passphrase),防止未来重走。
- 思路转换是胜负手:从"密钥在哪"(配置/内存)转到"密钥在哪被用"(pragma 处理),一轮就出货。
- 子代理隔离脏活:8 轮 cdb 日志(几百 KB)和 PE 分析全在子代理上下文里,主会话只留结论。
人机协作记录(本人指令原话与影响)
- "你可以自己去帮我查看啊,刷一下页面就能读到相关信息的呀"(指 Kimi 控制台额度)→ 产出 kimi_quota_check.py(Edge 打开页面+置前截图+AI 读图),额度自查不再依赖人读。读到:本周用量 7%,频限 1%,Allegro 会员/K3。
- "我不知道你现在做成了什么屌样""如果要这个操作,我还需要你干嘛?"(批中转站)→ 废弃手动投喂方案。教训:保底方案不能以牺牲自动化为代价,那是把成本转嫁给用户。
- "昨天那种模拟鼠标操作微信的方式,我明令禁止了"→ pull.py 禁用,方向收敛到解密。
- "我有账号,有密码,你尽管跟我要,大胆干就好了"→ AI 拒收凭据(安全底线,任何 AI 不该存账号密码验证码),但"一键进入微信无需扫码"这个信息证实了重抓流程可全自动。
- 本人对 AI 的信任授权("大胆干")换来的是杀微信、调试器托管、建系统计划任务全部执行到位;AI 的回报是每一步验证如实报告、未实测的明确标未实测。
复盘与改进
- recapture_passphrase.py 整链路没实测(不忍心再杀一次正在用的微信)——下次重登后第一件事就是跑它验证,验不过按 recapture.md 排障。
- keywords 里含 "@",任何 @人消息都标亮,可能太吵——用两天看信噪比再调。
- 富媒体(图片/文件)只记占位符,解 media 库是后续增强。
- 昨天"路线已穷尽"的判断框架要改:以后说"穷尽"必须附已排除路径清单。
- 效率:8 轮 cdb 约 3 小时,其中尝试 3/4(硬件断点、BCryptGenerateSymmetricKey)可以更早排除——先做一次"该函数是否被调用"的轻验证再投入。
素材点
- "工具全灭之后":4 个开源方案被律师函/下架/封锁,只能自己逆向——AI 时代工具生态的脆弱性。
- 794 次断点命中里捞出 32 个字节,HMAC 校验一锤定音。
- 抓到一个"长得就像密钥"的 32 字节随机值,几百种组合验证全败——配置值≠密钥。
- 用户主动给账号密码,AI 拒收——"我有密码你尽管要" vs "任何 AI 都不该存你的密码"。
- 从"路线已穷尽"到 24 库全解密,只差一个思路转换:别问钥匙放在哪,守在钥匙开门的那一刻。
