1、 前提
已知wx使用的是 wcdb,这是一个基于 SQLite 和 SQLCipher的数据库框架。阅读其源码,可以定位到一个函数setCipherKey,这是用来设置数据库key的。
void Database::setCipherKey(const UnsafeData& cipherKey, int cipherPageSize, CipherVersion cipherVersion)
{
if (cipherKey.size() > 0) {
m_innerDatabase->setConfig(
CipherConfigName,
std::static_pointer_cast<Config>(std::make_shared<CipherConfig>(
cipherKey, cipherPageSize, cipherVersion)),
Configs::Priority::Highest);
} else {
m_innerDatabase->removeConfig(CipherConfigName);
}
}
UnSafeData 核心成员变量如下所示,这里作者猜测m_buffer指针指向内容就是key。
class UnsafeData {
protected:
unsigned char *m_buffer;
size_t m_size;
};
有几种方式可以获取这个key。
1. hook setCipherKey函数,解析cipherkey参数即可得到key,比较简单。
2. 通过key的来源反推key生成算法,难度较高。
3. 还有这种通过内存扫描的方式。(a48K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1j5H3P5r3I4S2L8X3g2Q4x3V1k6%4k6h3y4Z5j5i4c8Q4x3X3c8V1N6h3#2H3i4K6u0V1M7Y4y4Q4c8f1k6Q4b7V1y4Q4z5o6V1`.
作者尝试了前两种方法,第一种简单实现,第二种方法定位到了key生成算法的位置,但是能力有限没有把该算法逆向出来
(望各位大佬赐教)
这里主要讲解逆向思路,以及第一种方式的实现。
2、正式分析
1. 定位setCipherKey的地址
wx的功能基本都在Weixin.dll这个dll里面,用ida打开这个dll,由于是没有符号的(废话,当然没符号),所以没法直接搜索到setCipherKey。
但是在setCipherKey中有一行代码是
m_innerDatabase->removeConfig(CipherConfigName);
而CipherConfigName参数是一个全局字符串。
WCDBLiteralStringDefine(CipherConfigName, "com.Tencent.WCDB.Config.Cipher");
这时候我们可以搜索字符串定位到CipherConfigName的地址。
(ps : 如果CipherConfigName的字符串被混淆了也没关系,运行时还是会恢复的,用CheatEngien在进程中查找字符串,也能定位CipherConfigName)
然后根据CipherConfigName来查找它的引用。

对于反汇编的伪代码和源代码可以初步定位到setCipherKey。

2. 动态调试setCipherKey
x64dbg附加到wx,先不要登录,不然不会触发setCipherKey了。在setCipherKey处下个断点,然后登录。
由于setCipherKey是个成员函数,所以第一个参数cipherKey对应寄存器是rdx,而不是rcx。rdx处地址如图所示,可以看到rdx+0x10处是0x20,也就是cipherKey->m_size。那么rdx+0x8就是cipherKey->m_buffer

跳转到cipherKey->m_buffer指向地址,这0x20个字节就是key。

拿这个key去打开数据库验证一下,验证通过,ok,得到key。
3. 工程化
1. 注入
注入就不多说了,自行查找。
2. 获取setCipherKey地址
由于setCipherKey是一个比较基础和底层的库,改动的可能性比较小,且有全局唯一性,因此我们完全可以通过特征码定位该函数。
注意,匹配特征码的时候要将地址相关的做模糊匹配,否则会匹配失败。且匹配的时候建议只匹配代码节,减少匹配时间。
3. hook setCipherKey
这个也没啥好说的,detours。(注意下重入就行)
4. 根据得到的key即可读取数据库内容。
作者将用到的一些偏工具性的代码放到了0e7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6L8s2A6J5j5i4W2Q4x3V1k6T1K9h3&6S2M7X3q4&6i4K6g2X3M7s2u0G2K9X3g2U0N6q4!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4W2)9&6b7#2)9^5z5h3W2F1K9X3g2U0N6q4!0q4x3#2)9^5x3q4)9^5x3h3S2G2L8$3E0Q4c8e0y4Q4z5o6m8Q4z5o6q4E0k6h3#2G2M7Y4W2Q4c8e0N6Q4z5f1u0Q4b7U0S2Q4c8e0g2Q4z5o6g2Q4b7U0y4Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0g2Q4z5p5q4Q4z5f1k6Q4c8e0S2Q4z5o6y4Q4b7V1c8Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0g2Q4z5p5k6Q4b7f1k6Q4c8e0c8Q4b7V1g2Q4z5f1u0Q4c8e0g2Q4z5p5k6Q4z5o6u0Q4c8e0S2Q4z5o6m8Q4z5o6y4Q4c8e0y4Q4z5o6m8Q4z5o6t1`.
4. 其他
作者通过setCipherKey一层一层往上找,基本定位到了generatekey函数,还有一些全局变量,加/解密时候需要调用的表。但是意义不大,因为这个生成key所需的数据应该没存在本地,而是通过网络请求获取的,所以逆向出算法也没用,还是要通过hook的方式去做。如果各位大佬有什么高见,可以指点一下。
微信数据库相关的信息:ec2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1j5H3P5r3I4S2L8X3g2Q4x3V1k6%4k6h3y4Z5j5i4c8Q4x3X3c8V1N6h3#2H3i4K6u0V1M7Y4y4Q4x3V1k6T1L8r3!0T1i4K6u0r3N6U0c8Q4x3V1k6V1L8$3y4K6i4K6u0r3N6$3g2U0K9r3q4@1i4K6g2X3y4q4)9#2k6U0m8Q4y4h3j5H3i4K6g2X3x3U0k6Q4y4h3k6@1j5h3u0D9k6g2)9#2k6Y4y4@1M7Y4g2U0N6q4)9J5k6h3#2V1 。
不过这个不是100%准,比如只查message_.db是不行的,还要和message_fts.db关联。后续有时间的话,且如果大家有兴趣的话考虑聊天记录这块整一下。
所用工具:IDA、x64dbg、CheatEngine
引用:
d61K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6f1k6h3&6U0k6h3&6@1i4K6u0r3N6$3y4V1j5R3`.`.
9daK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6L8s2A6J5j5i4W2Q4x3V1k6T1K9h3&6S2M7X3q4&6i4K6g2X3M7s2u0G2K9X3g2U0N6l9`.`.
264K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1j5H3P5r3I4S2L8X3g2Q4x3V1k6%4k6h3y4Z5j5i4c8Q4x3X3c8V1N6h3#2H3i4K6u0V1M7Y4x3`.
4. 免责声明
本文只供研究和学习,作者不对任何滥用行为负责。使用者应自行承担风险。!
本文只供研究和学习,作者不对任何滥用行为负责。使用者应自行承担风险。!
本文只供研究和学习,作者不对任何滥用行为负责。使用者应自行承担风险。!
重要的事情说三遍!!!
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-7-30 07:21
被小周学习站编辑
,原因: