背景
整个过程分为三步:
- 提取聊天消息数据库 EnMicroMsg.db 文件
- 获取数据库密码,解密
- 使用 Sql 命令指定条件导出聊天记录
获取数据库文件
微信的本地数据库 EnMicroMsg.db 存储在 /data/data/com.tencent.mm/MicroMsg/{hash}/EnMicroMsg.db
中
- 手机 Root 后,使用 RE 文件浏览器等文件浏览器复制出来,
- 使用手机自带的备份功能,将微信应用数据备份下来,解压提取
获取数据库密码与解密数据库
上一步取出的 EnMicroMsg.db 数据库文件是加密过的,加密方式为 SQLCipher V1 加密
微信本地数据库密码算法
- 获取手机 IMEI 码
IMEI 码在手机上拨号 *#06# 就可以查看,一般手机使用卡槽 1 的 IMEI,如若不行可以试试卡槽 2 的,CDMA 手机使用 MEID。
针对 MIUI 等隐私保护系统无法获取到 IMEI 码,则使用默认的 1234567890ABCDEF
也有说法说:IMEI 码 存储在/data/data/com.tencent.mm/MicroMsg
下的systemInfo.cfg
和CompatibleInfo.cfg
中,或/data/data/com.tencent.mm/shared_prefs/DENGTA_META.xml
,可以尝试找找。 -
获取当前登录微信账号的 uin(存储在 sp 里面) uin 存储在
/data/data/com.tencent.mm/shared_prefs/com.tencent.mm_preferences.xml
、auth_hold_prefs.xml
、auth_info_key_prefs.xml
等文件中,博主在最后一个文件中找到,类型为 int,名称为_auth_uni,数据是带有负号的 int 型。 形如<int name="_auth_uin" value="-123456789" />
- 拼接 IMEI 和 uin 不管 uin 是不是负数,当作字符串和 IMEI 拼接起来就好。然后把 IMEI + uin 这个拼接完的字符串拿去计算 32 位 md5 值(百度一下就会有计算的在线工具),截取加密后的字符串的前七位(字母必须为小写)就是密码。
暴力破解方案参考
https://zhuanlan.zhihu.com/p/123942610
https://github.com/chg-hou/EnMicroMsg.db-Password-Cracker
https://github.com/whiteblackitty/SQLCipher-Password-Cracker-OpenCL
打开连接数据库
使用最新版本的 sqlitestudio
下载地址:
参考https://www.jianshu.com/p/eb7f96c0c36f文章
查看 Android 微信数据库使用 SqlCipher 加密代码,可见使用 sqlcipher 1 版本
private static final SQLiteCipherSpec qDP =
new SQLiteCipherSpec().setPageSize(1024).setSQLCipherVersion(1);
再查看 SQLCipher 的源码发现设置 CipherVersion 为 1 时 , 会关闭 hmac, kdf_iter 为 4000。
public SQLiteCipherSpec setSQLCipherVersion(int version) {
switch (version) {
case 1: hmacEnabled = false; kdfIteration = 4000; break;
case 2: hmacEnabled = true; kdfIteration = 4000; break;
case 3: hmacEnabled = true; kdfIteration = 64000; break;
default: throw new IllegalArgumentException("Unsupported SQLCipher version: " + version);
}
return this;
}
于是手动指定参数配置 Cipher configuration 来连接:
PRAGMA kdf_iter = '4000';
PRAGMA cipher_use_hmac = OFF;
PRAGMA cipher = 'AES-256-CBC';
PRAGMA cipher_page_size = 1024;
失败告终,查阅官方文档,发现
PRAGMA cipher_compatibility :Force SQLCipher to operate with default settings consistent with that major version number for the current connection.
强制 SQLCipher 使用与当前连接的主版本号一致的默认设置运行。
通过 PRAGMA cipher_compatibility 配置,强制指定 SQLCipher 的版本号后,会自动使用该版本默认的配置链接,这样避免我们自行设置那些乱七八糟的配置,将下列配置粘贴到工具中的加密算法配置选项中
PRAGMA cipher_page_size = 1024;
PRAGMA cipher_compatibility = 1;
测试发现, OK~
使用 3.2.1 版本的 sqlitestudio
参考https://blog.csdn.net/nbvnvnvbn/article/details/97903456和https://cloud.tencent.com/developer/article/2093693文章使用 3.2.1 版本的 sqlitestudio
- 数据类型选择 SQLCipher
- Cipher 为默认的 aes-256-cbc
- KDF iterations 为 4000
- Cihper page size 为默认的 1024
-
1.1 compatibility 勾选上
然后点击测试连接,出现对勾则表示连接成功
导出聊天记录
message 表里有所有的聊天记录,具体的 sql 语句不做赘述。当然不管用什么软件,可读性都很差,需要有程序把它们按照聊天群组/人分开展示。
参考https://zhuanlan.zhihu.com/p/123942610文章推荐wechat-dump库是一个很好的从数据库中提取消息并分类导出的库,需要在 linux + python 2 环境下运行,具体配置参见项目 README 。 列出所有联系人:
./list-chats.py decrypted.db
导出所有聊天记录为文本文件:
./count-message.sh output_dir
将某个对话渲染为 html 文件:
./dump-html.py "<contact_display_name>"
导出的 html 文件可以用浏览器打开,也可以进一步打印成 pdf 。
后话
用户产生的数据不属于用户自己,说来也是可笑,应用的设计并不是以用户体验为中心,而是以用户的钱包为中心。 如此严格的限制聊天数据可以极大的提升微信用户的迁移成本,因为用户无法将聊天记录导入其它的软件。 但是这样做对用户来说就极为不友好,聊天记录漫游的功能需要付费,而且最高级别的漫游也不能保存全部聊天记录,这样用户根本无法在手机之外备份数据,手机出问题数据就全没了。 我一直对 QQ 和微信这样的行为深恶痛绝,奈何上一个手机已经用了很久,想要 root 会失去全部数据,想要通过安卓模拟器暗度陈仓又问题频出。 因此,换手机之后我的第一件事就是 root ,冒着设备安全性的风险,不为了各类插件,只为取回自己的数据。
感谢
https://www.jianshu.com/p/eb7f96c0c36f
https://blog.csdn.net/nbvnvnvbn/article/details/97903456
https://cloud.tencent.com/developer/article/2093693
https://zhuanlan.zhihu.com/p/123942610
wechat-dump