Feature request: 读取新版QQNT数据库
ygjsz opened this issue · comments
现在腾讯在推NT架构的QQ,要搞全平台统一,然后手机版的NT架构的QQ也开始内测了。
最新的手机版内测QQNT上的数据库架构已经大改了,变得和Windows/Mac/Linux版QQNT一样了。
版本:8.9.58.11050
如图 老版本的数据库仍然存在,但是很明显聊天记录已经不存放在老库里面了
现在新QQNT聊天记录数据库的位置是/databases/nt_db
从文件名来看这个数据库架构和电脑版QQNT是一样的
(Windows版QQNT数据库)
目前还没研究出新数据库密钥存放的位置以及新数据库的格式
(从文件头来看是SQLite3?)
不知道有没有希望搞定新版的数据库解密
另:手机版QQNT内测包下载链接:https://downv6.qq.com/qqweb/QQ_1/android_apk/qq_8.9.58.11050_64.apk
(就算没有内测资格也能用,在弹出内测活动已结束的窗口的时候按两下返回就可以把那个窗口关掉)
(不建议用大号测试,这个内测QQNT一旦登录之后就会把所有的老库里的聊天记录全都迁移进新库,无法撤销)
另2:MacQQNT的数据库位置:/Users/{用户名}/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/nt_qq_{一串ID}/nt_db
目测数据库格式和其他平台是一样的
Electron那个?有计划,有时间可能去试一试
运气好的话跟msg3.0.db一样的加密方式就会很轻松
是的 大佬加油!
然后Windows版貌似是在wrapper.node
里,是编译为 PE 可执行文件格式
由进程标题为QQ
的进程以dll
方式加载,用了sqlcipher
,理论上 hook nt_sqlite3CodecAttach
即可
那应该是比较简单的,用Frida hook一下就可以了
(如果没改sqlcipher算法的话)
我这边貌似也不太行……只要注入 Frida 就会段错误/拒绝注入,想必是有反调一类的
似乎 Windows 上 key 长度为 16(?
为啥感觉不是很对的样子
似乎 Windows 上 key 长度为 16(? 为啥感觉不是很对的样子
我在这边没有什么进展,要是有什么进度可以给我发发
顺带一提,windows 上 key 长就是 16
但是我不知道 BLOB 中的内容如何解码
“打开”是用社区版sqlcipher就可以了吗?
是的,开源的那个就可以
Blob我明天再看看
PR 的话
好像这个函数在不同版本的 wrapper.node 里二进制不太一样,比较难写成脚本。。。。
PR 的话
好像这个函数在不同版本的 wrapper.node 里二进制不太一样,比较难写成脚本
有什么特征汇编指令序列一类的吗?再不济用户手动输入函数地址都没问题,毕竟这个repo本身就不是为了提供全套解决方案的
就算是丢个 教程.md 也很有用!
那我试着写个教程
更新 已经寄了
8.9.78+上vmp了
更新 已经寄了 8.9.78+上vmp了
8.9.78+包括8.9.78.12275吗?我这儿显示是最新版本了,但是能hook到呢,只是地址变了。
就是hook出来密钥还是没法解密,这方面还需要研究。
setTimeout(function () {
let base_addr = Module.findBaseAddress("libkernel.so");
console.log("libkernel.so base address: " + base_addr);
// nt_sqlite3_key_v2, sub_1CCE4DC
let nt_sqlite3_key_v2_addr = base_addr.add(0x1cce4dc);
console.log("nt_sqlite3_key_v2_addr: " + nt_sqlite3_key_v2_addr);
Interceptor.attach(nt_sqlite3_key_v2_addr, {
onEnter: function (args) {
console.log("pKey: " + Memory.readCString(args[2]));
console.log("nKey: " + args[3].toInt32());
},
});
}, 1200);
解密的话你看下那个仓库里的android.md
,我没试过。
看看有没有什么特征字节?(也就是single_function
的参数)
或者我再拉出来我之前写的一些小工具找offset吧…
@yllhwa
解密的话你看下那个仓库里的
android.md
,我没试过。 @yllhwa
我测试是不行的,我怀疑那是Windows的解密方式
看起来qq用的是4.5.1版本的sqlcipher,我打算bindiff下看看是不是哪儿有魔改的地方
解密的话你看下那个仓库里的
android.md
,我没试过。 @yllhwa我测试是不行的,我怀疑那是Windows的解密方式
那我最近也试一试,不行的话去加个“可能不可靠”的标志
呼,搞了一晚上终于搞好了。
之前attach会出现问题应该是奇怪的权限问题引起的,将导出地址设置为公共目录即可。
以下代码对应的安卓qqnt版本为8.9.78.12275。
不保证不会对聊天记录产生影响(
// frida -U -f com.tencent.mobileqq -l final.js
const DATABASE_URI = "/data/user/0/com.tencent.mobileqq/databases/nt_db/nt_qq_{CHNAGE_THIS_TO_YOURS}/nt_msg.db";
// FOR LOG
let SQLITE3_EXEC_CALLBACK_LOG = true;
let index1 = 0;
let xCallback = new NativeCallback(
(para, nColumn, colValue, colName) => {
if (!SQLITE3_EXEC_CALLBACK_LOG) {
return 0;
}
console.log();
console.log(
"------------------------" + index1++ + "------------------------"
);
for (let index = 0; index < nColumn; index++) {
let c_name = colName
.add(index * 8)
.readPointer()
.readUtf8String();
let c_value = "";
try {
c_value =
colValue
.add(index * 8)
.readPointer()
.readUtf8String() ?? "";
} catch {}
console.log(c_name, "\t", c_value);
}
return 0;
},
"int",
["pointer", "int", "pointer", "pointer"]
);
// CODE BELOW
let get_filename_from_sqlite3_handle = function (sqlite3_db) {
// full of magic number
let zFilename = "";
try {
let db_pointer = sqlite3_db.add(0x8 * 5).readPointer();
let pBt = db_pointer.add(0x8).readPointer();
let pBt2 = pBt.add(0x8).readPointer();
let pPager = pBt2.add(0x0).readPointer();
zFilename = pPager.add(208).readPointer().readCString();
} catch (e) {}
return zFilename;
};
setTimeout(function () {
let base_addr = Module.findBaseAddress("libkernel.so");
console.log("libkernel.so base address: " + base_addr);
// sqlite3_exec -> sub_1CFB9C0
let sqlite3_exec_addr = base_addr.add(0x1cfb9c0);
console.log("sqlite3_exec_addr: " + sqlite3_exec_addr);
let sqlite3_exec = new NativeFunction(sqlite3_exec_addr, "int", [
"pointer",
"pointer",
"pointer",
"int",
"int",
]);
let target_db_handle = null;
let js_sqlite3_exec = function (sql) {
if (target_db_handle == null) {
return -1;
}
let sql_pointer = Memory.allocUtf8String(sql);
return sqlite3_exec(target_db_handle, sql_pointer, xCallback, 0, 0);
};
// ATTACH BELOW
Interceptor.attach(sqlite3_exec_addr, {
onEnter: function (args) {
// sqlite3*,const char*,sqlite3_callback,void*,char**
let sqlite3_db = ptr(args[0]);
let sql = Memory.readCString(args[1]);
let callback_addr = ptr(args[2]);
let callback_arg = ptr(args[3]);
let errmsg = ptr(args[4]);
let databasae_name = get_filename_from_sqlite3_handle(sqlite3_db);
if (databasae_name == DATABASE_URI) {
console.log("sqlite3_db: " + sqlite3_db);
console.log("sql: " + sql);
target_db_handle = sqlite3_db;
}
},
});
setTimeout(function () {
let ret = js_sqlite3_exec(
`ATTACH DATABASE '/storage/emulated/0/Download/plaintext.db' AS plaintext KEY '';SELECT sqlcipher_export('plaintext');DETACH DATABASE plaintext;`
);
console.log("js_sqlite3_exec ret: " + ret);
}, 4000);
}, 1200);
主要就是几个点:
- 用
===
/!==
而非==
/!=
- databasae_name 那里直接
indexOf
一下基本就行了,个人认为没必要用完整路径 - hook libkernel 的时候可以去 hook dlopen,而非等待常数时间(ref,我也不太记得这个能不能用)
下一步的话可以看看insert是怎么实现的/能不能hook到 大概
以及,各位有兴趣进一下这个组织吗
https://github.com/QQBackup
发个pr?我稍微修改一下后 merge 了
主要就是几个点:
- 用
===
/!==
而非==
/!=
- databasae_name 那里直接
indexOf
一下基本就行了,个人认为没必要用完整路径- hook libkernel 的时候可以去 hook dlopen,而非等待常数时间(ref,我也不太记得这个能不能用)
issue发在另一个仓库了QQBackup/qq-win-db-key#12
熬夜赶工,有些地方可能比较hacky(
另外剩余的工作可能就是
- 跨版本适配(有必要吗?)
- 对解密后的数据库进行解析,因为字段名和编码方式都不太明确,不清楚Windows和Mac上解密出来是否类似。不过好在没有旧版QQ奇怪的混淆了。
issue发在另一个仓库了QQBackup/qq-win-db-key#12 熬夜赶工,有些地方可能比较hacky(
主要是标记成contributor,虽然你不在意的话也没问题
- 跨版本适配(有必要吗?)
只要他不把那个log的字符串删掉,直接搜adrp和ldr/add命令的机器码应该就行,具体我有时间再看一下
(以及,我感觉定位offset这一部分可以从脚本里抽离出来,拿Python之类的写)
- 对解密后的数据库进行解析,因为字段名和编码方式都不太明确,不清楚Windows和Mac上解密出来是否类似。不过好在没有旧版QQ奇怪的混淆了。
确实,所以能多hook几个sqlite3_prepare之类的可能会有点用
关于prepare等: https://zhuanlan.zhihu.com/p/583446952
主要是标记成contributor,虽然你不在意的话也没问题
没问题,麻烦整理下(
确实,所以能多hook几个sqlite3_prepare之类的可能会有点用
原来sqlite3_prepare走的和sqlite3_exec不是一条路啊,我惯性思维觉得prepare底层调exec了,难怪觉得少了很多调用。
不过prepare里面的字段名称还是[40055],[40010],[40027]
这样的无意义数字,感觉是上层进行了某种映射
关于跨版本适配,貌似FF4302D1FD7B03A9FC6F04A9FA6705A9F85F06A9F65707A9F44F08A9FDC3009154D03BD5881640F9F80304AAF50303AAF60302AA
这个sig是没变的,直接用大概就行?
更新 已经寄了 8.9.78+上vmp了
以及,想问下你是从何看出有vmp的?在你那边有造成什么具体影响吗?
NT QQ的(目前无法解析的)聊天记录能否使用QQ自带的聊天记录迁移功能迁移到到非NT QQ?