首页
论坛
课程
招聘
[原创]某数字浏览器数据库算法逆向
2020-12-1 16:26 3697

[原创]某数字浏览器数据库算法逆向

2020-12-1 16:26
3697

前言

目标

在一些安全的项目中,有时候需要对目标进行取证,而浏览器中包含的信息往往对我们来说帮助很大,同样也想学习更多的逆向知识,所以就有了这次的文章,初学逆向,多多指教!

逆向的对象

 

图片描述

第一层解密

sqlite3的知识点

首先需要知道的,Sqlite3库中常用的函数有如下:

 

1、sqlite3_open:

1
int sqlite3_open(const char *filename,sqlite3 **ppDb);

2、sqlite3_key:如果数据库已加密必须先执行此函数并输入正确密钥才能进行操作,如果数据库没有加密,执行此函数后进行数据库操作反而会出现“此数据库已加密或不是一个数据库文件”的错误

1
int sqlite3_key( sqlite3 *db, const void *pKey, int nKey)

machineguid加密

接着通过在所有模块中进行字符串搜索,会发现在chrome.dll中存在”sqlite3_open/assis2.db”字符串以及一系列相关SQL语句的执行,这里是从"sqlite3_open"入手的

需要知道的是

在sqlite3数据库中,如果数据库事先是被加密的,那么想要进行SQL操作的话要先进行sqlite3_key函数的解密,所以sqlite3_open函数之后就会对sqlite3_key对该数据库进行解密

 

 

这里为什么sub_105C03A3判断为是sqlite3_open的原因是你跟进去会发现某些特征会跟sqlite3库源文件中的一些特征”main” “temp”字符串特征,一般打开的数据库都是main名称的数据库,因为默认的数据库名为”main”,特征如下:

 

 

然后sqlite3库中提供的函数sqlite3_open定义有相关特征:

 

图片描述

 

接着就是sub_105C03BA函数进行sqlite3的解密操作,参数为数据库的句柄,同时出现了guid和guid_length

1
sub_105C03BA(**sqlite3_obj, guid, guid_length);

 

这里同样动态调试也会看到,参数是传入数据库句柄和一些某序列号字符串(machineguid),字符串的长度来进行解密的

 

 

通过监视工具API monitor也可以发现该字符串是注册表中的某项的序列号值

 

第二层解密

到目前第一层解密到现在就结束了,接着就是第二层解密,也就是Base64编码和AES128-ECB的加密的处理

定位关键点分析点

我这里是通过定位关键的sqlite3的insert语句来进行寻找,因为想法是在插入的时候程序肯定会对相关的密码进行先进行加密处理,然后最后进行整合再执行SQL语句,所以这个时候肯定是会有加密函数的调用

1
2
3
798497D7  |.  68 AEA1A77B   push chrome.7BA7A1AE
 
Insert into [tb_account (domain,username,password,last_modify_time)values(?,?,passencode(?, ?), ?);

 

接着就在SQL语句这里下断点,然后慢慢跟后跟,下面的内容先会域名长度格式校验,然后对用户名长度格式校验,接着密码校验 一个一个进行校验操作和一些自带的sqlite事务回滚操作,大家可以跟一下看

 

图片描述

 

图片描述

 

接着来到下面的地方才开始对密码进行加密操作

1
553A98CA    E8 0D1E1EFF     call chrome.5458B6DC ; 开始对密码进行加密

 

跟进去在加密之前的操作的时候会压入一个固定的字符串,这个字符串在后面会与另一串字符串进行拼接然后用来加密

 

图片描述

密码奇偶数混淆加密

然后进去继续动态调试跟,接着会寻找如下的CALL

1
553A47B3    E8 AA020000     call chrome.553A4A62; 密码混淆CALL

 

这里的与运算的结果导致的最低位0/1来决定两种取值情况

1
082D4E40    81E1 FE000000   and ecx,0xFE                                    ; 取低2
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
080B4E34    E8 A7A20302     call chrome.0A0EF0E0                     ; eax的结果计算
080B4E39    89C1            mov ecx,eax                              ; ecx = eax
080B4E3B    C1E9 1F         shr ecx,0x1F                             ; ecx = ecx >> 0x1f
080B4E3E    01C1            add ecx,eax                              ; ecx = ecx + eax
080B4E40    81E1 FE000000   and ecx,0xFE                             ; ecx = ecx & 0xFE
080B4E46    29C8            sub eax,ecx                              ; eax = ah
080B4E48    89C3            mov ebx,eax                              ; ebx = ah
080B4E4A    8945 D8         mov dword ptr ss:[ebp-0x28],eax
080B4E4D    8D443F 02       lea eax,dword ptr ds:[edi+edi+0x2]       ; 密码的长度*2+2
080B4E51    FEC3            inc bl                                   ; ebx = ebx+1
080B4E53    8945 DC         mov dword ptr ss:[ebp-0x24],eax          ; 压入密码的长度
080B4E56    50              push eax                                 ; 压入密码的长度
080B4E57    E8 E0740002     call chrome.0A0BC33C
080B4E5C    83C4 04         add esp,0x4
080B4E5F    89C1            mov ecx,eax
080B4E61    8945 E0         mov dword ptr ss:[ebp-0x20],eax
080B4E64    FF75 DC         push dword ptr ss:[ebp-0x24]             ; 压入密码的长度
080B4E67    31C0            xor eax,eax
080B4E69    50              push eax
080B4E6A    51              push ecx
080B4E6B    E8 80940102     call chrome.0A0CE2F0
080B4E70    83C4 0C         add esp,0xC
080B4E73    8B45 E0         mov eax,dword ptr ss:[ebp-0x20]
080B4E76    85FF            test edi,edi                             ; 判断密码的长度是否存在
080B4E78    885D DC         mov byte ptr ss:[ebp-0x24],bl
080B4E7B    8818            mov byte ptr ds:[eax],bl
080B4E7D    7E 65           jle short chrome.080B4EE4
080B4E7F    8B5D E0         mov ebx,dword ptr ss:[ebp-0x20]          ; 存储加密完的密码的地方 每次都会先在ebx放个\x01 或 \x02

最终[ebp-0x28]的值来影响下面的跳转决定混淆字节如何生成,这里会经过了奇数偶数的校验,最终导致的结果就是[ebp-0x28]中的值如果是\x02的话,那么就偶数位读取,如果为\x01就奇数位读取

1
2
3
4
08404E83    807D D8 00      cmp byte ptr ss:[ebp-0x28],0x0
08404E87    74 2E           je short chrome.08404EB7
08404E89    807D DC 02      cmp byte ptr ss:[ebp-0x24],0x2
08404E8D    75 51           jnz short chrome.08404EE0

图片描述

AES128-ECB加密

接着就是密钥的生成,这里会看到又压入了一串类似序列号的东西,上面一开始加密的时候也会压入一串序列号的东西,这两串最后会进行拼接字符串用于aes密钥的生成

1
553A47E8    E8 77FEFFFF     call chrome.553A4664    ; 生成了一串32位的值 可能是密钥

图片描述

 

这个call跟进去就会看到两串字符串开始进行拼接

 

图片描述

 

接着就是开始进行密钥的生成

 

图片描述

 

这里主要是两个call,一个call是通过上面拼接的字符串来生成了一段密码字节数组,另一个则是通过该字节数组的十六进制的值转成字符串

 

这个算法来进行生成一段字节数组的,它会获取了上面拼接的两组固定的序列号取最后的八个字节进行加密生成的一段字节数组

1
08784713    E8 C82B12FF     call chrome.078A72E0                            ; 生成一段用来生成aes密钥的数组

然后这个call是用来对这个字节数组中的十六进制转换为字符串

 

图片描述

1
08784720    E8 CBC47F00     call chrome.08F80BF0                            ; 生成aes密钥的call

接着就是AES的加密CALL,压入了混淆后的密码和AES128 ecb加密的密钥

1
553A4860    E8 66E2FFFF     call chrome.553A2ACB ; AES加密CALL

图片描述

Base64加密

AES加密完之后,接着就是生成Base64编码,因为最终是要存储到数据库中所以需要经过编码操作

1
553A4933    E8 988120FF     call chrome.545ACAD0  ; 生成了base64编码

图片描述

 

Base64编码的特征如下,并且就是默认的码表

 

 

此时已经完成了AES128 ECB加密和Base64的加密,最后拼接"(4B01F200ED01)"固定字符串和Base64编码的结果

1
553A495A    E8 458613FF     call chrome.544DCFA4; 拼接(4B01F200ED01)序列号和base64编码

总结

模拟解密过程

本机的Machineguid:

1
5cbfe6e6-21aa-40df-8b1e-XXXXXXXXXXXXX

图片描述

 

图片描述

 

 

这时候会发现看起来是乱码的,其实就是这样的,360自己会混淆一层,这个混淆在上面已经提到过了,这时候你隔一个字节读取,就是222222了

 

需要注意的就是第一个字节是\x01 \x02的情况处理:

 

图片描述

代码实现解密过程

解密总过程:

1
MachineGuid -> Base64 -> aes128 ecb -> 奇偶混淆

获取MachineGuid

1
2
3
MachineGuid = pomocna.getKey(@"SOFTWARE\Microsoft\Cryptography", "MachineGuid", 2);
 
dbPath = pomocna.getKey(@"360SeSES\DefaultIcon", null, 0).Split(',')[0].Replace(@"360se6\Application\360se.exe", "") + @"360se6\User Data\Default\apps\LoginAssis\assis2.db";

Base64解码

1
Convert.FromBase64String

AES解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public string DecryptAes(string text)
{
 
    byte[] src = Convert.FromBase64String(text);
    RijndaelManaged aes = new RijndaelManaged();
    byte[] key = Encoding.ASCII.GetBytes("");
    aes.KeySize = 128;
    //aes.IV = Encoding.UTF8.GetBytes("");//
    //aes.Padding = PaddingMode.Zeros;//
    //aes.BlockSize = 128;//
    aes.Padding = PaddingMode.None;
    aes.Mode = CipherMode.ECB;
    using (ICryptoTransform decrypt = aes.CreateDecryptor(key, null))
    {
        byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
        decrypt.Dispose();
        return Encoding.UTF8.GetString(dest);
    }
}

奇偶位数的判断和读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    if (passItem[0] == '\x02')
    {
        for (int p = 0; p < passItem.Length; p++)
        {
            if (p % 2 == 1)
            {
                _stringb.Append(passItem[p]);
            }
        }
        dr["password"] = _stringb;
    }
    else
    {
        for (int p = 1; p < passItem.Length; p++)
        {
            if (p % 2 != 1)
            {
                _stringb.Append(passItem[p]);
            }
        }
        dr["password"] = _stringb;
    }
}

github项目工具

这是一个一键辅助抓取360安全浏览器密码的CobaltStrike脚本以及解密小工具,用于节省红队工作量,通过下载浏览器数据库、记录密钥来离线解密浏览器密码。360SafeBrowsergetpass.cna主要用与记录解密所需要的信息用于离线解密,3Bpass主要用于解密浏览器密码,支持本机自动导出以及离线解密。

 

github地址:https://github.com/hayasec/360SafeBrowsergetpass

 

图片描述

文章仅做技术分享,切勿违法,一切与本人无关!

工具仅用于合法渗透测试,切勿违法,一切与本人无关!

参考文章:

相关逆向sqlite的文章

 

sqlite3的函数详解


[招聘] 欢迎你加入看雪团队!

最后于 2020-12-2 18:18 被初学逆向编辑 ,原因:
收藏
点赞7
打赏
分享
最新回复 (18)
雪    币: 8573
活跃值: 活跃值 (4149)
能力值: ( LV13,RANK:370 )
在线值:
发帖
回帖
粉丝
TkBinary 活跃值 5 2020-12-1 16:50
2
0
可以可以
雪    币: 113
活跃值: 活跃值 (186)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
编程小白 活跃值 2020-12-1 17:05
3
0
雪    币: 1519
活跃值: 活跃值 (425)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
basketwill 活跃值 1 2020-12-1 18:43
4
0
这个某浏览器的  进程名 已经漏出来了 
雪    币: 1277
活跃值: 活跃值 (1056)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
初学逆向 活跃值 1 2020-12-1 20:03
5
0
basketwill 这个某浏览器的 进程名 已经漏出来了
米有关系 本来就是分享36X浏览器的撒
雪    币: 4856
活跃值: 活跃值 (2335)
能力值: (RANK:452 )
在线值:
发帖
回帖
粉丝
顾何 活跃值 8 2020-12-1 21:17
6
0
感谢分享
雪    币: 959
活跃值: 活跃值 (26)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
lynxtang 活跃值 2020-12-1 21:34
7
0
学习了,厉害厉害
雪    币: 133
活跃值: 活跃值 (238)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
柒雪天尚 活跃值 2020-12-1 21:39
8
1
360: 为了突出文章精彩,彰显作者才华,我司的律师函正在加速前往作者手中
雪    币: 8
活跃值: 活跃值 (139)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hackbs 活跃值 2020-12-2 18:40
9
0

sqlite3_open 断到 assis2.db需要什么特别的触发点么,我添加账号密码都没断到。

雪    币: 1277
活跃值: 活跃值 (1056)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
初学逆向 活跃值 1 2020-12-2 18:43
10
0
hackbs sqlite3_open 断到 assis2.db需要什么特别的触发点么,我添加账号密码都没断到。
你好 这个是上个星期弄 你可以看下 https://down.360safe.com/se/360se12.2.1678.0.exe 这个版本的
雪    币: 3527
活跃值: 活跃值 (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hades一KXXY 活跃值 2020-12-3 09:36
11
1

这里有多次调用到sqlite3_open()这个函数,楼上那位找的是第一次调用的位置

第二次才是调用数据解密的位置

最后于 2020-12-3 09:47 被Hades一KXXY编辑 ,原因:
雪    币: 3527
活跃值: 活跃值 (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hades一KXXY 活跃值 2020-12-3 09:37
12
0
初学逆向 你好 这个是上个星期弄 你可以看下 https://down.360safe.com/se/360se12.2.1678.0.exe 这个版本的

这个和版本没什么关系,更新并没有修改对应的代码.

最后于 2020-12-3 09:49 被Hades一KXXY编辑 ,原因:
雪    币: 8
活跃值: 活跃值 (139)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hackbs 活跃值 2020-12-3 09:47
13
0
Hades一KXXY 这个和版本没关系

新版的加密那块好像有变动,3xx反应够快的。

最后于 2020-12-3 09:47 被hackbs编辑 ,原因:
雪    币: 3527
活跃值: 活跃值 (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hades一KXXY 活跃值 2020-12-3 09:51
14
0
hackbs Hades一KXXY 这个和版本没关系 新版的加密那块好像有变动,3xx反应够快的。
你指的是打开数据库?,你看我回的帖子,你找错地方了.
雪    币: 8
活跃值: 活跃值 (139)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hackbs 活跃值 2020-12-3 10:01
15
0
Hades一KXXY 这里有多次调用到sqlite3_open()这个函数,楼上那位找的是第一次调用的位置第二次才是调用数据解密的位置

确实和老哥说的一样有两处地方,下面的才是真正解密的地方。感谢了!

雪    币: 3527
活跃值: 活跃值 (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hades一KXXY 活跃值 2020-12-3 10:23
16
0
hackbs 确实和老哥说的一样有两处地方,下面的才是真正解密的地方。感谢了!

第一次调用打开的是数据库的配置文件

最后于 2020-12-3 10:23 被Hades一KXXY编辑 ,原因:
雪    币: 1961
活跃值: 活跃值 (557)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
mudebug 活跃值 2020-12-3 10:27
17
0
取证要求合法渠道的。你这样取证算合法么
雪    币: 8
活跃值: 活跃值 (139)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hackbs 活跃值 2020-12-7 14:53
18
0
加密算法入口那个Call 好难跟啊,lz是根据网站填入密码后根据登录管家里面有没有记录数据来判断是哪个具体Call 跟的吗
雪    币: 432
活跃值: 活跃值 (61)
能力值: ( LV6,RANK:81 )
在线值:
发帖
回帖
粉丝
mlgbwoai 活跃值 1 2020-12-12 17:19
19
0
还没搞定登录用户吗?
游客
登录 | 注册 方可回帖
返回