瞎猫碰到死耗子,安卓nt_qq数据库密钥算法
这个我实际上弄了很久了,一开始更新的时候,发现数据库操作都是在so里,那时候是在libkernel.so
里直接hook sqlcipher的密钥函数拿到的密钥,32位字符串,很容易让人联想到md5,但是没有找到在哪里计算的
最近又想着做一下,这时打开数据库的so就变了,这是easyFrida
的sofileopen
插件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
,可以看到aa030bae477df5ced6093d4702523a3a
和d28951ee5178259c2916f46f37a14c30
,前者与数据库路径有关,后者就是数据库密钥
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)+数据库文件头中的随机字符串)
,就是0x1208
和0x1a07
之间的字符串
那么现在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密钥就是从那学的,结果发现人家已经把算法搞出来了。。差距啊
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异