zoukankan      html  css  js  c++  java
  • Python查看Android QQ本地消息记录数据库

    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个字段,其中extStrfrienduinselfuinsenderuin均为 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)
      

    注意事项

    • 若仅查询消息记录筛选senderuintimemsgData三个字段即可。

    • 若单独通过mr_troop_***查询群聊记录,无法查出群成员昵称,需结合表 TroopMemberCardInfo/TroopMemberInfo 进行显示。

    • 若仅查询QQ聊天记录中的图片、视频、语音等文件,直接检索内部存储 /sdcard/tencent/MobileQQ 文件夹即可。

    • 若发现一些较早的聊天记录缺失,则需结合 slowtable_QQ号.db 进行查询,QQ默认将超出一定数量限制的聊天记录转存至该文件中。

    • 在撰写完本文后偶然发现一位大佬编写的源码与GUI,还比较全面,一并分享于此,供大家研究参考:怎样导出手机中的QQ聊天记录?

    • 本文所述方法理论上可用于查看曾在本机登录过的他人消息记录,若如此,请务必征得其本人同意。

    • 本文仅用于技术研究和学习之用,切勿用于非法用途。

    参考资料

  • 相关阅读:
    ASP.NET 后台页面无法识别服务器控件ID
    HTML5 <li> <ol> <ul> 用法
    【转】船体分段测量 船舶精度管理
    【转】2000国家大地控制网
    【转】水准原点
    【转】城市CORS系统建设
    【转】我国常用的高程系统
    【转】地图的分幅与编号
    【转】大地测量系统和参考框架
    【转】国家天文大地网
  • 原文地址:https://www.cnblogs.com/lolipop2019/p/14411478.html
Copyright © 2011-2022 走看看