Python查看Android QQ本地消息记录数据库
需求
- 随着电子产品的更新换代,每隔一段时间我们就会更换手中的电子设备,早期版本的QQ不支持备份聊天记录,Android 版本的不同也给QQ数据迁移带来了一定的麻烦。能不能通过提取QQ的数据文件来获取到以往设备中的消息记录?
调研
-
Android QQ的聊天记录存储于
/data/data/com.tencent.mobileqq/databases
目录下,其中QQ号.db
文件即为该QQ号的聊天记录数据库,获得该文件即有机会调取出相应的聊天记录。 -
本文仅适用于 Android 设备。
-
本文仅用于技术研究和学习之用,切勿用于非法用途。
前期准备
-
数据库获取
首先需要获得上面提到的 QQ号.db 文件。由于该文件位于
/data
分区下,Android 默认不可读写。如果手机已root可直接复制到电脑上。如果手机没有root并且不想root,现在的系统大多提供了数据备份与恢复这一功能,我们可以使用这一工具备份整个QQ的数据,然后将压缩包导出至电脑上,提取出其中的QQ号.db
文件。直接打开这个 .db 文件是不是就可以看到消息记录了呢?如果有那么简单自然就不会有这篇文章了。
我们使用 SQLite Expert 打开这个数据库,可以看到,这个 database 包含很多张 table ,其中有许多名为
mr_friend_***
、mr_troop_***
的数据表。查看这些 table ,我们可以发现里面存有诸如 msgId、msgUid、msgData、msgtype、senderuin、selfuin、time 等字段。通过这些字段以及其中的内容,我们基本可以确定,这就是 QQ 存放消息记录的数据表。表名mr_friend_***
应当代表与某好友的聊天记录,mr_troop_***
应当代表某QQ群的聊天记录。 -
数据表分析
以
mr_friend_***_New
为例,此类数据表一共26个字段,其中extStr
、frienduin
、selfuin
、senderuin
均为 TEXT 文本类型,msgData
为 BLOB 二进制类型,且以上5个字段内容均为乱码,猜测应当是被加密了。其余21个字段均为 INTEGER 类型,无乱码未加密。现在要做的就是解密这5个加密字段的内容,重点是msgData
字段。 -
数据表解密
互联网是很发达的,在本文之前很多年早已有人破解出了密钥,在此就不用多费时间去亲自推敲了:表名
mr_friend_***_New
中的***为QQ号/群号的32位大写MD5值,表中用于加密5字段的密钥为本设备的IMEI号,将加密数据与该IMEI号进行逐位异或即可解密。如果手机背面的进网许可标签没有撕掉,直接把手机翻过来就能看到IMEI号(有些设备贴在电池内侧);也可以直接在手机里查询,打开拨号盘,输入
*#06#
,会显示14位长度的MEID和15位长度的IMEI,如果手机有两个卡槽则还有第2个15位的IMEI。
代码实现
-
仅查询
import sqlite3 import time import hashlib IMEID = '****************' # 可以是15位IMEI也可以是14位MEID conn = sqlite3.connect('*********.db') # QQ号 c = conn.cursor() cu = conn.cursor() # 获取表名,保存在tab_name列表 cu.execute("select name from sqlite_master where type='table'") tab_name = cu.fetchall() tab_name = [line[0] for line in tab_name] num = input("请输入QQ号:") hl = hashlib.md5() hl.update(num.encode(encoding='utf-8')) for line in tab_name: if "mr_friend_"+hl.hexdigest().upper() in line: # 查找对应QQ号的聊天记录,如果是群聊记录则需将mr_friend改成mr_troop cursor = c.execute('SELECT senderuin,time,msgData FROM '+line+' WHERE msgtype=-1000;') # msgtype=-1000代表文本类型 def decrypt_msg(encrypted_msg): # 解密单条数据 if type(encrypted_msg) is bytes: "" # bytes类型无需转码 elif type(encrypted_msg) is int: if 1000000000<encrypted_msg<10000000000: timeArray = time.localtime(encrypted_msg) encrypted_msg = time.strftime("%Y-%m-%d %H:%M:%S", timeArray) # 时间戳需要转换为标准时间 return encrypted_msg else: encrypted_msg = bytes(encrypted_msg, encoding = "utf8") msg = [] for i in range(len(encrypted_msg)): msg.append(encrypted_msg[i] ^ IMEID[i%15].encode()[0]) # 如果前面用的14位MEID此处也对应换成i%14 return bytes(msg).decode() for row in cursor: decrypted_msgs = [] for item in row: decrypted_msgs.append(decrypt_msg(item)) print(decrypted_msgs)
注意事项
-
若仅查询消息记录筛选
senderuin
、time
、msgData
三个字段即可。 -
若单独通过
mr_troop_***
查询群聊记录,无法查出群成员昵称,需结合表 TroopMemberCardInfo/TroopMemberInfo 进行显示。
-
若仅查询QQ聊天记录中的图片、视频、语音等文件,直接检索内部存储
/sdcard/tencent/MobileQQ
文件夹即可。 -
若发现一些较早的聊天记录缺失,则需结合
slowtable_QQ号.db
进行查询,QQ默认将超出一定数量限制的聊天记录转存至该文件中。 -
在撰写完本文后偶然发现一位大佬编写的源码与GUI,还比较全面,一并分享于此,供大家研究参考:怎样导出手机中的QQ聊天记录?
-
本文所述方法理论上可用于查看曾在本机登录过的他人消息记录,若如此,请务必征得其本人同意。
-
本文仅用于技术研究和学习之用,切勿用于非法用途。