时间:2021-05-20
前言
提前说明下:(该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓促,有错别字还请见谅。)
为了获取黑产群的动态,有同事潜伏在大量的黑产群(QQ 微信)中,干起了无间道的工作。随着黑产群数量的激增,同事希望能自动获取黑产群的聊天信息,并交付风控引擎进行风险评估。于是,我接到了这么一个工作……
分析了一通需求说明,总结一下:
准备工作
参阅很多相关的文章之后,对这个需求有了大致的想法,开始着手准备:
获取微信聊天记录
说明:
微信的聊天记录保存在"/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());}然后你就可以把信息发到服务器落地了。
后续
这里还有几个需要注意的地方:
最新安卓系统很难写个死循环直接跑了,所以我们需要使用Intent,来开始Service,再通过Service调用AlarmManager。
然后再创建一个LongRunningService,在其中调用AlarmManager。
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); int Minutes = 60*1000; //此处规定执行的间隔时间 long triggerAtTime = SystemClock.elapsedRealtime() + Minutes; Intent intent1 = new Intent(this, AlarmReceiver.class);//注入要执行的类 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent1, 0); manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent); return super.onStartCommand(intent, flags, startId);在AlarmReceiver中调用我们的方法。
Log.e("saurfang","测试定时任务----BEGIN"); //微信部分 postWXMsg.readWXDatabase(); //QQ部分 postQQMsg.readQQDatabase(); Log.e("saurfang","测试定时任务----END"); //再次开启LongRunningService这个服务,即可实现定时循环。 Intent intentNext = new Intent(context, LongRunningService.class); context.startService(intentNext);如果复制文件时失败,校验文件路径不存在,多半是因为授权问题。需要对数据库文件授权 全用户rwx权限
部分源码
(因为种种原因,我不太好直接把源码贴上来。)
复制文件的方法
/** * 复制单个文件 * * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public static boolean copyFile(String oldPath, String newPath) { deleteFolderFile(newPath, true); Log.e("copyFile", "time_1:" + System.currentTimeMillis()); InputStream inStream = null; FileOutputStream fs = null; try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); Boolean flag = oldfile.exists(); Log.e("copyFile", "flag:" +flag ); if (oldfile.exists()) { //文件存在时 inStream = new FileInputStream(oldPath); //读入原文件 fs = new FileOutputStream(newPath); byte[] buffer = new byte[2048]; while ((byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 fs.write(buffer, 0, byteread); } Log.e("copyFile", "time_2:" + System.currentTimeMillis()); } } catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); } finally { try { if (inStream != null) { inStream.close(); } if (fs != null) { fs.close(); } } catch (IOException e) { e.printStackTrace(); } } return true; } /** * 删除单个文件 * * @param filepath * @param deleteThisPath */ public static void deleteFolderFile(String filepath, boolean deleteThisPath) { if (!TextUtils.isEmpty(filepath)) { try { File file = new File(filepath); if (file.isDirectory()) { //处理目录 File files[] = file.listFiles(); for (int i = 0; i < file.length(); i++) { deleteFolderFile(files[i].getAbsolutePath(), true); } } if (deleteThisPath) { if (!file.isDirectory()) { //删除文件 file.delete(); } else { //删除目录 if (file.listFiles().length == 0) { file.delete(); } } } } catch (Exception e) { e.printStackTrace(); } } }MD5方法
public class MD5Until { public static char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; //将字符串转化为位 public static String toHexString(byte[] b){ StringBuilder stringBuilder = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { stringBuilder.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); stringBuilder.append(HEX_DIGITS[b[i] & 0x0f]); } return stringBuilder.toString(); } public static String md5(String string){ try { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(string.getBytes()); byte messageDigest[] = digest.digest(); return toHexString(messageDigest); }catch (NoSuchAlgorithmException e){ e.printStackTrace(); } return ""; }}QQ信息解密方法
public class MessageDecode { public String imeiID; public int imeiLen; public MessageDecode(String imeiID) { this.imeiID = imeiID; this.imeiLen = imeiID.length(); } public boolean isChinese(byte ch) { int res = ch & 0x80; if(res != 0) return true; return false; } public String timeDecode(String time) { String datetime = "1970-01-01 08:00:00"; SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { long second = Long.parseLong(time); Date dt = new Date(second * 1000); datetime = sdFormat.format(dt); } catch (NumberFormatException e) { e.printStackTrace(); } return datetime; } public String nameDecode(String name) { byte nbyte[] = name.getBytes(); byte ibyte[] = imeiID.getBytes(); byte xorName[] = new byte[nbyte.length]; int index = 0; for(int i = 0; i < nbyte.length; i++) { if(isChinese(nbyte[i])){ xorName[i] = nbyte[i]; i++; xorName[i] = nbyte[i]; i++; xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]); index++; } else { xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]); index++; } } return new String(xorName); } public String uinDecode(String uin) { byte ubyte[] = uin.getBytes(); byte ibyte[] = imeiID.getBytes(); byte xorMsg[] = new byte[ubyte.length]; int index = 0; for(int i = 0; i < ubyte.length; i++) { xorMsg[i] = (byte)(ubyte[i] ^ ibyte[index % imeiLen]); index++; } return new String(xorMsg); } public String msgDecode(byte[] msg) { byte ibyte[] = imeiID.getBytes(); byte xorMsg[] = new byte[msg.length]; int index = 0; for(int i = 0; i < msg.length; i++) { xorMsg[i] = (byte)(msg[i] ^ ibyte[index % imeiLen]); index++; } return new String(xorMsg); }}总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
主卡不可以查副卡的微信记录,由于聊天记录涉及用户隐私,为了保护用户隐私,聊天记录会保存到用户本机,服务器不会保存用户任何聊天记录。 微信(WeChat)是腾讯
微信聊天记录迁移如何合并?如果我们换了新的手机登录微信以后,微信聊天记录都会没有了哦,如果你要保存微信聊天记录的话,可以去微信聊天记录迁移哦,微信聊天记录迁
只要设置过手机QQ聊天记录网络保存,即使更换手机登录,所使用的手机QQ软件支持网络保存功能,就可以将聊天记录保存到网络,方便随时查看。反之未进行网络保存,其聊天
微信聊天记录删除了怎么恢复?好多友友的微信聊天记录删除了想找回来。那么怎么恢复删除了的微信聊天记录?微信聊天记录保存说明:聊天记录(包括视频、语音、图片、文字)
电脑如何查看记录如何查看微信聊天记录电脑如何查看记录如何查看微信聊天记录电脑如何查看记录如何查看微信聊天记录电脑如何查看记录如何查看微信聊天记录电脑如何查看记录