-
-
[旧帖]
[原创]对QQ Registry.db的再次分析
0.00雪花
-
发表于:
2012-9-16 22:33
8391
-
[旧帖] [原创]对QQ Registry.db的再次分析
0.00雪花
我还是要改一下的我的分析,我的分析连QQ版本都没有说,因此这次完善下
首先说下我原先的的目的,我的目的很简单,就是想编程实现取消QQ记住密码选项
原先我认为QQ记住密码选项肯定是某一样设置,经过网上搜索 得知All Users\QQ下
Registry.db里面包含QQ用户列表,将它删掉,QQ列表就全空了,看来设置就在这个文件
里面,但是删除不是我的目的,我的目的是取消记住密码选项
没办法,随手把QQ拖到OD里,谁让它不加壳,不反调试,我的QQ是QQ2012beta3,估计QQ2011也差不多
先说一下Registry.db文件,它是复合文档,里面有很多流和存储,用DBViewer打开该文件
明眼人一下子就看到了Main_LoginAccountList流,QQ用户列表信息百分之百放在这里面
刚才不是说把QQ拖到OD里吗,先运行QQ,QQ肯定要打开Registry.db的,大家都知道下什么断点了吧,
我下的是ZwCreateFile,原因是所有文件打开操作都要经过它,下好断点后登陆QQ,或者点一下QQ用户
列表,QQ自动断下来了,如果你电脑正常的话,第一个就是打开All Users\QQ目录下的Registry.db
查看堆栈信息,继续往下翻,能看到C:\Program Files\Tencent\QQ\Users\All Users\QQ\Registry.db,
Main_LoginAccountList,GlobalDataRegistry:等字符串,还能看到
学过C++的都知道,TXOpenStorage@@YAJPB_WHAAV?$scoped_refptr@VCStorage@@@@AAV?$scoped_refptr@VCCompoundDocument@@@@@Z
它类似如C++里面的StgOpenStorage函数,可见这里是打开复合文档Registry.db的,
通过IDA我们知道,它的原型是
int __cdecl TXOpenStorage(void *lpFileName, int, int, int);
但我们接下来要怎么做,当然是看它是怎样解析Main_LoginAccountList流的
既然这样,它肯定会读取流吧,通过IDA我们看到,有一个符合我们要求的函数,或者说是
类成员,这个就是符号?Read@CStream@@QAEJPAXKPAK@Z,原型是
long __thiscall CStream::Read(void *, unsigned long, unsigned long *);
另外还可以看到它的地址是300520A8,取消上面的断点,下300520A8,也就是 CStream::Read(void *, unsigned long, unsigned long *)方法,继续运行,程序
如期的停在了300520A8,返回该函数后,到达下面的代码
查看堆栈
可见,CStream::Read第一个参数是缓存区,第二个是读取的大小,第三个未知,这时
016100B8就是读取到的流的数据,也就是那堆TD,TA,
熟悉OD的人肯定知道下硬件访问断点到016100B8到它的首字节,看下它是怎么解密这一大串
数据的,如果不出错的话,程序停在了这里,这是在用memcpy复制缓存区,
它将016100B8的数据复制到01610D20,不用我说了大家也知道再次下硬件断点01610D20吧,
取消原先的016100B8断点,下01610D20的首字节,运行程序,停在了这里
解密的海洋已经达到,其中ESI就01610D20,因为它会对Main_LoginAccountList流进行加密
如果该函数返回后01610D20,应该就是这样的
我这里的堆栈地址是0x01607AA8,原先应该是0x01610D20,因为我重新弄了一遍
至于解密方法,我分析不出来,技术有限,但是通过跟进它的解密函数,还是能得出一些信息
上一部分是介绍了逆向的过程,这部分主要介绍腾讯DB的格式,我只能说以下全是我推测出来,正确性未知
用DBViewer打开Main_LoginAccountList的流,DBViewer你们一定在网上找不到吧,稍后我会发出来的
大概每个人的这个流前两个字节都TD,还依稀的看TA,TD等
我只能先说腾讯的数据类型,这样好理解一些
0x01 相等于C++里面的BOOL(4个字节)
0x02 相等于C++里面的char(1个字节)
0x04 相等于C++里面的short(2个字节)
0x06 相等于C++里面的unsigned int(4个字节)
0x07 相等于C++里面的int(4个字节)
0x08 Uncode字符串
0x09 缓存区,char数组
0x0b 不知道名字,只知道是以Td开头
0x0c 不知道名字,只知道是以Ta开头
关于TD的格式
0~2 两个字节,总是TD,也就是54 44
2~4 两个字节,保留,总是01 01
4~6 两个字节,条目数量
到了这里,就要看,条目数量了,如果条目数量为0的话,下面的就没了,如果为2的话就说明有两条数据,这里假如为2
第一条数据
6~7 一个字节,数据类型,就是上面说的腾讯数据类型的哪些值
7~9 两个字节,数据的名字长度,这里叫nameLen1,简称L1,解密字符串的关键
9~9+L1 L1个字节,数据的名字,加密的Unicode字符串
9+L1~9+L1+4 4个字节,数据的长度,这里叫DataLen1,简称N1
9+L1+4~9+L1+4+N1 N1个字节,看6~7的数据类型,如果是是Uncode字符串的话,还要进行解密,如果是数字等的话,就不用了
第二条数据,够长了
9+L1+4+N1~9+L1+4+N1+1 一个字节,数据类型,就是上面说的腾讯数据类型的哪些值
9+L1+4+N1+1~9+L1+4+N1+1+2 两个字节,数据的名字长度,这里叫nameLen2,简称L2,解密字符串的关键
9+L1+4+N1+1+2~9+L1+4+N1+1+2+L2 L2个字节,数据的名字,加密的Unicode字符串
9+L1+4+N1+1+2+L2~9+L1+4+N1+1+2+L2+4 4个字节,数据的长度,这里叫DataLen2,简称N2
9+L1+4+N1+1+2+L2+4~9+L1+4+N1+1+2+L2+4+N2 N2个字节,如果是是Uncode字符串的话,还要进行解密,如果是数字等的话,就不用了
终于说完了
关于TA的格式,和TD差不多
0~2 两个字节,总是TA,也就是54 41
2~4 两个字节,保留,总是01 01
4~8 四个字节,条目数量,TD的是两个字节
到了这里,就要看,条目数量了,如果条目数量为0的话,下面的就没了,如果为1的话就说明有一条数据,这里假如为1
第一条数据
8~9 一个字节,数据类型,注意,没有数据名字了,光数据了
9~13 四个字节,数据长度,叫DataLen1,简称N1
13~13+N1 数据,看8~9的数据类型,以确定数据
可以看到腾讯的数据类型里面包含TD,和TA,这说明一个TD或者TA的数据还可能是一个TD或者TA的数据,就这样嵌套
首先说下解密字符串的方法,用C++表示就是这样
/*
szSrcBuf 要解密的缓冲区
szDestBuf 解密之后的缓冲区
nLen 要解密的缓冲区长度
*/
void Decrypt(unsigned char *szSrcBuf, unsigned char *szDestBuf, unsigned int nLen)
{
unsigned int key;
unsigned int i;
if(!szSrcBuf || !szDestBuf || !nLen)
return;
key = (nLen >> 8) | (nLen & 0xff);
for(i = 0; i < nLen; i++)
{
szDestBuf[i] = ~szSrcBuf[i];
szDestBuf[i] ^= key;
}
szDestBuf[i] = '\0';
szDestBuf[i + 1] = '\0';
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!