微信4.0聊天记录数据库文件解密分析

2024-11-14 0 3,928

工具介绍

代码仓库:https://github.com/0xlane/wechat-dump-rs

微信4.0的数据库解密已在工具中实现,寻找方式依然是暴力搜索。

工具支持:

  • 导出已登录微信进程的 db key
  • 通过指定 key 离线解密微信数据库文件
  • 将 db key 转为 sqlcipher 原始密钥

具体使用方式参考 README.md

库表结构

4.0.0.26

微信4.0分析记录

要定位 key 的位置之前肯定先要找到真实的 key ,下面简单记录一下寻找过程。使用微信版本 4.0.0.26 进行分析。

数据文档存储位置发生变化:C:UsersxxxDocumentsxwechat_fileswxid_xxxxxdb_storage,且不可修改。

进程名从 Wechat.exe 变化为 Weixin.exe,关键 DLL 从 WeChatWin.dll 变化为 Weixin.dll,微信将很多库静态编译成这1个文件所以很大,IDA打开都要解析很久才行。

先说一下取巧的简单办法,根据之前版本微信找 key 经验,可以使用 CE 在内存中搜索 SetDBKey 就可以定位到一个日志打印的位置,这个位置所在的函数就是 SetDBKey 函数,第二个参数就是 key 指针。(ps:可以观察一下指针周围有什么可以用于定位的特征)

下面说复杂方法,因为微信使用的是 WCDB,WCDB 算是 sqlcipher 和 sqlite 定制,所以根据 https://github.com/Tencent/wcdb/wiki/C++-加密与配置 可知微信内部应该需要调用 setCipherKey 进行加密配置,所以如果能定位到这个函数就可以找到 key。

直接定位这个函数尝试了下并不好找,另外Weixin.dll里的一些代码还是混淆的,但还是要往简单了想,继续找特征字符串定位一些关键函数,通过看源码找 setCipherKey 调用的函数,一层层向下找一直找到一个能定位的位置然后倒推回来。如果实在没办法定位它,就尝试定位它附近的函数,一般编译后也都在一起。

大致思路是这样。实际过程中,我先去找 sqlite3_exec 去了,想看一下微信执行的 sql 语句,

按上面思路对 sqlite3_exec 定位,内部没有明显字符串,比较幸运的是函数开头调用的 sqlite3SafetyCheckOk 里面有个字符串 “unopened” 在内存中直接定位到了有且仅有一个的位置 Weixin.dll+410BDCB,该位置必然是 sqlite3SafetyCheckOk

微信4.0聊天记录数据库文件解密分析

转到 IDA 中找所有交叉引用,只有 52 个,可以挨个看过去:

微信4.0聊天记录数据库文件解密分析

在里面找到最像 sqlite3_exec 的:

微信4.0聊天记录数据库文件解密分析

然后对照源码将 sqlite3_exec 调用的函数一一定位,还可以找到几个比较关键的函数:

然后找到这几个又能找出一片,不一一列举了。

现在在 windbg 里对 sqlite3_exec 下断点,监控后发现和想象中的不一样,只有一两个固定的语句经过 sqlite3_exec 执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:059> bp weixin+0x4602e50 ".printf "%ma\n", rdx;"
0:059> g
ModLoad: 00007ff9`78030000 00007ff9`780b3000   C:WindowsSystem32fwpuclnt.dll
ModLoad: 00007ff9`78360000 00007ff9`7836a000   C:WindowsSystem32rasadhlp.dll
SELECT name, rootpage, sql FROM "main".sqlite_master ORDER BY rowid
Weixin!GetHandleVerifier+0x3c40f90:
00007ff8`d0d22e50 4157            push    r15
0:021> g
SELECT name, rootpage, sql FROM "main".sqlite_master ORDER BY rowid
Weixin!GetHandleVerifier+0x3c40f90:
00007ff8`d0d22e50 4157            push    r15
0:021> g
SELECT name, rootpage, sql FROM "main".sqlite_master ORDER BY rowid
Weixin!GetHandleVerifier+0x3c40f90:
00007ff8`d0d22e50 4157            push    r15

所以微信并不直接通过 sqlite3_exec 执行 sql,此时对 sqlite3_prepare_v2 下断比较有用,基本执行的所有语句都会经过这个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
0:025> g
SELECT name, rootpage, sql FROM "main".sqlite_master ORDER BY rowid
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA checkpoint_fullfsync = TRUE
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA temp_store = 1
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA cipher_compatibility = 4
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA cipher_page_size = 4096
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA journal_mode
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
SELECT name, rootpage, sql FROM "main".sqlite_master ORDER BY rowid
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:025> g
PRAGMA checkpoint_fullfsync = TRUE
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h

可以看到一些 sqlcipher 配置参数:

1
2
3
4
5
PRAGMA journal_mode
PRAGMA cipher_compatibility = 4
PRAGMA cipher_page_size = 4096
PRAGMA checkpoint_fullfsync = TRUE
PRAGMA temp_store = 1

根据 cipher_compatibility = 4 可知新版微信使用的是 sqlcipher4,其他参数没怎么变基本默认。然后这时候已经可以使用之前快速方法找到的 key 尝试解开数据库了。

通过这个函数能观察到要执行的所有语句,但是并不能拿到执行结果。比如期间看到一个比较有意思的查询语句想看一下查询结果:

1
2
3
4
5
SELECT FROM main.LoginKeyInfoTable LIMIT 1
PRAGMA main.table_info('LoginKeyInfoTable')
CREATE UNIQUE INDEX IF NOT EXISTS LoginKeyInfoTable_USER_KEYINFO ON LoginKeyInfoTable(user_name_md5, key_info_md5)
COMMIT
SELECT user_name_md5, key_md5, key_info_md5, key_info_data FROM main.LoginKeyInfoTable WHERE user_name_md5 == '963a71084510983d212311f24bfdea5a9d' ORDER BY rowid DESC LIMIT 1

此时需要监控 sqlite3_column_text,从返回值中就可以拿到每列值内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0:021> g
SELECT user_name_md5, key_md5, key_info_md5, key_info_data FROM LoginKeyInfoTable WHERE user_name_md5 == '963a71084510983d212311f24bfdea5a9d' ORDER BY rowid DESC LIMIT 1
Weixin!GetHandleVerifier+0x3746d00:
00007ff8`d0828bc0 4883ec38        sub     rsp,38h
0:016> bp weixin+4107110
0:016> g
Breakpoint 1 hit
Weixin!GetHandleVerifier+0x3745250:
00007ff8`d0827110 56              push    rsi
0:016> gu;da rax
00000237`74852030  "963a71084510983d212311f24bfdea5a9d"        // user_name_md5
00000237`74852050  ""
0:016> g
Breakpoint 1 hit
Weixin!GetHandleVerifier+0x3745250:
00007ff8`d0827110 56              push    rsi
0:016> gu;da rax
00000237`7484b460  ""                                          // key_md5
0:016> g
Breakpoint 1 hit
Weixin!GetHandleVerifier+0x3745250:
00007ff8`d0827110 56              push    rsi
0:016> gu;da rax
00000237`7484b910  "6d4196fc4b3183a88692bde4e82966db"           // key_info_md5
00000237`7484b930  ""

但是你会发现拿不到 key_info_data 值,因为它是 BLOB 类型,sqlite3_column_text 只能看到 TEXT 类型值,所以对于不同的值需要监控不同的 sqlite3 接口:

1
2
3
4
5
6
7
8
9
10
bp weixin+4108bc0 ".printf "%ma\n", rdx;"
bp weixin+4602e50 ".printf "%ma\n", rdx;"
bp weixin+4106f30
bp weixin+4107010 "gu;db rax;"
bp weixin+4107040 "gu;db rax;"
bp weixin+4107070
bp weixin+41070B0
bp weixin+41070E0
bp weixin+4107110
bp weixin+4107140

刚开始我以为这个 key_info_data 里就是 db key,发现并不是,login key 和 db key 是不一样的,login key 每次登录都会变化,db key 重新登录不会变。

此时已经扯远了,继续想办法定位 setCipherKey

setCipherKey 中实际调用的是 innerDatabase->setConfig

1
2
3
4
5
6
7
8
9
10
11
12
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);
    }
}

我注意到传给 setConfig 的第一个参数是 CipherConfigName,它是一个定值:com.Tencent.WCDB.Config.Cipher,在 CoreConst.cpp 中被初始化。

该字符串在内存中有且仅有一处:

微信4.0聊天记录数据库文件解密分析

由此在 IDA 中找到对应位置在 .data 区域:

微信4.0聊天记录数据库文件解密分析

交叉引用只有2处,实际是在一起,该位置就是 CoreConst 文件范围,可以看到我标记的 com_Tencent_WCDB_Config_Cipher 才是真正的全局变量 CipherConfigName

微信4.0聊天记录数据库文件解密分析

然后找它的交叉引用,也仅有4处而已:

微信4.0聊天记录数据库文件解密分析

因为 c++ 类调用的原因,调用 setConfig 时,CipherConfigName 实际是第二个参数,也就是说和 RDX 有关,从上图也能直观看出来只有2处引用和该寄存器有关,这里代码有点被混淆,看了下基本确定第1处为 setConfig 调用:

微信4.0聊天记录数据库文件解密分析

在 windbg 中此位置下断后,找到 setCipherConfig 函数位置为 Weixin+1ed3f30,根据 setCipherConfig 源码可知,const UnsafeData& cipherKey 存储在 RDX 中,后传给了 R14 保存,所以从 R14 中就可以读取到 key 值:

1
2
3
0:021> db poi(r14+8) L20
0000023f`0ea53fc0  f2 2e d9 3b 4f 3f 48 3f-9e 2e 14 dd a9 30 bb f2  ...;O?H?.....0..
0000023f`0ea53fd0  0f bc e4 17 69 8b 3a 17-b7 b4 3e 09 58 9a d4 1c  ....ikJ'....H...
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

注:在使用本系统时,使用方必须在国家相关法律法规范围内并经过国家相关部门的授权许可,禁止用于一切非法行为。使用用途仅限于测试、实验、研究为目的,禁止用于一切商业运营,本团队不承担使用者在使用过程中的任何违法行为负责。

83源码 技术教程 微信4.0聊天记录数据库文件解密分析 https://www.83ym.com/182.html

认准唯一TG:@ym830

常见问题
  • 站内所有资源,针对不同等级VIP会员可直接下载,特殊资源商品会注明是否免费,指会员所享有根据选择购买的会员选项所享有的特殊服务,具体以本站公布的服务内容为准。
查看详情
  • 按照我国的法律规定,运营网络棋牌首先需要成立一个注册正规备案的公司,然后申请网站备案、文网文、ICP等等,这些证件缺一不可。 一.注册公司 在当地工商进行注册,公司名称以“XX科技有限公司”为名,如:富裕棋牌经营范围填写“计算机软硬件、网络设备的设计开发与购销”。 二.域名及网站备案 在国内从事网站经营活动就必须经过相关部门的备案,因此棋牌运营商在购买了域名后,就要到当地网监局办理网站备案,或者请服务器提供商代为备案。 三.申请文网文 文网文全称为网络文化经营许可证,是从事经营性互联网文化活动所必需的资质。一般是需要到当地省一级(省、直辖市、自治区)的文化行政部门提出申请,并经由当地的文化行政部门合法批准。次资质要求申请公司注册资金必需达到1000万,并提供游戏版权证明文件。 四.申请ICP ICP又称为增值电信业务许可证,所有网络游戏运营商均需要办理ICP许可证,此证件要求公司注册资金1000万,需到当地市级通讯管理局办理。 五.申请文网游——游戏备案 根据《网络游戏管理暂行办法》(文化部第49号)的规定,国产网络游戏在上网运营之日起30日内应当按规定向国务院文化行政部门履行备案手续。 以上就是网络棋牌游戏正规运营所必需的资质证明。一般作为正规有实力的棋牌游戏开发公司,不光要具备所有的正规资质,而且会对投资者、代理商等合作伙伴给予相关指导和协助,与合作伙伴携手共赢!
查看详情

相关文章

猜你喜欢
官方客服团队

为您解决烦忧 - 24小时在线 专业服务