Android如何获取QQ与微信的聊天记录并保存到数据库详解


本文整理自网络,侵删。

前言

提前说明下:(该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓促,有错别字还请见谅。)

为了获取黑产群的动态,有同事潜伏在大量的黑产群(QQ 微信)中,干起了无间道的工作。随着黑产群数量的激增,同事希望能自动获取黑产群的聊天信息,并交付风控引擎进行风险评估。于是,我接到了这么一个工作……

分析了一通需求说明,总结一下:

  • 能够自动获取微信和 QQ群的聊天记录
  • 只要文字记录,图片和表情包,语音之类的不要
  • 后台自动运行,非实时获取记录

准备工作

参阅很多相关的文章之后,对这个需求有了大致的想法,开始着手准备:

  • 一个有root权限的手机,我用的是红米5(强调必须要有ROOT)
  • android的开发环境
  • android相关的开发经验(我是个PHP,第一次写ANDROID程序,踩了不少坑)

获取微信聊天记录

说明:

微信的聊天记录保存在"/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb*/EnMicroMsg.db"

该文件是加密的数据库文件,需要用到sqlcipher来打开。密码为:MD5(手机的IMEI+微信UIN)的前七位。文件所在的那个乱码文件夹的名称也是一段加密MD5值:MD5('mm'+微信UIN)。微信的UIN存放在微信文件夹/data/data/com.tencent.mmshared_prefs/system_config_prefs.xml中。(这个减号一定要带着!)


另外,如果手机是双卡双待,那么会有两个IMEI号,默认选择 IMEI1,如果不行,可以尝试一下字符串‘1234567890ABCDEF'。早期的微信会去判定你的IMEI,如果为空 默认选择这个字符串。


拿到密码,就可以打开EnMicroMsg.db了。微信聊天记录,包括个人,群组的所有记录全部存在message这张表里。

代码实现

第一步,不可能直接去访问EnMicroMsg.db。没有权限,还要避免和微信本身产生冲突,所以选择把这个文件拷贝到自己的项目下:

oldPath ="/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb**\***/EnMicroMsg.db";
newPath ="/data/data/com.你的项目/EnMicroMsg.db";
copyFile(oldPath,newPath);//代码见 部分源码

第二步,拿到文件的密码:

String password = (MD5Until.md5("IMEI+微信UIN").substring(0, 7).toLowerCase());

第三步,打开文件,执行SQL:

SQLiteDatabase.loadLibs(context);
SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
 public void preKey(SQLiteDatabase database) {
 }

 public void postKey(SQLiteDatabase database) {
  database.rawExecSQL("PRAGMA cipher_migrate;");//很重要
 }
};
SQLiteDatabase db = openDatabase(newPath, password, null, NO_LOCALIZED_COLLATORS, hook);
 long now = System.currentTimeMillis();
 Log.e("readWxDatabases", "读取微信数据库:" + now);
 int count = 0;
 if (msgId != "0") {
  String sql = "select * from message";
  Log.e("sql", sql);
  Cursor c = db.rawQuery(sql, null);
  while (c.moveToNext()) {
   long _id = c.getLong(c.getColumnIndex("msgId"));
   String content = c.getString(c.getColumnIndex("content"));
   int type = c.getInt(c.getColumnIndex("type"));
   String talker = c.getString(c.getColumnIndex("talker"));
   long time = c.getLong(c.getColumnIndex("createTime"));
   JSONObject tmpJson = handleJson(_id, content, type, talker, time);
   returnJson.put("data" + count, tmpJson);
   count++;
  }
  c.close();
  db.close();
  Log.e("readWxDatanases", "读取结束:" + System.currentTimeMillis() + ",count:" + count);
 }

到此,就可以拿到微信的聊天记录了,之后可以直接将整理好的JSON通过POST请求发到服务器就可以了。(忍不住吐槽:写服务器落地程序用了30分钟,写上面这一坨花了三四天,还不包括搭建开发环境,下载SDK,折腾ADB什么的)

获取QQ聊天记录

说明

QQ的聊天记录有点麻烦。他的文件保存在/data/data/com.tencent.mobileqq/databases/你的QQ号码.db

这个文件是不加密的,可以直接打开。QQ中群组的聊天记录是单独建表存放的,所有的QQ群信息存放在TroopInfoV2表里,需要对字段troopuin求MD5,然后找到他的聊天记录表:mr_troop_" + troopuinMD5 +"_New。

但是!!!

问题来了,它的内容是加密的,而且加密方法还很复杂:根据手机IMEI循环逐位异或。具体的我不举例子了,太麻烦,直接看文章最后的解密方法。

代码实现

第一步,还是拷贝数据库文件。

final String QQ_old_path = "/data/data/com.tencent.mobileqq/databases/QQ号.db";
final String QQ_new_path = "/data/data/com.android.saurfang/QQ号.db";
DataHelp.copyFile(QQ_old_path,QQ_new_path);

第二步,打开并读取内容

SQLiteDatabase.loadLibs(context);
String password = "";
SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
 public void preKey(SQLiteDatabase database) {}
 public void postKey(SQLiteDatabase database) {
  database.rawExecSQL("PRAGMA cipher_migrate;");
 }
};
 MessageDecode mDecode = new MessageDecode(imid);
HashMap<String, String> troopInfo = new HashMap<String, String>();
try{
 SQLiteDatabase db = openDatabase(newPath,password,null, NO_LOCALIZED_COLLATORS,hook);
 long now = System.currentTimeMillis();
 Log.e("readQQDatabases","读取QQ数据库:"+now);
 //读取所有的群信息
 String sql = "select troopuin,troopname from TroopInfoV2 where _id";
 Log.e("sql",sql);
 Cursor c = db.rawQuery(sql,null);
 while (c.moveToNext()){
  String troopuin = c.getString(c.getColumnIndex("troopuin"));
  String troopname = c.getString(c.getColumnIndex("troopname"));
  String name = mDecode.nameDecode(troopname);
  String uin = mDecode.uinDecode(troopuin);
  Log.e("readQQDatanases","读取结束:"+name);
  troopInfo.put(uin, name);
 }
 c.close();

 int troopCount = troopInfo.size();
 Iterator<String> it = troopInfo.keySet().iterator();
 JSONObject json = new JSONObject();
 //遍历所有的表
 while(troopCount > 0) {
  try{
   while(it.hasNext()) {
    String troopuin = (String)it.next();
    String troopname = troopInfo.get(troopuin);
    if(troopuin.length() < 8)
     continue;
    String troopuinMD5 = getMD5(troopuin);
    String troopMsgSql = "select _id,msgData, senderuin, time from mr_troop_" + troopuinMD5 +"_New";
    Log.e("sql",troopMsgSql);
    Cursor cc = db.rawQuery(troopMsgSql,null);
    JSONObject tmp = new JSONObject();
    while(cc.moveToNext()) {
     long _id = cc.getLong(cc.getColumnIndex("_id"));
     byte[] msgByte = cc.getBlob(cc.getColumnIndex("msgData"));
     String ss = mDecode.msgDecode(msgByte);
     //图片不保留
     if(ss.indexOf("jpg") != -1 || ss.indexOf("gif") != -1
       || ss.indexOf("png") != -1 )
      continue;
     String time = cc.getString(cc.getColumnIndex("time"));
     String senderuin = cc.getString(cc.getColumnIndex("senderuin"));
     senderuin = mDecode.uinDecode(senderuin);
     JSONObject tmpJson = handleQQJson(_id,ss,senderuin,time);
     tmp.put(String.valueOf(_id),tmpJson);
    }
    troopCount--;
    cc.close();
   }
  } catch (Exception e) {
   Log.e("e","readWxDatabases"+e.toString());
  }
 }
 db.close();
}catch (Exception e){
 Log.e("e","readWxDatabases"+e.toString());
}

然后你就可以把信息发到服务器落地了。

后续

这里还有几个需要注意的地方:

阅读剩余部分

相关阅读 >>

数据库连接池以及sequelize实现增删改查等操作指南

关于若干数据库数据插入性能的对比分析

listview与Sqlite结合实现记事本功能

intellj idea 2020版添加Sqlite数据库的方法

android ormlite更改数据库默认位置

Sqlite3中数据导入到mysql中的实战教程

android数据库sd卡创建和图片存取操作

android 中自定义contentprovider与contentobserver的使用简单实例

Sqlite教程(三):数据表和视图简介

ubuntu下使用Sqlite3的基本命令

更多相关阅读请进入《Sqlite》频道 >>


数据库系统概念 第6版
书籍

数据库系统概念 第6版

机械工业出版社

本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...