瞎猫碰到死耗子,安卓nt_qq数据库密钥算法

这个我实际上弄了很久了,一开始更新的时候,发现数据库操作都是在so里,那时候是在libkernel.so里直接hook sqlcipher的密钥函数拿到的密钥,32位字符串,很容易让人联想到md5,但是没有找到在哪里计算的

最近又想着做一下,这时打开数据库的so就变了,这是easyFridasofileopen插件hook出来的结果,目前使用的是libbasic_share.so这个so

不过这个so在apk包里面并没有,应该是运行的时候释放出来的

在应用目录里是可以拿到的

结果这个so的导出表里有md5相关的函数,而以前的libkernel并没有,感谢qq!!!!!!!!!!!!!

而且很多以前没有函数名的函数,这次也能看到函数名了,就比如密钥初始化函数

 hook一下拿到密钥(直接attach,然后在登录、查看个人信息等时机可以hook到)

setImmediate(function () {
    Java.perform(function () {
        function buf2str(buffer) {
            let result = '';
            const byteArray = new Uint8Array(buffer);
            for (let i = 0; i < byteArray.length; i++) {
                result += String.fromCharCode(byteArray[i]);
            }
            return result;
        }
        var baseAddr = Module.findBaseAddress('libbasic_share.so');
        console.log(`baseAddr:${ baseAddr }`);
        var keyFuncAddr = baseAddr.add(5083692);
        //nt_sqlite3_key_v2的地址
        Interceptor.attach(keyFuncAddr, {
            onEnter: function (args) {
                var nk = args[3].toInt32();
                var pk = args[2].readByteArray(nk);
                console.log('pKey--------->' + buf2str(pk));
            },
            onLeave: function (retval) {
            }
        });
    });
});

拿到的这个key,非常符合md5的结果,前面又有md5导出函数,干脆就看一下?

先hook了MD5DigestToBase16,可以看到aa030bae477df5ced6093d4702523a3ad28951ee5178259c2916f46f37a14c30,前者与数据库路径有关,后者就是数据库密钥

setImmediate(function () {
    function readStdString(str) {
        const isTiny = (str.readU8() & 1) === 0;
        // 检查是否为小字符串
        if (isTiny) {
            return str.add(1).readUtf8String();	// 读取小字符串
        }
        return str.add(2 * Process.pointerSize).readPointer().readUtf8String();	// 读取大字符串
    }
    Java.perform(function () {
        Interceptor.attach(Module.findExportByName('libbasic_share.so', '_ZN4xpng17MD5DigestToBase16ERKNS_9MD5DigestE'), {
            onEnter: function (args) {
            },
            onLeave: function (retval) {
                console.log('md5 ret----------->' + readStdString(retval));
            }
        });
    });
});

然后再看一下update函数的参数,能够看到到底是计算了哪些,能看到密钥对应的参数是c8d05c49e391e2e1d0bda579cc5250085sWvEGXu,恰好是上一条u_bv0QrXXA4YqAB3PaYr3PpQ的md5结果,而前面路径中的值则和这个结果也有关联,是md5(md5(u_bv0QrXXA4YqAB3PaYr3PpQ)+'nt_kernel'),从而得到aa030bae477df5ced6093d4702523a3a,那么密钥参数c8d05c49e391e2e1d0bda579cc5250085sWvEGXu的后面几个字符串是哪来的?

setImmediate(function () {
    Java.perform(function () {
            Interceptor.attach(Module.findExportByName("libbasic_share.so",'_ZN4xpng9MD5UpdateEPA88_cPKhm'), {
                onEnter: function (args) {
                    console.log('update data ------->', args[1].readUtf8String(args[2].toInt32()));
                },
                onLeave: function (retval) {
                }
            });
    });
})

居然都是在数据库里?

原来是在文件头写好的

那么密钥的算法就是md5(md5(u_bv0QrXXA4YqAB3PaYr3PpQ)+数据库文件头中的随机字符串),就是0x12080x1a07之间的字符串

那么现在u_bv0QrXXA4YqAB3PaYr3PpQ这一串又是什么呢?其实这个就是QQ的uid,之前分析手机大师日志的时候,恰好看到了,从mmkv(读取这玩意儿还不是个容易事儿,得通过腾讯开源的mmkv/Python去编译。。索性搓了一个解析工具,省的换机器要重新编译)中读取用户的uid,实际上是通过QQ号(uin)去获取的

因此最终,数据库路径中的哈希=md5(md5(uid)+nt_kernel),密钥=md5(md5(uid)+文件头字符串)

而要打开数据库的话,需要先把数据库的前1024字节删掉,然后进行解密,解密参数中,hmac算法应该是文件头中的sha1,kdf_iter是4000,其他都是sqlcipher4的配置

打开数据库后40800字段是聊天记录,内容是protobuf编码,45101是聊天内容

ForensicsTool集成了ntqq数据库解密

mmkvReader会同步上传至GitHub

想上传到之前刷到过的一个github,hook密钥就是从那学的,结果发现人家已经把算法搞出来了。。差距啊

posted @   WXjzc  阅读(416)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示