wechat-db-decrypt-SKILL.md
6.75 KB
name: wechat-db-decrypt description: 端上解密打开微信 EnMicroMsg.db(SQLCipher),拿聊天/联系人/朋友圈原始数据。纯本地 su,不经 Mac/Frida。含 key 推导 + 多账号选库 + 已验证密钥缓存。 version: 1.0
category: communication
WeChat DB Decrypt(微信数据库端上解密)
微信的 EnMicroMsg.db 是 SQLCipher 加密的。这个 skill 让你在设备内直接算出密钥、拷库、解密只读打开,
拿到 rcontact(联系人)、message(聊天)、SnsInfo(朋友圈)等原始表。纯 su + sqlcipher,不经 Mac、不用 Frida。
When to Use
- 需要读微信联系人 / 聊天记录 / 朋友圈的结构化原始数据(不是截图 OCR)
- 给 lifelog / CRM 灌微信数据、做人物档案、群消息摘要
- 任何"要查微信数据库"的任务
密钥推导公式(真机验证)
key = MD5( IMEI + uin ) 取 hex 前 7 位
IMEI = "1234567890ABCDEF" # 微信默认值,本机适用
uin = 从 shared_prefs 读 last_login_uin(当前登录账号)
SQLCipher 参数: cipher_compatibility = 1, cipher_page_size = 1024
⭐ 已验证密钥缓存(2026-07-08,先查这里,命中就别重算)
| uin | MicroMsg 目录 | key | 库大小 | 备注 |
|---|---|---|---|---|
| 257966415 | ec4f423b316d41e9af5e8398155cf105 |
1986554 |
~90MB | 当前主账号,已实测 720 表 / rcontact 42271 条 |
只要
last_login_uin还是 257966415,直接用 key1986554,跳过推导。 换号了才需要走下面的"重新推导"。
选对账号目录(多账号必看)
设备上 MicroMsg/ 下可能有多个账号目录,目录名 = MD5("mm" + uin)。
不要用 ls | head -1——那会按字典序选错账号。正确定位:
BASE=/data/user/0/com.tencent.mm
UIN=$(su --mount-master -c "grep -hoE 'last_login_uin\">[0-9-]+' $BASE/shared_prefs/*.xml" | grep -oE '[0-9-]+' | head -1)
DIR=$(printf '%s' "mm${UIN}" | md5sum | cut -c1-32) # = 当前登录账号的目录名
echo "uin=$UIN dir=$DIR"
完整流程(重新推导 + 打开)
BASE=/data/user/0/com.tencent.mm
export PATH=/system/bin:/system/xbin:$PATH
# 1. 读当前账号 uin,算目录、算 key
UIN=$(su --mount-master -c "grep -hoE 'last_login_uin\">[0-9-]+' $BASE/shared_prefs/*.xml" | grep -oE '[0-9-]+' | head -1)
DIR=$(printf '%s' "mm${UIN}" | md5sum | cut -c1-32)
KEY=$(printf '%s' "1234567890ABCDEF${UIN}" | md5sum | cut -c1-7)
echo "uin=$UIN key=$KEY dir=$DIR"
# 2. su 拷出该账号的库到可读位置(含 wal/shm,保证最新数据)
su --mount-master -c "D=$BASE/MicroMsg/$DIR; cp \$D/EnMicroMsg.db /sdcard/en.db; \
[ -f \$D/EnMicroMsg.db-wal ] && cp \$D/EnMicroMsg.db-wal /sdcard/en.db-wal; \
[ -f \$D/EnMicroMsg.db-shm ] && cp \$D/EnMicroMsg.db-shm /sdcard/en.db-shm; \
chmod 666 /sdcard/en.db*"
# 3. sqlcipher 打开验证 + 查数据
sqlcipher /sdcard/en.db <<EOF
PRAGMA key = '$KEY';
PRAGMA cipher_compatibility = 1;
PRAGMA cipher_page_size = 1024;
SELECT count(*) FROM sqlite_master; -- 能出数字=解开了
SELECT count(*) FROM rcontact; -- 联系人数
-- .headers on
-- SELECT username, nickname, conRemark FROM rcontact LIMIT 20;
EOF
# 4. 用完删明文
rm -f /sdcard/en.db*
若返回 file is not a database (26):key 不对 → 兜底把 IMEI 换成空串再试一把
(KEY=$(printf '%s' "${UIN}" | md5sum | cut -c1-7));仍不行则微信大版本可能改了 KDF 参数,需重新逆向。
两个 su 坑(否则静默失败)
- 必须
su --mount-master:否则看不到com.tencent.mm的真实数据目录(mount namespace 隔离)。 export PATH=/system/bin:/system/xbin:app 侧起的 su 环境 PATH 可能是空的,grep/ls/cp/md5sum全找不到。
常用表速查
rcontact— 联系人(username 微信号, nickname 昵称, conRemark 备注, alias 微信号别名)message— 聊天消息(talker 会话对象, content 内容, createTime, isSend, type 类型)SnsInfo— 朋友圈img_flag/ 头像等资源在MicroMsg/<dir>/avatar/及独立库
读会话上下文(自动回复·环节2:给回复生成备料)
拿到一条触发事件 {talker, isGroup, sender(wxid), kind}(来自 watcher 的 incoming.jsonl),
一次拷库即可查齐下面三块,拼成喂给 LLM 的上下文。别分三次拷库。
1. 发送者身份
SELECT nickname, conRemark, alias FROM rcontact WHERE username='<sender_wxid>';
- 私聊:sender = talker;群聊:sender = 群消息
wxid:前缀那个 - 显示名优先级:conRemark(备注) > nickname(昵称) > alias > wxid
- 群名:
SELECT nickname FROM rcontact WHERE username='<talker@chatroom>';(群的 nickname 就是群名)
2. 最近 N 条会话历史
SELECT isSend, type, createTime, content FROM message
WHERE talker='<talker>' ORDER BY msgId DESC LIMIT 20; -- 再按时间正序排回来
解析规则:
isSend=1→ 我(小火子)发的;isSend=0→ 对方发的- 群消息 content 形如
wxid_xxx:\n正文,剥前缀取「发送者 wxid + 正文」,每个 wxid 再走身份查询解析显示名 type:1=纯文本;3=图片;34=语音;43=视频;47=表情;49=appmsg(链接/文件/引用/聊天记录);大 type + 内容含「撤回了一条消息 / 拍了拍」= 系统消息(忽略)- 只把文本喂给 LLM;非文本给占位符
[图片]/[语音]/[链接:标题],别把整坨 XML 塞进去
3. 引用原文(kind=group_quote_me,或消息含 <refermsg>)
type=49 且 appmsg <type>57</type>,<refermsg> 块内:
<fromusr>/<chatusr>= 被引消息作者 wxid<displayname>= 作者显示名<content>= 被引原文(群里被引消息也可能带wxid:前缀,同样剥) 把<content>当作「对方在引用这句话回复你」。(真样例少,遇到先 dump 一条核对字段。)
组装(喂环节3 的上下文骨架)
[群] {群名} ← 群聊才有
对方:{显示名}({备注或wxid})
[引用] {被引作者}: {被引原文} ← 有引用才有
最近对话:
{时间} {对方显示名}: {文本}
{时间} 我: {文本}
...
本条:{对方显示名}: {正文}
注意
- 官方号(username 以
gh_开头)= 公众号推送,不是真人,通常不回(策略层过滤) - 群成员的「群昵称」(per-group) 在 chatroom 表的 displayname,比 rcontact.nickname 更贴切但要多查一次;MVP 先用 rcontact 的名字够用
说明
- key 是确定性的:uin 不变 → key 不变。没有"独立 key 文件"存在别处,缓存表就是唯一的省算路径。
- app 侧同逻辑源码在仓库
lifelog-recorder/.../vision/WeChatDb.kt(供交叉参考)。