首页
论坛
课程
招聘
如何解密QQ数据库
2021-9-28 19:17 1799

如何解密QQ数据库

2021-9-28 19:17
1799

前言

以下仅作为学习交流用,切勿用在非法用途。

 

就拿这个数据库来实验,由于这个数据库不登录的情况下就会打,所以很方便调试。
C:\Users\xxx\Documents\Tencent Files\xxxx\ Registry2.0.db
其他的数据库同理
原理: 分析定位sqlite的open函数,在定位到key函数,然后hook获取到密钥数据,即可解密数据库,如果是进一步分析的话就分析密钥生成算法,因为之前分析过其他的算法,不是很难,所以这里不做分析,一般分析方法就是内存查找外加堆栈分析去定位密钥的加密流程。

 

思路:有多种

定位数据库打开函数就sqlite_open类似这样子的名字

1
2
1--通过createfile下断点,一个个去过滤,然后堆栈回溯分析
2--直接搜索关键词,类似sqlite open fail等。具体根据程序而定

这里为了通用,就介绍createfile分析法,
由于qq是启动的时候会创建其他进程,所以,比较快速的方法就是注入dll来定位到目标进程。
首先需要你会dll注入,这个是基本的,自己写一个或是网上找注入工具都可以,把dll(有messagebox弹窗的)注入到qq的SSOPlatform.dll这个动态库中,之后,启动qq,然后此时qq启动后会载入SSOPlatform.dll ,此时就会弹出咱们插入的消息框,这样qq进程就被暂停住了。
图片描述
打开xdbg,载入错误信息所在的进程
如下图的进程
图片描述
对createfilew下断点,
图片描述
接着点击对话框的确定,此时程序就继续运行了
图片描述
运行后就会触发断点,接着按几下f9,观察如下图,看到目标地址后,停止f9
图片描述
此时堆栈回溯分析,反正就是一层层的点击去查看是否有可疑代码。
原理也很简单,
数据库的open函数一定会调用到底层的createfilew函数,因此只要查找调用堆栈即可找到open函数
下图是我这边程序的情况,你们自己调试的时候不一定是这里
图片描述
点击进入后就来到了,箭头处就是open函数
图片描述
往上拉,可以看到这个函数其实是kernelutil.lib里面的函数,是qq官方自己开发的把sqlite.dll中的函数进行二次封装
来到ida中,打开KernelUtil.dll 进行伪代码分析
图片描述

 

回到xdbg中通过调用堆栈 ,再返回上一层,然后继续ida分析
下图中有个page_size,下次要分析数据库就可直接查找这个关键字符串,省得还要调试,调试为了初学者用的,学到了就可以直接定位了。
图片描述

 

依次回溯如下图所示
图片描述
图片描述
图片描述
上图的红色箭头指的其实就是key的函数调用
总结: 流程如下 以上分析的结果:调用过程

 

Open --> innerOpen—> CppSQLite3DB::open --> xxx

 

有了上面这些信息,就可以很容易确定到sqlite_key的函数所在了,
进一步分析有多种方法:

  1. 动态分析innerOpen函数确定sqlite_key函数
  2. 结合源码去分析innerOpen函数或是整个函数
  3. 通过ida中搜索api CppSQLite3DB::key确定哪个是sqlite_key,并重命名方便查看
    这里通过ida查找函数来分析
    图片描述
    图片描述
    接着回到xdbg中,根据ida中获取的setkey_10087896这个函数偏移,
    定位到汇编代码处,然后对此函数下断点,动态分析,获取密钥数据
    图片描述

记录下密钥,然后直接结束调试,不要正常退出qq,之后就是编写代码了,
编写代码的原理是通过偏移去获取数据库相关的函数,然后去调用就可以了,比较简单。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include "sqlite3.h"
#include <windows.h>
 
 
static int dbcallback(void* data, int argc, char** argv, char** azColName);
 
typedef SQLITE_API int (* Mysqlite3_open)(
    const char* filename,   /* Database filename (UTF-8) */
    sqlite3** ppDb          /* OUT: SQLite db handle */
);
 
 
typedef SQLITE_API int (*Mysqlite3_key)(
    sqlite3* db,                   /* Database to be rekeyed */
    const void* pKey, int nKey     /* The key */
);
 
typedef SQLITE_API int (*Mysqlite3_rekey)(
    sqlite3* db,                   /* Database to be rekeyed */
    const void* pKey, int nKey     /* The new key */
);
 
 
typedef SQLITE_API int (*Mysqlite3_exec)(
    sqlite3*,                                  /* An open database */
    const char* sql,                           /* SQL to be evaluated */
    int (*callback)(void*, int, char**, char**),  /* Callback function */
    void*,                                    /* 1st argument to callback */
    char** errmsg                              /* Error msg written here */
);
 
typedef SQLITE_API int (*Mysqlite3_close)(
    sqlite3*
);
 
 
sqlite3 *pdb = NULL;
 
 
int main()
{
 
    HMODULE h_module =  LoadLibraryExW(L"C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\KernelUtil.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
 
    Mysqlite3_open psqlite_open = (Mysqlite3_open)((DWORD)h_module + 0x36FB1);
    Mysqlite3_key psqlite_key = (Mysqlite3_key)((DWORD)h_module + 0x87896);
    Mysqlite3_rekey psqlite_rekey = (Mysqlite3_rekey)((DWORD)h_module + 0x87A55);
    Mysqlite3_exec psqlite_exec = (Mysqlite3_exec)((DWORD)h_module + 0x36372);
    Mysqlite3_close psqlite_close = (Mysqlite3_close)((DWORD)h_module + 0x3717E);
 
    int ret = psqlite_open("D:\\qqdecrypt\\Registry2.0.db", &pdb);
    char* zErrMsg = 0;
    //动态调试可知8192
    psqlite_exec(pdb,"PRAGMA page_size = 8192;",NULL,NULL, &zErrMsg);
    const char* key = "57094686228D44B0";
    ret = psqlite_key(pdb, key, 16);
 
 
    const char* data = "Callback function called";
 
    //psqlite_exec(pdb,"SELECT count(*) FROM sqlite_master;", dbcallback, (void*)data, &zErrMsg);
 
    psqlite_exec(pdb, "select name from sqlite_master where type = 'table' order by name", dbcallback, (void*)data, &zErrMsg);
 
 
 
    //psqlite_rekey(pdb, 0, 0);
    psqlite_close(pdb);
    std::cout << "Hello World!\n";
}
 
static int dbcallback(void* data, int argc, char** argv, char** azColName) {
    int i;
    fprintf(stderr, "%s: ", (const char*)data);
    for (i = 0; i < argc; i++) {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

最后于 2021-9-28 19:24 被黑洞yyh编辑 ,原因: 补充代码
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 877
活跃值: 活跃值 (328)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
icoon 活跃值 2021-9-29 16:23
2
0
学习下思路
游客
登录 | 注册 方可回帖
返回