Open ygjsz opened 1 year ago
Electron那个?有计划,有时间可能去试一试 运气好的话跟msg3.0.db一样的加密方式就会很轻松
是的 大佬加油!
目前是定位到了相关代码在libkernel.so
里,也是用的和Msg3.0.db
一个思路的 sqlite3 加密。就是具体的算法可能有改动
周末试着动态调试一下吧
(具体怎么找的:find . | xargs grep -Hn nt_msg.db {} 2>/dev/null
)
然后Windows版貌似是在wrapper.node
里,是编译为 PE 可执行文件格式
由进程标题为QQ
的进程以dll
方式加载,用了sqlcipher
,理论上 hook nt_sqlite3CodecAttach
即可
那应该是比较简单的,用Frida hook一下就可以了 (如果没改sqlcipher算法的话)
试试? https://github.com/Young-Lord/qq-win-db-key/blob/master/android.md
我这不太行 frida貌似跑不起来的样子 (SELinux已经是permissive了 有空我换个设备再试一下
我这边貌似也不太行……只要注入 Frida 就会段错误/拒绝注入,想必是有反调一类的
成了。 以及,如果你跑不起来也可以再试试,看起来这个只是在QQ未运行时不工作而已 (不过也挺奇怪的,之前好像是看到有几处地方相关代码被inline了,但也看不出逻辑)
似乎 Windows 上 key 长度为 16(? 为啥感觉不是很对的样子
似乎 Windows 上 key 长度为 16(? 为啥感觉不是很对的样子
我在这边没有什么进展,要是有什么进度可以给我发发
🥳🥳🥳 方法
hook nt_sqlite3_key_v2 拿 key
删掉 nt_msg.db 前 1024 个字符
打开的时候 pbkdf iter=4000
顺带一提,windows 上 key 长就是 16
但是我不知道 BLOB 中的内容如何解码
🥳🥳🥳 方法
hook nt_sqlite3_key_v2 拿 key
删掉 nt_msg.db 前 1024 个字符
打开的时候 pbkdf iter=4000
“打开”是用社区版sqlcipher就可以了吗?看起来我后面这两步都没有做XD 方便的话可以提个pr,感谢 以及,blob的话可以考虑下Protobuf,盲猜也是写在native层
“打开”是用社区版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);
完美。 发个pr?我稍微修改一下后 merge 了
主要就是几个点:
===
/!==
而非 ==
/!=
indexOf
一下基本就行了,个人认为没必要用完整路径下一步的话可以看看insert是怎么实现的/能不能hook到 大概
以及,各位有兴趣进一下这个组织吗 https://github.com/QQBackup
发个pr?我稍微修改一下后 merge 了
主要就是几个点:
- 用
===
/!==
而非==
/!=
- databasae_name 那里直接
indexOf
一下基本就行了,个人认为没必要用完整路径- hook libkernel 的时候可以去 hook dlopen,而非等待常数时间(ref,我也不太记得这个能不能用)
issue发在另一个仓库了https://github.com/QQBackup/qq-win-db-key/issues/12 熬夜赶工,有些地方可能比较hacky(
另外剩余的工作可能就是
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的?在你那边有造成什么具体影响吗?
猜对了。hook prepare + bind可以得到insert一类的具体sql语句 sqlite3_prepare_v2也很好找,sqlite3_exec函数里直接就有调用(前文中这个版本 8.9.78.12275 是0x1d2da74) (顺带一提,是 exec 调 prepare & step )
NT QQ的(目前无法解析的)聊天记录能否使用QQ自带的聊天记录迁移功能迁移到到非NT QQ?
iOS Frida hook 得到了密钥,不过似乎还是无法解密,长度32位,只有字母和数字。
iOS Frida hook 得到了密钥,不过似乎还是无法解密,长度32位,只有字母和数字。
看看这个? https://github.com/QQBackup/qq-win-db-key/blob/master/android_dump.js
以及怎么跨年还在折腾这个
iOS Frida hook 得到了密钥,不过似乎还是无法解密,长度32位,只有字母和数字。
看看这个? https://github.com/QQBackup/qq-win-db-key/blob/master/android_dump.js ~以及怎么跨年还在折腾这个~
😂
不过已经解决了,在iOS上 HMAC 算法需要设置为 HMAC_SHA1
快过年了没事儿干又来折腾了😂
import sqlite3
import blackboxprotobuf
conn = sqlite3.connect('plaintext.db')
c = conn.cursor()
print ("数据库打开成功")
def get_message_from_single(message):
# print(message)
if isinstance(message, list):
return [get_message_from_single(m) for m in message]
try:
message_id = message.get("45001")
message_type = message.get("45002")
if message_type == 1:
# 普通文本消息
message_content = message.get("45101").decode("utf-8")
elif message_type == 2:
# 图片消息
local_name = message.get("45402") # ?
if message.get("45804"):
picture_url = "https://c2cpicdw.qpic.cn"+ message.get("45804").decode("utf-8") # 45802, 45803, 45804 区别?(可能是清晰度)
else:
picture_url = ""
message_content = f"[图片消息 {picture_url}]"
elif message_type == 3:
# 文件消息
file_name = message.get("45402")
message_content = f"[文件消息 {file_name}]"
elif message_type == 6:
# 表情消息
message_content = "[表情消息]" # TODO
elif message_type == 10:
# 应用消息
# message_content = message.get("47901")
message_content = "[应用消息]"
else:
message_content = "[未知消息类型]"
if message_content == "[未知消息类型]":
# print(message)
pass
if message_content == None:
message_content = ""
return message_content
except Exception as e:
print(e)
return ""
def get_message_from_raw(raw_message):
(messages, typedef) = blackboxprotobuf.decode_message(raw_message)
if not isinstance(messages, list):
messages = [messages]
results = []
for message in messages:
message = message.get("40800")
results.append(get_message_from_single(message))
return results
cursor = c.execute("SELECT * from c2c_msg_table")
for row in cursor:
data = row[17]
print(get_message_from_raw(data))
conn.close()
现在的效果:
我感觉数据库里面那些奇怪的字段名和protobuf里面的id有关。 估计只能手搓protobuf定义了。
先手搓了一份
syntax = "proto3";
message Message { repeated SingleMessage messages = 40800; }
message SingleMessage {
uint64 messageId = 45001;
uint32 messageType = 45002;
// 1:文字,2:图片,3:文件,6:表情,7:回复,
// 8:提示消息(中间灰色),10:应用消息
// 21:电话
// 26:动态消息
// 回复消息
string senderId = 40020;
string receiverId = 40021;
// 文字消息
string messageText = 45101;
// 文件消息
string fileName = 45402;
uint64 fileSize = 45405;
uint64 sendTimestampFile = 45505; // ?
// 图片消息
string imageUrlLow = 45802;
string imageUrlHigh = 45803;
string imageUrlOrigin = 45804;
string imageText = 45815;
uint32 senderUid = 47403;
uint32 sendTimestamp = 47404;
uint32 receiverUid = 47411;
SingleMessage replyMessage = 47423;
// 表情消息
// 1: QQ 系统表情,2: emoji 表情
// https://bot.q.qq.com/wiki/develop/api/openapi/emoji/model.html
uint32 emojiId = 47601;
string emojiText = 47602;
// 应用消息
string applicationMessage = 47901;
// 语音消息
string callStatusText = 48153;
string callText = 48157;
// 动态消息
FeedMessage feedTitle = 48175;
FeedMessage feedContent = 48176;
string feedUrl = 48180;
string feedLogoUrl = 48181;
uint32 feedPublisherUid = 48182;
string feedJumpInfo = 48183;
string feedPublisherId = 48188;
// 提示消息
string noticeInfo = 48214;
string noticeInfo2 = 48271; // ?
}
message FeedMessage { string text = 48178; }
大佬们,我想问一下到目前有可行的方案吗?我的qq版本目前是9.0.17.15190
大佬们,我想问一下到目前有可行的方案吗?我的qq版本目前是9.0.17.15190
目前的方案是安卓端有root就可以,见教程。
好难T.T
I'm currently trying to open the encrypted nt_msg.db of QQNT (Windows) in DB Browser with SQLCipher with the passphrase obtained as mentioned in the [QQ NT Windows database decryption + image/file cleanup] (https://github.com/Mythologyli/qq-nt-db) and QQBackup/qq-win-db-key but i m not able to do so. I'm not sure where it is going wrong.
I'm currently trying to open the encrypted nt_msg.db of QQNT (Windows) in DB Browser with SQLCipher with the passphrase obtained as mentioned in the [QQ NT Windows database decryption + image/file cleanup] (https://github.com/Mythologyli/qq-nt-db) and QQBackup/qq-win-db-key but i m not able to do so. I'm not sure where it is going wrong.
Make sure KDF iterations set to 4000, page size set to 4096, HMAC algorithm set to HMAC_SHA1 and KDF algorithm set to PBKDF2_HMAC_SHA512
I'm currently trying to open the encrypted nt_msg.db of QQNT (Windows) in DB Browser with SQLCipher with the passphrase obtained as mentioned in the [QQ NT Windows database decryption + image/file cleanup] (https://github.com/Mythologyli/qq-nt-db) and QQBackup/qq-win-db-key but i m not able to do so. I'm not sure where it is going wrong.
Make sure KDF iterations set to 4000, page size set to 4096, HMAC algorithm set to HMAC_SHA1 and KDF algorithm set to PBKDF2_HMAC_SHA512
Even though I have kept similar settings as you have mentioned, I m not able to open the database (nt_msg.db).
Please show the error message. Also make sure the original file has 1024 bytes skipped.
For CLI decryption, if the passphrase is correct, this should work:
cp ~/.config/QQ/nt_qq_6bb87db59dd2e7d303966b6fc81dc8dd/nt_db/nt_msg.db .
cat nt_msg.db | tail -c +1025 > db
sqlcipher db "pragma key = 'XXXXXXXXXXXXXXXX'; pragma kdf_iter = 4000" .d | tail +2 | sqlite3 decrypted-db
Please show the error message. Also make sure the original file has 1024 bytes skipped.
For CLI decryption, if the passphrase is correct, this should work:
cp ~/.config/QQ/nt_qq_6bb87db59dd2e7d303966b6fc81dc8dd/nt_db/nt_msg.db . cat nt_msg.db | tail -c +1025 > db sqlcipher db "pragma key = 'XXXXXXXXXXXXXXXX'; pragma kdf_iter = 4000" .d | tail +2 | sqlite3 decrypted-db
The error message : sqlite> .open nt_msg.clean.db sqlite> pragma key = "################"; pragma kdf_iter = "4000"; ok sqlite> attach database "nt_msg.db" as plaintext key ""; Select sqlcipher_export("plaintext"); Detach database plaintext; Parse error: file is not a database (26) sqlite> I m not sure the key extracted from the method is correct.
现在腾讯在推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/ntqq{一串ID}/nt_db 目测数据库格式和其他平台是一样的