在之前的文章中,我直接用现成的代码实现了解密Msg3.0.db。不过,随着QQ更新到了9.7.25.29417,解密的脚本(pcqq_get_key.py)失效了,于是我尝试手动对QQ下断点来获取key。
逆向KernelUtil.dll
首先用IDA逆向KernelUtil.dll,定位到key所在的地方。
参考已有的分析帖子,不需要自己做更多的深入分析,可以直接得知,key可以在innerOpen函数中定位到,而innerOpen随后又会将需要解密的数据库和key传入checkDB下方的那个函数,也就是sub_56DD6EC3:

在这个函数中,第一个参数是数据库,第二个参数是key的内容,第三个参数是key的长度,此处为16:

hook登录过程
先打开QQ,当前处于登录前的状态,然后用x32dbg附加到QQ的进程:

直接找到KernelUtil.dll中的innerOpen函数:

跳到innerOpen对应的指令起点,然后往下翻,先找到checkDB的调用指令,在其之后的下一个call就是刚才在IDA中看到的sub_56DD6EC3函数了,也就是图中的kernelutil.72FD6EC3:

继续跳到kernelutil.72FD6EC3的指令起点,下一个断点:

开始登录QQ。登录的过程中会多次来到断点的位置,不过通常前几次都是在打开Tencent Files\All Users\QQ\Registry2.0.db文件,此处需要注意观察kernelutil.72FD6EC3的第一个参数(ebp+8)的内容,直到遇到Msg3.0。前面有提到过,该函数的第二个参数(ebp+C)就是key的内容,直接查看它在内存中的数据,这一行十六进制就是key了:

解密
由于KernelUtil.dll的变化,pcqq_rekey_to_none.cpp也不能用了,关键问题在于找不到open函数。
不过,这里不涉及到对QQ的hook了,因此可以从dllme.com中下载一个
9.7.23.29368版本的KernelUtil.dll,重命名一下文件名,放进KernelUtil.dll所在的目录中,顺便pcqq_rekey_to_none.cpp中也要将KernelUtil.dll字符串换成这个文件名,这样解密的过程中就是加载旧版的dll了。
接下来的步骤就跟之前一样了,把key填入pcqq_rekey_to_none.cpp中,然后编译,移动Msg3.0.db和编译好的a.exe到KernelUtil.dll所在的目录中,以管理员身份运行a.exe,即可解密。
修改获取key的脚本
每次都需要断点hook登录过程,未免有些不方便,因此,我还是想尝试修改pcqq_get_key.py,让它支持新版的KernelUtil.dll。
观察运行错误,可以发现,其根本原因在于找不到name_function的十六进制特征了。
继续往下看,发现这个函数是被用与从参数中获取数据库文件路径的。而上面已经提到过,sub_56DD6EC3函数的第一个参数实际上就是数据库的文件路径。而通过十六进制特征的匹配可以发现,脚本代码中获取到的key_function正是这个函数。
那就省事了,在代码中把name_function相关的内容都去掉,然后直接从key_function的第一个参数中获得数据库文件路径:

经过这一简单的修改,就可以通过这个脚本来获取key了。