QQ数据库解析总结

简介: QQ数据库解析总结

1.概述

Android QQ的聊天记录存储于/data/data/com.tencent.mobileqq/databases目录下,其中QQ号.db文件即为该QQ号的聊天记录数据库,获得该文件即有机会调取出相应的聊天记录。QQ数据库是不加密的,但是表里面的关键内容字段是加密的,需要破解。

破解教程:https://github.com/xxxyanchenxxx/QqDecrpt

https://github.com/Yiyiyimu/QQ-History-Backup

本机登录的QQ号查找方法:/shared_prefs/qb_info.xml,如下图所示:

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址https://gitee.com/plasticene3/plasticene-boot-starter-parent

微信公众号Shepherd进阶笔记

接下来我们进入今天主题:基于Android解析QQ数据库

2.数据库表

目前主要涉及的表有:conversation_info(会话信息表),Friends(好友表),TroopInfoV2(群聊表),TroopMemberInfo(群成员信息表),TroopMemberCardInfo(群成员信息表),DynamicAvatar(头像表)。

好友会话消息记录表:mr_friend_***_New:其中的”*“是好友的QQ号uin的MD5加密32位字符串。

eg:mr_friend_3DAEEB4D801A88F08AF16930B5DB7C60_New

群会话消息记录表:mr_troop_***_New: 其中的”*“是群组的群号uin的MD5加密32位字符串。

eg:mr_troop_62CE79A16BB9C8F8193BF1B043B05D82_New

以上数据库表分析不做截图数据展示,因为数据库全是乱码,需要解密

  • 会话表:conversation_info

    会话表有存入所有的会话uin,目前qq的无用会话非常多,我们只需要关注type=0(好友会话)和type=1(群会话),但是会话表没有会话时间,所以没办法根据时间来判断聊天会话是否改变,基础查询sql如下

    select type, uin from conversation_info where type=0 or type = 1
    

    在我们拿到uin之后,就可以遍历uin,按照上面所示uin的md5加密大写32字符串得到相应会话聊天记录表,这时候需要Android记录每张会话聊天记录表的上次上报聊天记录的最后时间

    还有另外一种方式不需要借助会话表,那就是通过如下sql查询出所有的会话聊天记录表,再去和上面方式遍历

    select * from sqlite_master where name like 'mr_friend_%New' or name like 'mr_troop_%New' ;
    
  • 好友表:Friends

    目前好友表存储中好友的qq号,昵称,备注,昵称全拼,时间字段等等有效信息,sql如下所示:

    select uin, name, remark, mCompareSpell, datetime from Friends
    

    同时未通过的添加好友信息也在该表中,目前来看和通过的好友区别是:datetime=0。还需要注意一个问题,在研究中发现,好友的信息改变了,但是datatime的时间不会立即变化,例如好友的头像换了或者备注改了,立刻拉取数据,会出现备注变成空的情况,估计需要经过一段时间qq后台数据库datetime字段更新了才能上报最新的好友信息

  • 群聊表:TroopInfoV2

    群聊表中包含了当前qq号,群qq号,群昵称,群备注,群主qq,时间等字段,sql如下所示:

    select uin, troopuin, troopname, troopRemark, troopowneruin, timeSec from TroopInfoV2
    

    查询陌生人信息:即同属于一个群但不是好友的关系的sql如下

    select memberuin,troopuin, alias, autoremark, friendnick, datetime from TroopMemberInfo where memberuin not in(select uin from Friends)
    
  • 消息记录表

    qq的每个会话聊天记录都会新生成一张消息表,格式:mr_friend_md5(qq)_New(好友会话聊天记录), mr_troop_md5(qq)__New(群会话聊天记录,查询sql如下:

    select selfuin, frienduin, senderuin, issend, msgUid, msgData, msgType from mr_friend_3DAEEB4D801A88F08AF16930B5DB7C60_New where msgtype=-1000 or msgType=-2000 or msgType=-2002
    

    -1000:文本消息 -2000:图片 -2002:语音

    这里的消息都是存在msgData这个字段里面,需要解密,msgData原生字段类型为blob,解密的方式:文本和图片,语音的解密方式是不一样的。

    1) 文本解密:文本的解密方式是手机key和内容进行异或即可,代码示例如下:

     public static String decryptString(String content) {
         
         
            if (StringUtils.isEmpty(content)) {
         
         
                return null;
            }
            if (!isPassInit) {
         
         
                genCodeKey();
            }
            return decryptString(content, false);
        }
    
        private static String decryptString(String str, boolean z) {
         
         
            char[] cArr = null;
            if (str == null) {
         
         
                return null;
            }
            try {
         
         
                if (z) {
         
         
                    cArr = StringUtil.reflactCharArray(str);
                }
                if (cArr == null) {
         
         
                    cArr = new char[str.length()];
                    z = false;
                }
                for (int i = 0; i < str.length(); i++) {
         
         
                    cArr[i] = (char) (str.charAt(i) ^ codeKey[i % codeKeyLen]);
                }
                if (cArr.length == 0) {
         
         
                    return "";
                }
                if (z) {
         
         
                    return str;
                }
                return StringUtil.newStringWithData(cArr);
            } catch (Throwable th) {
         
         
                return "";
            }
        }
    

​ 2)图片,语音解密相对就比较复杂,qq使用了Google的Protobuf,一种平台无关,语言无关的结构化数据编解码协议,详细了解请自行百度,简单来说需要根据具体的使用场景编写特定的.proto文件,然后再通过客户端Protobuf.exe生成相应配置文件的代码工具类,在解析的工程中加入该工具类代码,进行解析。由于解析代码工具类过长,这里提供代码下载RichMsg

解析案例:

public static void testMsg(Connection connection) throws SQLException, InvalidProtocolBufferException {
   
   
        Statement statement = connection.createStatement();
        statement.setQueryTimeout(30); // set timeout to 30 sec.
        // 执行查询语句
        ResultSet rs = statement.executeQuery("select extStr,selfuin, frienduin, senderuin, issend, msgUid, msgData from mr_friend_3DAEEB4D801A88F08AF16930B5DB7C60_New where msgtype=-2000");
        while (rs.next()) {
   
   
            byte[] msgData = QqDecryptUtil.decryptBytes(rs.getBytes("msgData"));
            RichMsg.PicRec picRec = RichMsg.PicRec.parseFrom(msgData);
            String url = "chatimg:" + picRec.getMd5();
            long value = CRC64FromString(url);
            String filename = "";
            if (value < 0) {
   
   
                long abs = Math.abs(value);
                filename = "-"+Long.toHexString(abs);
            } else {
   
   
                filename = Long.toHexString(value);
            }
            filename = "Cache_" + filename.replace("0x", "");
            System.out.println(picRec);

        }
    }



    private static final long[] table = new long[256];
    static {
   
   
        for(int n = 0; n < 256; ++n) {
   
   
            long crc = (long)n;

            for(int k = 0; k < 8; ++k) {
   
   
                if ((crc & 1L) != 0L) {
   
   
                    crc = crc >> 1 ^ -7661587058870466123L;
                } else {
   
   
                    crc >>= 1;
                }
            }
            table[n] = crc;
        }
    }

    public  static  long CRC64FromString(String val) {
   
   
        byte[] s = val.getBytes();
        long v = -1;
        for (int i = 0; i < val.length(); i++) {
   
   
            int value = s[i];
            v = table[(value ^ ((int) v)) & 255] ^ v >> 8;
        }
        return v;
    }
目录
相关文章
|
4月前
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
95 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
3月前
|
存储 关系型数据库 数据库
高性能云盘:一文解析RDS数据库存储架构升级
性能、成本、弹性,是客户实际使用数据库过程中关注的三个重要方面。RDS业界率先推出的高性能云盘(原通用云盘),是PaaS层和IaaS层的深度融合的技术最佳实践,通过使用不同的存储介质,为客户提供同时满足低成本、低延迟、高持久性的体验。
|
10月前
|
SQL 数据挖掘 测试技术
南大通用GBase8s数据库:LISTAGG函数的解析
南大通用GBase8s数据库:LISTAGG函数的解析
|
10月前
|
数据库 索引
深入探索数据库索引技术:回表与索引下推解析
【10月更文挑战第15天】在数据库查询优化的领域中,回表和索引下推是两个核心概念,它们对于提高查询性能至关重要。本文将详细解释这两个术语,并探讨它们在数据库操作中的作用和影响。
169 3
|
10月前
|
存储 负载均衡 监控
数据库多实例的深入解析
【10月更文挑战第24天】数据库多实例是一种重要的数据库架构方式,它为数据库的高效运行和灵活管理提供了多种优势。在实际应用中,需要根据具体的业务需求和技术环境,合理选择和配置多实例,以充分发挥其优势,提高数据库系统的性能和可靠性。随着技术的不断发展和进步,数据库多实例技术也将不断完善和创新,为数据库管理带来更多的可能性和便利。
336 57
|
9月前
|
存储 关系型数据库 MySQL
double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用
本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。
1362 5
|
10月前
|
SQL 存储 Oracle
南大通用GBase 8s数据库游标变量解析:提升数据库操作效率
南大通用GBase 8s 数据库游标变量解析:提升数据库操作效率
|
10月前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
6月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
534 29
|
6月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
166 4

热门文章

最新文章

推荐镜像

更多
  • DNS