乐于分享
好东西不私藏

教 AI 助手"读懂"你的微信:Hermes Agent 对接微信全记录(含 4.x 本地数据库解密)

教 AI 助手"读懂"你的微信:Hermes Agent 对接微信全记录(含 4.x 本地数据库解密)

教 AI 助手"读懂"你的微信:Hermes Agent 对接微信全记录(含 4.x 本地数据库解密)

让 AI 助手帮我总结"今天微信有什么重要消息",听起来是个很自然的需求。但微信没有开放个人消息 API,怎么办?这篇文章记录了我如何一步步让开源 Agent 框架 Hermes 真正"读"到我电脑端微信的聊天记录——从官方机器人对接,到硬啃微信 4.x 加密数据库,再到把它沉淀成一个随叫随到的技能。
一、想解决什么问题

我日常用电脑端微信,消息很多。我希望有一个 AI 助手能:

  • 每天帮我扫一遍微信,挑出真正重要的消息(工作跟进、通知、家人的事);
  • 过滤掉购物群刷屏、广告;
  • 我随时问"昨天 XX 群聊了什么""导出和某人的记录",它能答上来。

难点很直接:微信不给第三方读个人消息的接口。所以路只有两条——

路径
原理
能读到的范围
① 官方机器人(iLink Bot)
以"机器人"身份接入,收发发给它的消息
只有你和 bot 的对话
② 解密本地数据库
解密电脑端微信客户端已同步的 SQLite 库
你的聊天/通讯录/朋友圈/收藏

①能让 AI"在微信里跟你对话",但读不到你和别人的记录;②才能真正读历史。我两条都做了,①是基础,②是重头戏。

⚠️ 先说合规:本文所有操作都只针对你自己账号、你自己电脑上的数据,用于个人备份与整理。请勿用于读取他人数据,尊重隐私。
二、Hermes Agent介绍

Hermes Agent 是 Nous Research 开源的 Agent 框架,可以理解成"可以接各种聊天平台、带工具调用、能装技能(Skill)的 AI 助手运行时"。

我的操作环境:

  • macOS 12(Intel x86_64)——老系统,后面会有坑
  • Python venv 由 Hermes 自带(~/.hermes/hermes-agent/venv)
  • 大模型接的是阿里云百炼(qwen 系列,走 Anthropic 兼容接口)

Hermes 的几个关键概念,后面会反复用到:

  • Gateway(网关):常驻服务,负责接收各平台消息(微信、Telegram 等),由 launchd 托管。
  • api_server:网关内置的 OpenAI 兼容接口,本机 127.0.0.1:8642。
  • Skill(技能):~/.hermes/skills/<分类>/<技能名>/SKILL.md,告诉 AI"遇到 XX 类任务该怎么做"。
  • SOUL.md:~/.hermes/SOUL.md,身份层提示词,会注入到每个会话,优先级很高。
三、第一步:把 Hermes 接进微信(iLink Bot)

Hermes 内置了 weixin 平台,走腾讯 iLink Bot API(长轮询,不是公开 webhook)。

hermes gateway setup# 选 Weixin → 手机微信扫码 → 按提示完成

成功后会在 ~/.hermes/.env 里写入一组 WEIXIN_* 配置:

WEIXIN_ACCOUNT_ID=<你的bot账号>@im.botWEIXIN_TOKEN=<token>WEIXIN_BASE_URL=https://ilinkai.weixin.qq.comWEIXIN_DM_POLICY=allowlist          # 只允许白名单用户私聊WEIXIN_ALLOWED_USERS=<你自己的wxid>WEIXIN_GROUP_POLICY=disabled        # 群聊关掉

重启网关后,你在微信里给这个 bot 发消息,它就是 Hermes 了:

hermes gateway restart

这条路的能力边界要清楚:bot 身份只能收发"发给它"的消息,读不到你和别人的聊天记录,普通群也基本进不去(iLink 侧限制)。所以我把它当成"对话入口",读历史交给第二步。

四、第二步:解密电脑端微信数据库
4.1 微信 4.x 把数据存在哪、怎么加密

电脑端微信的本地数据在这里:

~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/<账号>/db_storage/

下面按功能分目录:message(聊天)、contact(通讯录)、session、sns(朋友圈)、favorite(收藏)……每个 .db 都是 SQLCipher 4 加密库,参数是:

  • AES-256-CBC,HMAC-SHA512
  • PBKDF2 派生,仅 2 轮
  • 页大小 4096,每页保留 80 字节,盐 16 字节

解铃还须系铃人:只要能拿到那把 32 字节的主密钥,就能整库解密。

4.2 老办法失效:内存里扫不到密钥了

以前的工具(以及我一开始的尝试)是扫描微信进程内存,找形如 x'<64位hex密钥><32位hex盐>' 的明文。但 WeChat 4.1.10+ 把这层明文缓存删了,还裁掉了 SQLCipher 符号。我实测内存扫描:

Scan complete: 1241MB scanned, 609 regions, 0 unique keys

一个密钥都没有。路被堵死了。

4.3 新办法:钩住 CommonCrypto,在密钥"过路"时截下它

微信解密每一页数据,最终都要调用系统加密库 CommonCrypto 的 AES 原语(CCCrypt / CCCryptorCreate / CCCryptorCreateWithMode),而那把 32 字节主密钥会作为参数传进去。思路就是:用 lldb 在这几个函数上打断点,把参数里的密钥读出来。

我参考了社区的思路,但原版只支持 ARM64(M 系列芯片)。我的是 Intel x86_64,得按 System V AMD64 调用约定重写寄存器映射:

函数
密钥在哪个寄存器
长度在哪
CCCrypt(op,alg,options,key,keyLength,iv,…)
rcx(第4参)
r8(第5参)
CCCryptorCreateWithMode(op,mode,alg,padding,iv,key,keyLength,…)
r9(第6参)
栈 [rsp+8](第7参)

lldb 钩子脚本核心逻辑(Intel 适配版):

import lldbOUT = "/tmp/hunted_keys.txt"_seen = set()def _log_key(frame, kptr, klen):   if klen != 32 or not kptr:       return   err = lldb.SBError()   data = frame.GetThread().GetProcess().ReadMemory(kptr, 32, err)   if not err.Success() or not data:       return   h = data.hex()   if h in _seen:       return   _seen.add(h)   open(OUT, "a").write(h + "\n")   print("KEY32[%d]: %s" % (len(_seen), h))def on_cccrypt(frame, bp_loc, extra, internal_dict):   # key=rcx, len=r8   _log_key(frame, frame.FindRegister("rcx").GetValueAsUnsigned(),            frame.FindRegister("r8").GetValueAsUnsigned())   return Falsedef on_ccmode(frame, bp_loc, extra, internal_dict):   # key=r9, len=栈[rsp+8]   kptr = frame.FindRegister("r9").GetValueAsUnsigned()   rsp = frame.FindRegister("rsp").GetValueAsUnsigned()   err = lldb.SBError()   raw = frame.GetThread().GetProcess().ReadMemory(rsp + 88, err)   klen = int.from_bytes(raw, "little"if err.Success() and raw else 0   _log_key(frame, kptr, klen)   return False

注意一个前置坑:微信是带官方签名的,直接 attach 会被拒,需要先重签(ad-hoc):

osascript -e 'quit app "WeChat"'; killall WeChat 2>/dev/nullsudo xattr -cr /Applications/WeChat.appsudo codesign --force --deep --sign - /Applications/WeChat.app

然后 attach 并触发(看到 keyhunt armed 后,切回微信,多打开几个聊天、翻翻朋友圈/收藏/通讯录,每出现一行 KEY32[n] 就是抓到一把):

PID=$(pgrep -x WeChat | head -1)sudo lldb -p "$PID" \ -o "command script import /path/to/keyhunt.py" \ -o "keyhunt_start" -o "continue"

最终抓到了 14 把密钥。

4.4 密钥 ↔ 数据库:用第 1 页 HMAC 校验来配对

抓到一堆密钥,怎么知道哪把对应哪个库?用 SQLCipher 的第 1 页 HMAC 校验(参数无关,不依赖能否打开库):

import hashlib, hmac, structPAGE, RESERVE, HMAC = 40968064def mac_key_for(enc_key, salt):   mac_salt = bytes(b ^ 0x3A for b in salt)   return hashlib.pbkdf2_hmac("sha512", enc_key, mac_salt, 2, dklen=32)def page1_ok(enc_key, p1):   salt = p1[:16]   mk = mac_key_for(enc_key, salt)   data = p1[16: PAGE - RESERVE + 16] + struct.pack("<I"1)   return hmac.new(mk, data, hashlib.sha512).digest() == p1[PAGE - HMAC:]

能对上,就说明这把密钥能解这个库。结果:主账号 18 个库匹配上 14 个(聊天、通讯录、朋友圈、收藏等核心库全中;表情、媒体等 4 个库用了别的密钥,暂缺)。

4.5 解密 + 导出

有了密钥,用开源工具(我基于 ydotdog/wechat-export-macos 的解密/导出逻辑)整库解密,产物就是普通 SQLite:

python decrypt_db.py          # 14 成功, 4 失败python export_chat.py --list  # 按消息数列出所有会话

到这一步,数据已经能读了。

五、第三步:把能力"装进"Hermes,随叫随到

光能解密不够,我要的是"我交代一句,Hermes 自己去读"。这里做了三件事,也踩了三个坑。

5.1 工具固化 + 按需流程

我把工具包固定放在 ~/wxdecrypt/,并让 Hermes 每次现取现解密(不做后台同步,只在任务时跑):

# 1. 同步最新加密库rsync -a --prune-empty-dirs --include='*/' --include='*.db' --exclude='*' \ "$HOME/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/<账号>/db_storage/" \~/wxdecrypt/live/# 2. 解密cd ~/wxdecrypt && rm -rf decrypted && python decrypt_db.py# 3. 每日摘要(跨所有聊天按日期统计)python ~/wxdecrypt/today_digest.py
5.2 写成 Hermes Skill

在 ~/.hermes/skills/messaging/wechat-local/SKILL.md 里写清楚"何时用、怎么用、密钥在哪、失效怎么重抓",让 Hermes 任何会话里看到"查微信"类任务都能对上。

5.3 三个注意点

1、加了 Skill 不生效,必须重启网关。Hermes 把"可用技能列表"缓存在进程里,缓存键不含文件修改时间,所以新建的 Skill 不会自动被发现。改完 Skill 或 SOUL.md 后:

hermes gateway restart

2、模型"自认为"读不了微信,直接拒绝。我接的模型有个很强的先验——"微信不开放 API 给第三方"。即使 Skill 已经在可用列表里,它也懒得看,直接回"我无法访问你的聊天记录"。解法:在 SOUL.md(身份层,优先级最高)里明确授权——"你可以通过 wechat-local 读本机微信数据,别声称读不了"。加完后,它不再拒绝了。

3、不拒绝了,但它开始"自由发挥"。授权之后,模型会去读了,却经常不用我给的脚本,而是自己写 python -c / 屏幕抓取之类的野路子,还反复触发危险命令审批。解法有两层:

  1. 把正确命令写死进 SOUL.md,并明确禁止 execute_code/osascript/自写分析代码;
  2. 提供一个确定性的每日摘要脚本 today_digest.py(跨所有聊天、按日期统计、自动过滤压缩消息),模型照抄一条命令就行。
另外有个隐藏关键:如果之前的会话里已经积累了一堆错误尝试,模型会照着旧错误继续错。这时在微信里发 /new 开新会话(记得对确认框回 /approve,否则不会真的重置),再重新问。
六、最终效果

现在我在微信里直接问 Hermes:

我:看看昨天的消息,有没有重要的事情

它会自己跑"同步→解密→摘要",然后给出类似这样的回答(示意):

干净会话里它发挥得很稳:数字准确、会抓重点、会主动过滤噪音。

七、几点经验与提醒
  1. 版本敏感:微信 4.1.x 的密钥策略、Hermes 的缓存行为都可能随版本变。本文基于 WeChat 4.1.11 + Hermes 0.19,思路通用,细节需按你的版本核对。
  2. 老系统有坑:我在 macOS 12(Intel)上,很多新工具/新二进制不兼容,CCCrypt 钩子也要按 x86_64 重写寄存器映射。M 系列芯片请用 ARM64 版。
  3. 审批要分清:正确流程的命令(rsync/decrypt_db.py/today_digest.py)可以放心 /approve always;但 osascript/execute_code/python -c 这种野路子要 /deny。
  4. 合规底线:只解密你自己账号、你自己设备上的数据,用于个人备份与整理。技术本身中性,边界在使用者。
八、写在最后

整个过程最有意思的不是某个具体技巧,而是一个规律:把不稳定的"模型自由发挥",逐步收敛成确定性的"一条命令 + 明确授权"。模型会有先验、会偷懒、会即兴,而我们要做的是用提示词(SOUL.md)、技能(Skill)和脚本,把正确的路铺成阻力最小的那条。

后续还可以把hermes的微信能力设置为一个专职微信助手Profile

涉及工具:Hermes Agent、lldb、SQLCipher、wechat-export-macos 等,排名不分先后,感谢开源社区。

感谢阅读 · 欢迎分享