Android 手机QQ聊天记录导出(NTQQ),解密聊天数据库

Android 手机QQ聊天记录导出(NTQQ),解密聊天数据库

先看结果: 方法源自Github大佬项目:地址在这 本教程导出的聊天记录QQ版本的NT版本的,即QQ8.9之后的版本,我的QQ版本是9.1.50.23520,QQ8.9之后的版本为NT架构,导出比较困难,如果是之前的版本,建议查看另一个大佬的项目:地址 以下教程仅是记录了我自己的实际过程,结合了自身的实际情况,过程与大佬的项目不完全一样

获取UID

项目的方法:

1、将data/user/0/com.tencent.mobileqq/databases/beacon_db_com.tencent.mobileqq文件作为纯文本文件打开,查找你的 QQ 号对应的uid,形式如 “home_uin”: “390251789”,“uid”:“u_mIicAReWrdCB-kST6TXH7A”,其中u_mIicAReWrdCB-kST6TXH7A即为uid。 2、在/data/user/0/com.tencent.mobileqq/files/uid/目录下,可见到文件名形如390251789###u_mIicAReWrdCB-kST6TXH7A的若干个文件,其中u_mIicAReWrdCB-kST6TXH7A即为uid。 3、若使用了QAuxiliary模块,可以通过打开[辅助功能]聊天和消息-[消息]转发消息点头像查看详细信息功能,合并转发由自己发送的消息,查看消息的senderUid属性获取,详见#32

我的方法

其实我就是用了上面的方法2,只是我在手机文件管理器查看的时候,并未发现uid目录,然后我想到可能要root才能看见这个目录,于是在电脑上用模拟器(自己电脑上本来就有模拟器-雷电模拟器)开启root模式,然后就看到了uid目录: 打开到目录:data/data/com.tencent.mobileqq/files/uid 可以看到:

形如 u_mIicAReWrdCB-kST6TXH7A 的就是uid 对uid进行MD5加密即可得到QQ_UID_hash,在线MD5加密,取小写的值 如:u_mIicAReWrdCB-kST6TXH7A进行 MD5加密得到 255c42fc0f4d295678e6ff0135fcf5dd

获取聊天记录文件

模拟器这里也是可以获取的,位置在一下路径:

/data/user/0/com.tencent.mobileqq/databases/nt_db/nt_qq_{QQ_path_hash}/nt_msg.db

但是这个路径的记录肯定是不全的,当然如过在模拟器QQ这里一直拉取聊天记录,还是会有相关记录的。 我的手机是Redmi K70 Ultra ,在手机备份与恢复中,把QQ进行了备份,得到了QQ的备份文件,我的是在MIUI文件夹下,backup→AllBackup下面的文件夹就是,里面有个QQ(com.tencent.mobileqq).bak的文件,这个就是了,把ta搞到电脑上,然后解压,得到apps文件夹,进入到apps\com.tencent.mobileqq\db\nt_db目录下,如 对QQ_UID_hash进行如下运算即可得到QQ_path_hash:QQ_path_hash = md5(md5(uid) + “nt_kernel”) = md5(“255c42fc0f4d295678e6ff0135fcf5ddnt_kernel”) = “b69bfb8e74137f4e4253d1af3e99493a”

则聊天记录路径

/data/user/0/com.tencent.mobileqq/databases/nt_db/nt_qq_b69bfb8e74137f4e4253d1af3e99493a/nt_msg.db

nt_msg.db就是我们要解密的数据库文件,建议备份一份,防止意外。

获取密钥

使用HxD或者其他二进制查看工具打开nt_msg.db文件,将文件头部跟随在QQ_NT DB后的可读字符串复制,形如6tPaJ9GP,记为rand。

此时可以计算出数据库密钥key:key = md5(QQ_UID_hash + rand) = md5(“255c42fc0f4d295678e6ff0135fcf5dd6tPaJ9GP”) =+“71c0dfcef3b5ceae7c4a1c68ca662f4a”。

则数据库密钥为71c0dfcef3b5ceae7c4a1c68ca662f4a。

另外,文件头部还可能含有cipher_hmac_algorithm的值(如HMAC_SHA1)等与解密相关的信息,可被解析为Protobuf数据,详见#29 (comment)。

HxD下载 往下滑动就能看到了 下载后,打开HxD,点击右上角 文件→打开,选择nt_msg.db文件,即可看到如下内容: 其中,8MuONS9V就是rand,HMAC_SHA1就是下面等会就需要用到的cipher_hmac_algorithm的值。 !!!再次提醒,记得备份nt_msg.db文件!!!

移除无关文件头

首先,将nt_msg.db文件删除前1024字节,这可以通过以下方式完成:

使用二进制编辑器:Android 下的 MT 管理器(需要付费)、Windows 下的 HxD 等软件均可使用,细节从略。

使用tail命令(仅 Linux):tail -c +1025 nt_msg.db > nt_msg.clean.db

使用 Python:python -c “open(‘nt_msg.clean.db’,‘wb’).write(open(‘nt_msg.db’,‘rb’).read()[1024:])”

完成后,得到nt_msg.clean.db文件。 使用Hxd删除也行的,删除到有数据这里就行,然后另存为nt_msg.clean.db

打开数据库

下载DB Browser for SQLite 和SQLiteStudio, 其实下载其中一个就够了,我下载两个是因为SQLiteStudio能看到聊天记录的内容,DB Browser for SQLite只能看到字节 下面使用SQLiteStudio打开数据库

PRAGMA key = 'pass'; -- pass 替换为之前得到的密码key(32字节字符串)

PRAGMA cipher_page_size = 4096;

PRAGMA kdf_iter = 4000; -- 非默认值 256000

PRAGMA cipher_hmac_algorithm = HMAC_SHA1; -- 非默认值(见上文)

PRAGMA cipher_default_kdf_algorithm = PBKDF2_HMAC_SHA512;

PRAGMA cipher = 'aes-256-cbc';

将上面的内容填入下面的加密算法配置里,pass记得改成上面获取到的看key,打开刚才另存的nt_msg.clean.db文件,我图片里的是nt_msg.db是因为我重命名了 然后就打开数据库成功了,其中c2c_msg_table就是聊天记录内容表,点击数据即可查看 其中,40020字段就是发送人的uid,40021字段就是跟我们聊天的人,40080字段就是聊天的内容了

聊天内容导出

目前已知消息格式为protobuf,相关解密代码可以参考提取QQ NT数据库 group_msg_table 中的纯文本、这份 Python 代码与这份 protobuf 定义,完整实现暂无,欢迎贡献。 附上大佬的python代码:

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定义

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; }

相关推荐

补足产品线,Radeon HD 7800系列显卡评测汇总
365bet国际

补足产品线,Radeon HD 7800系列显卡评测汇总

📅 08-19 👁️ 5122
若依切换菜单路由跳转成功,没有调接口页面空白,刷新后正常
[交流]一样的装备多个五孔,价格贵了100
365bet国际

[交流]一样的装备多个五孔,价格贵了100

📅 07-09 👁️ 654