首页
社区
课程
招聘
[原创]某聊天工具数据库解密
2022-9-2 15:16 11549

[原创]某聊天工具数据库解密

2022-9-2 15:16
11549

众所周知的,微信PC版存放聊天记录等数据采用的是sqlite3去存放的,实现功能的核心dll是wechatwin.dll。所以帮wechat数据库解密需要两步,第一步是找到key,第二步是代码还原。

一、找到数据库解密的key

在目前版本wechat 3.7.0版本再去通过搜寻“encryptDB %s DBKey can’t be nul”上下文来寻找pkey的方法已经失效,,既然如此,把思路打开,sqlite3在使用加密数据库的时候必定先解密,解密必然会调用sqlite3_key函数去解密。
函数原型为:

1
2
3
4
5
6
SQLITE_API int sqlite3_key(
 sqlite3 *db,
 const void *pKey,
  int nKey) {
  return sqlite3_key_v2(db, "main", pKey, nKey);
}

直接wechatwin.dll 扒出来,扔到ida去看看。 找到关键字 cipher_version 和 4.2.0 ,看来用wechat用的是sqlcipher 4.2.0 ,直接上github.com/sqlcipher/sqlcipher 找到4.2.0版本,编译出来。
有了符号,我们可以通过对照找到sqlit3_key()函数,在wechatwein.dll 3.7.0版本中,并没有sqlite3_key()函数,应该是编译器优化掉了,通过阅读源码可知,sqlite3_key_v2()调用了sqlcipher_find_db_index 和 sqlite3CodecAttach函数。 sqlite3_key_v2函数原型为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQLITE_API int sqlite3_key_v2(
 sqlite3 *db,
 const char *zDb,
 const void *pKey,
 int nKey) {
      /*
      attach key if db and pKey are not null and nKey is > 0
   */
    if(db && pKey && nKey) {
     int db_index = sqlcipher_find_db_index(db, zDb);
     return sqlite3CodecAttach(db, db_index, pKey, nKey);
  }
  return SQLITE_ERROR;
}

到这里已经大概清楚,pkey是解密数据库的密钥,一种做法是直接hook函数sqlite3CodecAttach,通过hook的方式获取key(什么?不知道wechatwin.dll的sqlite3CodeAttach函数在哪里?方法很多种,我用的是目测对比法。),另一种方法是直接还原他的算法,很不幸的是3.7.0版本的核心算法是vm掉的,本菜鸡找到一个2.9.0版本的wechatwin.dll 关键函数也是vm掉的。所以还原算法就先需要还原VM掉的代码。难度太大了,(我这种小菜鸡做不来。有大佬会的教教我)。

二、通过代码解密数据库

通过资料得知sqlite3_rekey()可以解密函数,但是万万没想到的是这里面有个大坑,sqlcipher的sqlite3_rekey()因为文档在低版本标注出错了,导致我想了好长时间,扒开源码才发现sqlite3_rekey()在参数pkey和nkey == NULL的时候直接返回错误。
附3.4.2文档错误注释:

1
2
3
4
5
/*
** Change the key on an open database.  If the current database is not encrypted, this routine will encrypt it.  If pNew==0 or nNew==0, the  database is decrypted.
**
** The code to implement this API is not available in the public release  of SQLite.
*/

4.2.0版本注释

1
2
3
4
5
6
7
8
9
/* BEGIN SQLCIPHER
   SQLCipher usage note:
   If the current database is plaintext SQLCipher will NOT encrypt it.
   If the current database is encrypted and pNew==0 or nNew==0, SQLCipher will NOT decrypt it.
   This routine will ONLY work on an already encrypted database in order to change the key.
   Conversion from plaintext-to-encrypted or encrypted-to-plaintext should
   use an ATTACHed database and the sqlcipher_export() convenience function as per the SQLCipher Documentation.
   END SQLCIPHER
*/

很明显 社区版的明确告知 没有实现通过sqlite3_rekey()来解密数据库。应该使用sqlcipher_export() 来解密。
既然这样,不如使用一下tx的传统艺能,复制粘贴。扒下微信的操作,直接上od看看微信是怎么操作数据库的。
先通过符号 对照找到wechatwin.dll中 sqlite3_exec()这个函数,函数原型为:

1
2
3
4
5
6
7
SQLITE_API int sqlite3_exec(
  sqlite3 *db,                /* The database on which the SQL executes */
  const char *zSql,           /* The SQL to be executed */
  sqlite3_callback xCallback, /* Invoke this callback routine */
  void *pArg,                 /* First argument to xCallback() */
  char **pzErrMsg             /* Write error messages here */
)

把参数记录下来,找到 一下几条关键数据库语句操作
PRAGMA cipher_compatibility = 3
PRAGMA cipher_page_size = 4096
PRAGMA synchronous=NORMAL
SELECT name, rootpage, sql FROM \"main\".sqlite_master ORDER BY rowid
PRAGMA auto_vacuum=0
PRAGMA journal_mode = WAL
然后外加好多回调函数,不过大多数也是vm掉的回调,脑子疼。
附:(想问这些语句是什么意思的可以具参考一下 https://www.zetetic.net/sqlcipher/sqlcipher-api/文档介绍。)
额,很显然,没有找到任何线索。
不过参考sqlcipher文档,可以清晰的知道如何解密数据库的,我还是直接复制粘贴了

1
2
3
4
5
$ ./sqlcipher encrypted.db
sqlite> PRAGMA key = 'testkey';
sqlite> ATTACH DATABASE 'plaintext.db' AS plaintext KEY '';  -- empty key will disable encryption
sqlite> SELECT sqlcipher_export('plaintext');
sqlite> DETACH DATABASE plaintext;

关键是使用SELECT sqlcipher_export('plaintext');来重新生成一个未加密的数据库
直接上代码

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
33
34
35
36
37
38
39
40
41
42
43
44
45
int test_exec(sqlite3*Db,char*sql)
{
      // 提一句什么这么玩,因为根据文档解释,sqlite3_key出错的话,不会立即报错,
      // 只有执行一条sql语句后才知道sqlite3_key()执行成功与否。
 
    if (sqlite3_exec(Db, sql, NULL, NULL, NULL) == SQLITE_OK) {
        printf("key is correct\r\n");
    }
    else {
        printf("key is incorrect\r\n");
    }
}
int main()
{
    const char *filename = "C:\\Users\\xxx\\Desktop\\ChatMsg.db"; //加密数据库路径
    const char  pkey[0x20] ={} ;//自己下断点或者hook获取key
    sqlite3 *Db;
    if (sqlite3_open(filename, &Db))
    {
        printf("sqlite3_open error");
        return  0;
    }
 
    sqlite3_key(Db, pkey,0x20);
    test_exec(Db, "PRAGMA cipher_compatibility = 3");
    test_exec(Db, "PRAGMA cipher_page_size = 4096");
    test_exec(Db, "PRAGMA synchronous=NORMAL");
    test_exec(Db, "Select * from sqlite_master");
    //test_exec(Db, "PRAGMA rekey = 'testkey'");  //修改密码
    test_exec(Db, "PRAGMA count_changes=OFF");
 
    // 复制一个未加密的数据库
    test_exec(Db, "ATTACH DATABASE 'plaintext1.db' AS plaintext1 KEY '';");
    test_exec(Db, "SELECT sqlcipher_export('plaintext1');");
    test_exec(Db, "DETACH DATABASE plaintext1");
    // 继续流程
    test_exec(Db, "PRAGMA auto_vacuum=0");
    test_exec(Db, "PRAGMA journal_mode = WAL");
 
    if (SQLITE_OK != sqlite3_close(Db))
    {
       printf("sqlite3_close error");
    }
    return 0;
}

然后在 当前路径下会生成一个解密后的数据库。 直接使用数据库连接工具打开即可。
注:垃圾佬的日常垃圾分析。没有任何技术含量的分析。有不对的地方请告知。


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2022-9-2 17:12 被青丝梦编辑 ,原因: 修改
收藏
点赞2
打赏
分享
最新回复 (10)
雪    币: 475
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ftpwd 2022-9-4 18:19
2
0
可以查找字符串 cipher_integrity_check  找到引用该字符串的函数,该函数下面的第一个函数可以看到SQLITE format 3,继续往下滑给下面连续三个函数都下断点,断下后,有一个参数指向密钥,下一个参数是0x20
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_cyjhboga 2022-9-15 17:00
3
0
厉害了
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_cyjhboga 2022-9-15 17:02
4
0
想请教您一下 不知是否方便  有偿也是可以的~  
雪    币: 861
活跃值: (1553)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
凌哥 2022-10-18 18:23
5
0
这是我分析vx的思路,https://blog.ltools.vip/archives/173/。
雪    币: 530
活跃值: (1146)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
青丝梦 2022-10-18 19:28
6
0
凌哥 这是我分析vx的思路,https://blog.ltools.vip/archives/173/。
是这样的,不过我通过查看tencent在github的仓库,发现了他使用的sqlcipher。劫持数据库句柄是实现功能的一种手段。
雪    币: 9619
活跃值: (3457)
能力值: ( LV12,RANK:319 )
在线值:
发帖
回帖
粉丝
堂前燕 1 2022-10-18 20:38
7
1



wx_id也在这附近,忘了这个类叫啥了。。。

雪    币: 861
活跃值: (1553)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
凌哥 2022-10-24 15:52
8
0
青丝梦 是这样的,不过我通过查看tencent在github的仓库,发现了他使用的sqlcipher。劫持数据库句柄是实现功能的一种手段。
条条大路通罗马,互相学习
雪    币: 15
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
hi2022 2022-12-11 21:20
9
0
感谢分享
游客
登录 | 注册 方可回帖
返回