Skip to content

01 · 取密钥与解密

本章把「拿到密钥 → 解密成明文库」这条最硬的链路走通。配套代码在 code/wechat_win/,已在 微信 4.1.12.55 / Windows 11 实测跑通。

模块分工:

文件职责
mem_windows.py进程查找 + 内存枚举/读取(纯 ctypes)
dll_scan_win.pyWeixin.dll 提取 internal_db_key(pefile)
sqlcipher4.pySQLCipher-4 密钥派生、HMAC 校验、逐页 AES 解密
key_extractor_win.py内存原始密钥候选 + XOR 合并 + 多进程验证
paths_win.py数据库路径发现
main_win.pyCLI,把上面串起来

0. 安装依赖

powershell
cd course/wechat-ai-assistant/code
pip install -r wechat_win/requirements-win.txt

依赖只有四个:pycryptodome(AES)、zstandard(消息解压)、psutil(找主进程)、 pefile(解析 DLL)。内存读取用 ctypes,标准库自带。

1. SQLCipher-4:解密与验证(sqlcipher4.py

这是全链路的地基。上一章的加密参数在这里变成代码。核心是三个函数。

密钥派生——把 32 字节裸密钥 + salt 变成 enc_key(解密用)和 mac_key(校验用):

python
def derive_keys(raw_key: bytes, salt: bytes) -> tuple[bytes, bytes]:
    enc_key = hashlib.pbkdf2_hmac("sha512", raw_key, salt, KDF_ITER, KEY_SZ)   # 256000 次
    mac_salt = bytes(b ^ 0x3a for b in salt)
    mac_key = hashlib.pbkdf2_hmac("sha512", enc_key, mac_salt, HMAC_KDF_ITER, KEY_SZ)  # 2 次
    return enc_key, mac_key

验证密钥——重算第 1 页 HMAC,和存储值比对。这是「候选密钥对不对」的判据,很快:

python
def verify_key(first_page, salt, raw_key) -> bool:
    _, mac_key = derive_keys(raw_key, salt)
    hmac_off = PAGE_SIZE - RESERVE + IV_SZ            # = 4032
    stored = first_page[hmac_off:hmac_off + HMAC_SZ]
    calc = _page_hmac(mac_key, first_page, 1, SALT_SZ) # 第 1 页跳过前 16 字节 salt
    return hmac.compare_digest(stored, calc)

逐页解密——每页取出 IV,AES-256-CBC 解密密文区;第 1 页把 salt 头补回成 SQLite 头:

python
def decrypt_db(src_path, hex_key, dst_path):
    ...
    for i in range(total_pages):
        data_start = SALT_SZ if i == 0 else 0
        iv = page[PAGE_SIZE - RESERVE : PAGE_SIZE - RESERVE + IV_SZ]
        cipher_text = page[data_start : PAGE_SIZE - RESERVE]
        plain = AES.new(enc_key, AES.MODE_CBC, iv).decrypt(cipher_text)
        page_out = (SQLITE_HEADER + plain if i == 0 else plain) + page[PAGE_SIZE - RESERVE:]
        out.extend(page_out)

verify_enc_key 是一个优化:内存里驻留的有时已经是派生后的 enc_key,此时只需 2 次 迭代就能校验,适合对海量候选快速筛选。

先自测再往下走——不接触微信,用随机数据造一份合法密文,验证往返一致:

powershell
python wechat_win/selftest_crypto.py
# 期望:SELFTEST PASS: 派生/HMAC/AES 往返一致

自测过了,说明你的解密内核完全正确;后面再失败,一定是「密钥没取对」而不是解密错。

2. 读进程内存(mem_windows.py

Windows 没有 /proc/<pid>/mem,改用 kernel32 的三件套(纯 ctypes):

  • find_weixin_pid():用 psutil 找所有 Weixin.exe,选私有内存最大的那个 (主进程才持有 DB 句柄和密钥,小程序运行时 WeChatAppEx.exe 不算)。
  • open_process(pid)OpenProcess(PROCESS_VM_READ | QUERY_INFORMATION),失败通常是权限。
  • enum_regions(handle)VirtualQueryEx 遍历地址空间,只留已提交、可读写、私有 的区域(密钥藏在私有堆里),跳过超大区域。
  • read_region(handle, base, size)ReadProcessMemory 读出来。

同用户下的进程一般无需管理员;读不到时用管理员 PowerShell 重试。

3. 扫 DLL 取内部密钥(dll_scan_win.py

微信 4.x 内存里的原始密钥要和一个编译进 Weixin.dllinternal_db_key XOR 后才可用。 用正则在代码段里匹配「连续 4 条 mov rdx,imm64 + test rax,rax」的特征:

python
_PATTERN = re.compile(
    rb"\x48\xBA(.{8})" rb".{3,8}?\x48\xBA(.{8})"
    rb".{3,8}?\x48\xBA(.{8})" rb".{3,8}?\x48\xBA(.{8})"
    rb".{3,8}?\x48\x85\xC0", re.DOTALL)

4 个 8 字节立即数拼成 32 字节候选。resolve_weixin_dll() 会依次从入参、注册表 InstallPathC:\Program Files\Tencent\Weixin\<版本>\ 里定位 DLL。

4. 合成并验证密钥(key_extractor_win.py

把前面几步拼起来,这是「取密钥」的主逻辑:

  1. scan_raw_key_candidates(handle):在内存里按签名 [ptr(8)][0][0x20][0x2f] 找指针,读指针指向的 32 字节,做低熵过滤(去掉明显不是 密钥的字节序列)。
  2. 对每个原始候选,先试不 XOR,再试和每个 internal_db_key XOR,用 sqlcipher4.verify_key 校验第 1 页 HMAC。命中即真密钥。
  3. 候选多时用 multiprocessing 并行验证,命中即 terminate
  4. extract_all_keys() 为多个库(message / contact)一次性提取,且优先复用 已找到的密钥(多库常共用一把)。

5. 一条命令跑通(main_win.py

CLI 把「找进程 → 定位库 → 取密钥 → 解密 → 读消息/联系人」串起来:

powershell
# 提取密钥并打印最近消息和联系人(密钥默认脱敏显示)
python -m wechat_win.main_win

# 打印完整密钥
python -m wechat_win.main_win --dump-key

# 导出解密后的明文库(可用 DB Browser 直接打开,也供第 3 章 MCP 使用)
python -m wechat_win.main_win --out C:\temp\wx_plain

# 版本不匹配时手动指定回退
python -m wechat_win.main_win --db-base E:\xwechat_files --pid 1234 `
    --dll "C:\Program Files\Tencent\Weixin\4.1.12.55\Weixin.dll"
python -m wechat_win.main_win --internal-key <64位hex>

跑通后你会看到消息表数量、若干会话的最近消息、联系人条数。记住 --out C:\temp\wx_plain 这一步导出的明文库——第 3 章的 MCP 直接指向它,MCP 层就 完全不用碰密钥了。

故障排查

  • 提取不到密钥:确认微信已登录运行;用管理员 PowerShell;多开时 --pid 指定主进程。
  • DLL 无候选 / 版本不匹配:新版汇编特征可能变,用 --internal-key 手动传,或放宽 dll_scan_win.py 的匹配间距。
  • HMAC 始终不过:先跑 selftest_crypto.py 确认内核没问题;再核对 mac_key 长度=32、reserve=80、SHA512。

macOS

密钥获取方式不同(lldb 断点 CCKeyDerivationPBKDF 截获口令),需要 lldb 且微信可调试; 解密/读库与本章完全一致。步骤见 code/wechat_mac/README-mac.md,可先跑 python3 wechat_mac/selftest_mac.py 验证平台无关部分。


上一章:00 · 架构与原理 | 下一章:02 · 只读读库层