OnO's Blog - 2020年7月
type: channel
link: https://i.ctm49.com/2020/07/
description: Captain Teemo On Duty !
items: QQ聊天记录导出工具

QQ聊天记录导出工具
type: item
date: 2020-07-26T08:57:00+00:00
description: 简介这个项目初步完成于2020.07.26。一开始是想找个方法导出自己的QQ聊天记录，看到了roadwide/qqmessageoutput和Yiyiyimu/QQ_History_Backup两个项目，于是拿来用，却发现：1、导出内容较多时，网页根本不适用；2、QQ昵称中含有表情时，解密不出来。于是fork了roadwide/qqmessageoutput，自己改改，得到了一个带GUI版本的聊天记录。下载和使用不再赘述，请参见 https://github.com/ctem049/qqmessageoutputQQ聊天db文件分析QQ聊天数据储存位置为：data\data\com.tencent.mobileqq\databases\QQ号.db data\data\com.tencent.mobileqq\databases\slowtable_QQ号.dbroot过的设备可以复制出来，使用SQLite工具（例如Navicat）打开，可以看到：数据表含义其中比较重要的几个表：Friends表记录了好友信息，uin为QQ号，name为QQ名字，remark为备注，还有age、gender等等。TroopInfoV2表记录了加入的群聊，troopuin为群号，troopname为群名，trooponweruin为群主QQ号等。TroopMemberInfo表记录了群聊里的成员信息，troopuin为群号，memberuin为qq号，troopnick为群名片，friendnick为qq名，join_time为入群时间。mr_friend_XXX_New和mr_troop_XXX_New表即为QQ好友或群聊的聊天记录表，XXX处实际为QQ号或群号的md5。众所周知md5是单向的hash函数，所以先得知道好友/群聊的号码，再查询表访问数据。但实际上如果没有事先得到QQ号/群号表，也可以通过www.cmd5.com直接逆向破解，因为QQ号纯数字又比较短，基本上都是免费破解的。聊天记录表重要的列：senderuin为发送者QQ，time为发送的时间（10位时间戳，精确到秒）, msgData为内容, selfuin为自己的QQ, frienduin为好友的QQ或群号。通过判断selfuin==senderuin即可知道是不是自己发的消息。数据解密分析直接使用SQLite工具打开数据库可以发现，无论是QQ号（uin字段）还是聊天内容（msgData字段）都是乱码，根据前人的经验，它是采用简单的异或加密，密钥为imei或kc文件，位置：data\data\com.tencent.mobileqq\files\imei data\data\com.tencent.mobileqq\files\kc打开imei文件可以看到imei=XXXXXXXX，XXXXXXXX即为密钥，密钥是循环补位的。对于不同的数据，解密方法也有所不同：按unicode字符解密对于uin、昵称等信息，读取出来的数据为str（字符串）类型，例如0x01 06 03 0D 02 09 09 06 03 03读取出来为\x01\x06\x03\r\x02\t\t\x06\x03\x03，需要读取它的Unicode码0x01 06 03 0D 02 09 09 06 03 03密码为3524300727，Unicode码为0x33 35 32 34 33 30 30 37 32 37计算0x01 06 03 0D 02 09 09 06 03 03 XOR 0x33 35 32 34 33 30 30 37 32 37得0x32 33 31 39 31 39 39 31 31 34，解码得2319199114。对于中文字符，每一个中文字符只消耗一个字符的密钥，例如幁（Unicode码0x5E 41）与0x35异或得0x5E 74（年），实际python程序一个字符一个字符来就行。这种解密方式看似是对的，但是遇到emoji字符时，就出现了问题，如果QQ昵称中有emoji，解出来是乱码。比如我们得到昵称为0x01 3F 74如果按照上述解密方法，XOR 0x33 35 32得到0x32 0A 46，解不出🍁(0x01 F3 41)。经过逆运算0x01 3F 74 XOR 0x01 F3 41得0xCC 35，发现是由0x33 35将33左移两位得到的：0x33 35= 0011 0011 0011 01010xCC 35= 1100 1100 0011 0101因此对于>0xFF FF的字符，需要用密钥的两个字节解密，其他的字符用一个密钥解密即可。按字节解密对于消息内容，读取出来为bytes（字节码）类型，解密很简单，按字节顺序与密钥异或即可。例如0xD7 8A 9D D7 9C…