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,直接用 key 1986554,跳过推导。 换号了才需要走下面的"重新推导"。

选对账号目录(多账号必看)

设备上 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 坑(否则静默失败)

  1. 必须 su --mount-master:否则看不到 com.tencent.mm 的真实数据目录(mount namespace 隔离)。
  2. 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(供交叉参考)。