前言 此博客写于 2020.3.19 ,QQ版本 8.3.0
相关 Github 项目文件见 https://github.com/117503445/qq_get_message
如何获得数据库文件 首先需要已经取得 ROOT 权限的 Android 手机,使用RE管理器或其他方式提取
/data/data/com.tencent.mobileqq/databases/QQ号.db
/data/data/com.tencent.mobileqq/databases/slowtable_QQ号.db
/data/data/com.tencent.mobileqq/databases/QQ号-IndexQQMsg.db
/data/data/com.tencent.mobileqq/files/imei
其中3个db文件都是Sqlite3数据库,在 Windows 上可以使用 Database.NET 等软件打开。
接下来介绍如何获取聊天记录。
QQ号-IndexQQMsg.db 参考了 https://gist.github.com/recolic/02ba2e2dbae20f216c73b84baa91a39e 并修改脚本,简化操作。
这个数据库储存了最近21天的数据。
这个数据库没有进行加密,只进行了可逆的 Base64 编码,所以可以进行便捷的还原。
我们需要的 数据Table 是 IndexContent_content
将 Python3 脚本放在和 QQ号-IndexQQMsg.db 相同的路径
修改 index_db_path 变量并运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import sys import base64 import datetime import sqlite3 #输入一行,返回bool,决定是否保留这一行 def _filter(line): # return '111222333' in line # return '257112220' in line return True def decode_qtimestamp(s): # print('debug', s, file=sys.stderr) if s == '': return 0 ts = base64.b64decode(s)[4:8] return sum([int(ts[i])*(256**(3-i)) for i in range(4)]) def timestamp_to_str(int_ts): return datetime.datetime.fromtimestamp(int_ts).strftime('%Y-%m-%d %H:%M:%S') #IndexQQMsg.db的路径,请修改 index_db_path = '{QQ_Number}-IndexQQMsg.db' if __name__ == "__main__": conn = sqlite3.connect(index_db_path) c = conn.cursor() lines = c.execute('select * from IndexContent_content;') with open(f'{index_db_path}.txt', 'w', encoding='utf8') as f: for line in lines: try: timestamp = timestamp_to_str(decode_qtimestamp(line[-1])) lst = [timestamp] line = line[:-1] for i in [5, 6, 2]: lst.append(base64.b64decode( line[i]).decode(errors='ignore')) lst[1] = lst[1].replace('ZzZ0', '').replace('ZzZ1', '') line = '|'.join(lst) if _filter(line): f.write(line+'\n') except: pass
QQ号.db 参考了 https://github.com/roadwide/qqmessageorutput 并修改脚本,简化操作
这个数据库储存了最近21天的数据。
但是进行了异或加密。对于老版本的QQ,加密的Key就是手机的IMEI号,可以在 /data/data/com.tencent.mobileqq/files/imei 中查看。但是新版本中似乎更改了加密的key,可以参考 https://github.com/Yiyiyimu/QQ_History_Backup 中的解决方案。
每个好友占一个 Table,名字是 mr_friend_QQ号的MD5的16进制的字母大写
每个群占一个 Table,名字是 mr_troop_QQ群号的MD5的16进制的字母大写
将 Python3 脚本放在和 QQ号.db 相同的路径
修改 dbfile 和 key 变量并运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 from _overlapped import NULL import hashlib import sqlite3 import time import os class QQoutput(): def __init__(self, db, key): self.key = key # 解密用的密钥 self.c = sqlite3.connect(db).cursor() # msgdata mode=0 # other mode=1 def fix(self, data, mode): if(mode == 0): rowbyte = [] for i in range(0, len(data)): rowbyte.append(data[i] ^ ord(self.key[i % len(self.key)])) rowbyte = bytes(rowbyte) try: msg = rowbyte.decode(encoding='utf-8') except: msg = NULL return msg elif(mode == 1): str = '' try: for i in range(0, len(data)): str += chr(ord(data[i]) ^ ord(self.key[i % len(self.key)])) except: str = NULL return str def message(self, table_name): execute = f'select msgData,senderuin,time from {table_name}' cursor = self.c.execute(execute) allmsg = [] for row in cursor: msgdata = row[0] uin = row[1] ltime = time.localtime(row[2]) sendtime = time.strftime("%Y-%m-%d %H:%M:%S", ltime) msg = self.fix(msgdata, 0) senderuin = self.fix(uin, 1) amsg = [] amsg.append(sendtime) amsg.append(senderuin) amsg.append(msg) allmsg.append(amsg) return allmsg def output(self, dbfile): execute = f"SELECT name FROM sqlite_master WHERE type ='table' AND (name LIKE 'mr_friend_%' OR name LIKE 'mr_troop_%') ;" rows = self.c.execute(execute) lst_table_name = [] for row in rows: # row[0] like mr_friend_{QQ号的MD5的16进制的字母大写}_New lst_table_name.append(row[0]) dir_name = f'{dbfile}.dir' if not os.path.exists(dir_name): os.mkdir(dir_name) for table_name in lst_table_name: file = dir_name+'\\' + table_name+".html" f2 = open(file, 'w', encoding="utf-8") f2.write( "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head>") allmsg = self.message(table_name) for msg in allmsg: if msg[2] != 0: try: f2.write("<font color=\"blue\">") f2.write(msg[0]) f2.write("</font>-----<font color=\"green\">") f2.write(msg[1]) f2.write("</font></br>") f2.write(msg[2]) f2.write("</br></br>") except: pass if __name__ == "__main__": # config # 储存QQ聊天信息的db文件,qq号.db 或者是 slowtable_qq号.db,记得把文件和py代码放在同目录 dbfile = '' # 解密的key,一般为IMEI,在files/IMEI文件 key = '' q = QQoutput(dbfile, key) q.output(dbfile)
slowtable_QQ号.db slowtable_QQ号.db 储存了21天前的
slowtable_QQ号.db 和 QQ号.db 的数据结构是基本一致的,所以可以直接使用上面的脚本
总结 感谢下列参考链接做出的工作
https://github.com/roadwide/qqmessageoutput
https://gist.github.com/recolic/02ba2e2dbae20f216c73b84baa91a39e
https://github.com/Yiyiyimu/QQ_History_Backup
另外,目前因为key的改变,没有实现后面2个数据库的记录提取,但是 https://github.com/Yiyiyimu/QQ_History_Backup 中可以使用近期聊天记录进行提取,而近期聊天记录可以通过第一个数据库提取,这样就可以获得21天前的记录。不过目前我还没有实践 https://github.com/Yiyiyimu/QQ_History_Backup 中的方法,所以只是作为思路参考。
如果帮助到了你,请给个 star :D https://github.com/117503445/qq_get_message
最后更新时间:2023-04-25 15:38:21