- 逆向工程
- 密码学
【免费下载链接】WechatDecrypt
微信消息解密工具
导读:本文围绕开源仓库 WechatDecrypt(微信消息解密工具)的 README.md 使用教程展开,完整讲解如何编译该工具、将其部署到微信数据目录,并借助dewechat ChatMsg.db一条命令把加密的微信数据库(如 ChatMsg.db)还原为可用 sqlite 直接打开的明文数据库。同时,文章将深入 wechat.cpp 源码,逐层剖析其背后的 SQLite 逐页解密、PBKDF2-HMAC-SHA1 密钥派生、HMAC-SHA1 完整性校验与 AES-256-CBC 解密实现,让读者既能照章操作,也能读懂底层原理。
一、项目概览与仓库结构
WechatDecrypt 是一个基于 Windows + OpenSSL 的单文件 C++ 工具,用于解密微信 PC 客户端本地保存的加密 SQLite 数据库。微信把聊天记录存放在以微信 ID 命名的目录下,数据库文件(如ChatMsg.db)本身是加密的,无法直接打开;本工具读取加密文件后,按页还原出明文 SQLite 数据库,输出为dec_前缀的新文件。
仓库结构非常精简,全部源码与文档仅三个文件:
| 文件 | 说明 |
|---|---|
| README.md | 使用教程:编译、部署路径与解密命令 |
| wechat.cpp | 核心实现:单文件 C++ 解密程序(约 150 行) |
| LICENSE | MIT 开源协议 |
整个解密逻辑全部集中在 wechat.cpp 中,主流程由main()与Decryptdb()两个函数承担,没有额外的工程文件或构建脚本,属于典型的“拿到源码直接编译”风格的小工具。
二、环境准备与编译
2.1 依赖条件
从 wechat.cpp 的头部包含可以看出,程序依赖两类组件:
- Windows 运行环境:
#include <Windows.h>,且代码中大量使用fopen_s、strcpy_s、sprintf_s等 MSVC 安全函数,因此它面向 Windows 平台编译运行。 - OpenSSL 加密库:使用了
openssl/evp.h(AES 加解密、PBKDF2)、openssl/hmac.h(HMAC-SHA1)、openssl/aes.h、openssl/rand.h。编译时需要链接 OpenSSL 的libcrypto。
2.2 编译示例
仓库没有附带 Makefile 或 CMakeLists,需要手动调用编译器。以 MinGW-w64 + 已安装 OpenSSL 的环境为例,可参考如下命令(实际请根据本机 OpenSSL 的 include/lib 路径调整-I、-L参数):
g++ wechat.cpp -o dewechat.exe -I<openssl/include> -L<openssl/lib> -lcrypto -lssl在 Visual Studio 环境下,则用cl编译并链接 OpenSSL 库即可。编译成功后得到可执行文件,按 README 的使用方式,二进制名称应为dewechat(Windows 下即dewechat.exe)。
三、部署:把工具拷贝到微信数据库目录
编译完成后,README 给出的部署方式是:把这个可执行文件拷贝到微信数据库目录。该目录通常在:
C:\Users\Administrator\Documents\WeChat Files\********\Msg其中路径里的星号********处就是需要解密的微信 ID——每个微信账号在WeChat Files下都有一个以自身 ID 命名的文件夹,聊天数据库就存放在该文件夹下的Msg子目录中。
把dewechat.exe放到Msg目录后,后续只需在该目录下打开 CMD 执行解密命令即可。如果不想拷贝,也可以把可执行文件放在任意位置,运行命令时直接传入数据库文件的完整路径——从源码结构看,main() 会把argv[1]原样作为文件名打开(见 Decryptdb() 中的fopen_s(&fpdb, dbfilename, "rb+")),因此传入相对路径或绝对路径均被支持。
四、解密实战:CMD 命令与输出结果
4.1 一条命令完成解密
在Msg目录的 CMD 中执行:
dewechat ChatMsg.db即可解密当前目录下的ChatMsg.db。README 特别说明,ChatMsg.db就是需要解密的微信数据库文件之一(聊天消息数据库)。
4.2 解密成功后的输出
解密成功后,程序会在当前目录输出:
dec_ChatMsg.db即原文件名加上dec_前缀。README 明确说明:用 sqlite 打开dec_ChatMsg.db即可查看解密后的明文数据。常见的打开方式包括 SQLiteStudio、DB Browser for SQLite 等 GUI 工具,或sqlite3 dec_ChatMsg.db命令行工具。
4.3 运行期交互与提示信息
从 main() 可以看到,程序支持两种调用形态:
- 带参数:
dewechat ChatMsg.db,直接使用argv[1]作为文件名; - 不带参数:程序会提示
请输入文件名:,等待用户在命令行手工输入数据库文件名后再开始解密。
解密过程中,程序会逐页打印进度。结合 wechat.cpp 的源码,可以观察到这些关键提示:
| 提示信息 | 触发场景 |
|---|---|
打开文件错! | 文件不存在或无法以rb+方式打开 |
解密数据页:x/y | 每解密一页打印一次进度 |
哈希值错误! | 当前页 HMAC-SHA1 完整性校验失败(密钥不匹配或文件损坏) |
解密成功! | 全部页面解密完成 |
注意:任一页面的哈希校验失败都会直接终止程序并等待按键退出,说明工具对“密钥与文件不匹配”的情况有内置的防御性检查。
五、源码级解密原理(wechat.cpp 逐层拆解)
解密不是简单地用一把钥匙打开文件,而是遵循一套 SQLite 逐页加密方案。下面按 Decryptdb() 的执行顺序拆解。
5.1 整库读入与盐(Salt)提取
程序先把整个加密文件读入内存缓冲区:
fseek(fpdb, 0, SEEK_END); long nFileSize = ftell(fpdb); fseek(fpdb, 0, SEEK_SET); unsigned char* pDbBuffer = new unsigned char[nFileSize]; fread(pDbBuffer, 1, nFileSize, fpdb);随后取文件前 16 字节作为派生密钥用的盐:
unsigned char salt[16] = { 0 }; memcpy(salt, pDbBuffer, 16);也就是说,加密库文件的头部 16 字节以明文形式存放盐,而解密时这 16 字节并不会被解密——首页解密会跳过它们,并在输出文件中将其还原为 SQLite 文件头(详见 5.6)。
同时,程序还会生成一份用于 HMAC 密钥派生的mac_salt,规则是对盐逐字节异或0x3a:
mac_salt[i] ^= 0x3a;5.2 密钥派生:PBKDF2-HMAC-SHA1
工具没有直接使用一个静态密钥,而是通过 PBKDF2-HMAC-SHA1 从“主密码 + 盐”派生出 32 字节的 AES 密钥:
PKCS5_PBKDF2_HMAC_SHA1((const char*)pass, sizeof(pass), salt, sizeof(salt), DEFAULT_ITER, sizeof(key), key);其中pass[]是源码中硬编码的 32 字节主密码数组(wechat.cpp),源码注释明确写道:“PC 端密码是经过 OllyDbg 得到的 32 位 pass”——即该密码来自对微信 PC 客户端进程的动态调试/逆向分析,而非可自行配置的明文口令。
用于页面校验的 HMAC 密钥则进一步从主密钥派生,迭代次数仅为 2:
PKCS5_PBKDF2_HMAC_SHA1((const char*)key, sizeof(key), mac_salt, sizeof(mac_salt), 2, sizeof(mac_key), mac_key);5.3 页面结构与“保留区”(reserve)
加密库按固定大小页面组织数据。PC 端每页 4096 字节,源码注释给出了页面内的布局:
//4048数据 + 16IV + 20 HMAC + 12即每页由 4048 字节密文数据、16 字节 IV、20 字节 HMAC-SHA1 摘要,以及 12 字节余量构成,合计 4096。程序用以下逻辑计算尾部保留区大小:
int reserve = IV_SIZE; // 16 reserve += HMAC_SHA1_SIZE; // +20 = 36 reserve = ((reserve % AES_BLOCK_SIZE) == 0) ? reserve : ((reserve / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE; // 对齐到 16 的倍数 → 48因此每页尾部 48 字节为保留区,其中:
[4048, 4064):16 字节 AES-CBC IV;[4064, 4084):20 字节 HMAC-SHA1 校验值;[4084, 4096):12 字节余量。
5.4 逐页 HMAC-SHA1 完整性校验
在解密每一页之前,程序先用mac_key对该页的密文数据(从当前偏移到 4064 字节处)连同页码计算 HMAC-SHA1,并与页面尾部存储的 20 字节校验值比对:
HMAC_Init_ex(&hctx, mac_key, sizeof(mac_key), EVP_sha1(), NULL); HMAC_Update(&hctx, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset + IV_SIZE); HMAC_Update(&hctx, (const unsigned char*)&nPage, sizeof(nPage)); HMAC_Final(&hctx, hash_mac, &hash_len); if (0 != memcmp(hash_mac, pTemp + DEFAULT_PAGESIZE - reserve + IV_SIZE, sizeof(hash_mac)))将页码纳入 HMAC 计算意味着各页之间的校验值相互绑定,任何一页内容被替换或篡改都会导致校验失败,这正是防止“页序调换攻击”的标准做法。校验失败时程序打印哈希值错误!并终止。
5.5 AES-256-CBC 解密
通过校验后,程序使用 32 字节主密钥key和本页尾部存储的 IV,对页面数据执行 AES-256-CBC 解密,并显式关闭填充(EVP_CIPHER_CTX_set_padding(ectx, 0)),因为密文长度正好是 16 字节 AES 块大小的整数倍:
EVP_CipherInit_ex(ectx, EVP_get_cipherbyname("aes-256-cbc"), NULL, NULL, NULL, 0); EVP_CIPHER_CTX_set_padding(ectx, 0); EVP_CipherInit_ex(ectx, NULL, NULL, key, pTemp + (DEFAULT_PAGESIZE - reserve), 0); EVP_CipherUpdate(ectx, pDecryptPerPageBuffer + offset, &nDecryptLen, pTemp + offset, DEFAULT_PAGESIZE - reserve - offset);解密长度与起始偏移因页而异:首页从偏移 16 开始解密 4032 字节,其余页从偏移 0 开始解密 4048 字节。
5.6 首页 SQLite 文件头恢复与输出落盘
SQLite 数据库文件的标准头是 16 字节的"SQLite format 3",而加密库把这 16 字节位置用作明文盐。因此解密首页时,程序把解密缓冲区的前 16 字节替换回标准 SQLite 头:
if (nPage == 1) { memcpy(pDecryptPerPageBuffer, SQLITE_FILE_HEADER, offset); }每页解密完成后,页面尾部 48 字节保留区(IV + HMAC + 余量)被原样拷贝到输出缓冲区的相同位置,随后整页 4096 字节追加写入以dec_前缀命名的输出文件:
sprintf_s(decFile, "dec_%s", dbfilename); fopen_s(&fp, decFile, "ab+"); fwrite(pDecryptPerPageBuffer, 1, DEFAULT_PAGESIZE, fp);也就是说,输出的dec_ChatMsg.db在每页尾部仍保留 48 字节的 IV/HMAC 痕迹,但这部分字节落在 SQLite 页面数据区内,不影响 SQLite 正常读取整库,因此可以放心用 sqlite 工具直接打开。
六、核心常量与参数速查表
以下常量均直接来自 wechat.cpp,是理解与移植该算法时的关键数值:
| 常量 / 参数 | 值 | 含义 |
|---|---|---|
SQLITE_FILE_HEADER | "SQLite format 3" | SQLite 标准文件头(16 字节) |
IV_SIZE | 16 | AES-CBC 初始化向量长度 |
HMAC_SHA1_SIZE | 20 | HMAC-SHA1 摘要长度 |
KEY_SIZE | 32 | 派生出的 AES 密钥长度(即 AES-256) |
AES_BLOCK_SIZE | 16 | AES 分组大小(用于保留区对齐) |
DEFAULT_PAGESIZE | 4096 | PC 端每页字节数(4048 数据 + 48 保留区) |
DEFAULT_ITER | 64000 | PBKDF2-HMAC-SHA1 主密钥迭代次数 |
reserve | 48 | 每页尾部保留区(16 IV + 20 HMAC + 12 余量,对齐到 48) |
pass[] | 32 字节 | 硬编码主密码(逆向自 PC 端) |
| 输出文件命名 | dec_+ 原文件名 | 如dec_ChatMsg.db |
七、平台差异分支:源码中预留的 Android 配置
wechat.cpp 中通过条件编译宏ANDROID_WECHAT预留了一套移动端参数分支:
#ifndef ANDROID_WECHAT #define DEFAULT_PAGESIZE 4096 //4048数据 + 16IV + 20 HMAC + 12 #define DEFAULT_ITER 64000 #else #define NO_USE_HMAC_SHA1 #define DEFAULT_PAGESIZE 1024 #define DEFAULT_ITER 4000 #endif从源码结构看,Android 分支定义了三个差异点:页面大小降为 1024 字节、PBKDF2 迭代次数降为 4000,并通过NO_USE_HMAC_SHA1关闭 HMAC 校验。不过需要说明的是:本仓库中没有在任何构建入口定义ANDROID_WECHAT,且整个源文件依赖<Windows.h>与 MSVC 安全函数,实际可编译运行的场景仍是 Windows 桌面端;Android 分支更接近源码中预留的算法参数说明,而非本仓库现成可用的 Android 构建。
八、局限性与注意事项
- 主密码硬编码、依赖特定版本:32 字节主密码直接内嵌在源码中,且注释表明它来自对特定微信 PC 客户端版本的逆向调试(OllyDbg)。因此该工具的有效性绑定于对应微信版本的密钥与页面加密布局——微信升级、更换版本或数据格式调整后,使用旧密钥解密新库可能导致
哈希值错误!或解密失败。这是从 wechat.cpp 源码事实可以确认的边界条件。 - 仅适用于本人数据:工具用途是解密本机微信数据目录下的数据库,应仅在合法、授权的场景下使用(如数据迁移、本地存档分析),不应用于未授权访问他人数据。
- 代码中存在未实现的辅助函数:源文件开头声明了
CheckKey()与CheckAESKey()两个函数(wechat.cpp),但在当前版本中既未定义也未调用,可推断为早期设计遗留,不影响实际解密主流程。 - 输出文件保留每页尾部 48 字节:
dec_输出文件每页末尾会原样保留 IV/HMAC 保留区,虽然不影响 sqlite 读取,但若追求“纯净”的 SQLite 文件,可在此基础上自行剥离。
九、相关文件索引
想深入验证本文所述内容,可直接阅读仓库中的以下文件:
- README.md:官方使用教程(编译、部署路径、解密命令与输出文件说明);
- wechat.cpp:全部解密实现,重点阅读
main()(第 32-43 行)与Decryptdb()(第 45-149 行); - LICENSE:MIT 开源协议,注明版权归属。
整体来看,WechatDecrypt 以极简的单文件实现演示了一条完整的微信数据库解密链路:明文盐 → PBKDF2 派生主密钥与 HMAC 密钥 → 逐页 HMAC-SHA1 校验 → AES-256-CBC 解密 → SQLite 文件头还原。无论你是只想解密自己的聊天记录,还是想研究微信本地数据库的加密方案,这份代码都是值得对照阅读的入门级参考。
- 逆向工程
- 密码学
【免费下载链接】WechatDecrypt
微信消息解密工具
相关推荐
微信如何加密你的聊天记录?WeChatMsg拆解AES-256-CBC+HMAC-SHA1加密机制
微信如何加密你的聊天记录?WeChatMsg拆解AES 256 CBC+HMAC SHA1加密机制 WeChatMsg(微信聊天记录导出工具) 能提取 Wind
桌面应用数据分析数据可视化Chatlog Alpha 微信数据库解密原理全解:WCDB 加密、AES 与 all_keys.json 密钥映射指南
Chatlog Alpha 微信数据库解密原理全解:WCDB 加密、AES 与 all_keys.json 密钥映射指南 微信 4.x 的聊天记录并不是明文的—
后端CLIMCP 服务即时通讯RAG人工智能AI 技能SeaTunnel FieldEncrypt 加密 Transform 插件实战指南:AES-GCM/AES-CBC 字段加解密原理与配置
SeaTunnel FieldEncrypt 加密 Transform 插件实战指南:AES GCM/AES CBC 字段加解密原理与配置 SeaTunnel
数据集成ETL大数据批处理流处理变更数据捕获
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考