最近在公司接了一个任务,需要在几百台手机上安装一个app,目的是获取微信里面的通讯录,并且定时的把他发送到我们的服务器上。当时依次尝试的如下几个方案:

1.通过群控,将好友截图发送到服务端(python),利用python的图像识别库来获取好友的信息。

2.开发一个app,使用android自带AccessibilityService,模拟用户操作微信,然后获取屏幕中的内容。

3.破解微信的本地数据库。

非常尴尬的是前两个都失败了,否则也不会想到第三个方案了。第一个失败的原因是,利用图像识别,有些很相近的文字(i,1,l,h,n)识别成功率不高;第二个失败的原因是在于模拟用户操作的阶段无法达到预计的效果,也就导致了获取不到想要的屏幕内容。(前两个失败有可能是因为个人技术问题,无法实现)

但是重点来了:第三个我们成功了

我们是怎么知道微信把用户以及聊天的信息存到了本地数据库呢?

当我们打开手机的飞行模式的时候,打开微信,依旧可以看到里面的通讯录以及聊天记录。那么就说明微信肯定是将你能看到的所有信息都保存在了本地数据库里面,只是他将本地数据库加了密。既然存在了本地,我们就有办法把它取出来。

本地数据库的密码是什么呢?

请具体参考大神的文章,他通过反编译获取到微信的加密规则,特别厉害!

上述文章讲解主要是静态破解数据库,我们就基于他的静态破解方法,介绍下如何在代码中动态破解。不想看的同学们,我就直接介绍下微信本地数据库的加密规则了:

1.获取手机IMEI码

2.获取当前登录微信账号的uin(存储在sp里面)

3.拼接IMEI和uin

4.将拼接完的字符串进行md5加密

5.截取加完密的字符串的前七位(字母必须为小写)

那七位字符串就是数据库的密码了。因为微信已经有数亿的用户了,并且本地数据库又是存在用户的手机上,所以微信肯定不会轻易的对数据库进行大规模修改,所以密码的加密规则也是不可能变的,大家就放心用吧!

适用范围:已经获取root权限的手机

如果你的手机没有root,那下面的代码对你手机都是无效的哦~
下面正式进入主题

一、大致浏览下微信的目录

1.连接上你的手机,打开开发者模式
2.打开Android Device Monitor
3.进入到File Explorer子页,查看微信目录 /data/data/com.tencent.mm
如果你发现文件夹打不开,或者发现点击/data目录里面没有内容,可能是因为没有权限,请在Command中依次执行如下命令:
大概的介绍下微信的目录结构,本地数据库都在MicroMsg文件夹里面,SharedPerferences文件都在shared_prefs文件夹里面。之前说的获取数据库密码时候需要的uin就是存在微信的SharedPreferences里面,对应的是 /data/data/com.tencent.mm/shared_prefs文件夹。
微信的本地数据库存放在 /data/data/com.tencent.mm/MicroMsg里面的一长串字符串的目录里面
注意:如果你登录过多个账号就会出现多个此类的文件夹,所以我们在之后的代码中会通过循环来查找当前登录用户对应的数据库文件

二、授予当前app管理员权限以及修改微信目录的读写权限

最好在app一启动就执行下面的代码,并且在每次获取数据库内容的时候也要再次执行,避免出现无权限读取微信相关文件的异常
[java] view plain copy
  1. public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";
[java] view plain copy
  1. execRootCmd("chmod 777 -R " + WX_ROOT_PATH);
[java] view plain copy
  1. /**
  2. * 执行linux指令
  3. *
  4. * @param paramString
  5. */
  6. public void execRootCmd(String paramString) {
  7. try {
  8. Process localProcess = Runtime.getRuntime().exec("su");
  9. Object localObject = localProcess.getOutputStream();
  10. DataOutputStream localDataOutputStream = new DataOutputStream((OutputStream) localObject);
  11. String str = String.valueOf(paramString);
  12. localObject = str + "\n";
  13. localDataOutputStream.writeBytes((String) localObject);
  14. localDataOutputStream.flush();
  15. localDataOutputStream.writeBytes("exit\n");
  16. localDataOutputStream.flush();
  17. localProcess.waitFor();
  18. localObject = localProcess.exitValue();
  19. } catch (Exception localException) {
  20. localException.printStackTrace();
  21. }
  22. }

每次准备读取数据库之前都需要执行一次该命令。Process localProcess = Runtime.getRuntime().exec("su")先通过这个命令,使得当前app获取到root权限,然后再通过chmod命令来修改微信的data目录的读写权限,因为我们需要操作读取微信的数据库文件以及sp文件,所以必须要有微信文件的操作权限。

三、获取手机IMEI

IMEI的获取方法就很简单了
[java] view plain copy
  1. /**
  2. * 获取手机的imei码
  3. *
  4. * @return
  5. */
  6. private void initPhoneIMEI() {
  7. TelephonyManager tm = (TelephonyManager) MyApplication.getContextObject().getSystemService(TELEPHONY_SERVICE);
  8. mPhoneIMEI = tm.getDeviceId();
  9. }

记得添加权限

[html] view plain copy
  1. <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

四、获取微信的uin

微信的uin是存储在SharedPerferences里面,所以我们要在微信目录的shared_prefs文件夹里面查找其存放的xml文件,然后去解析它。
[java] view plain copy
  1. private static final String WX_SP_UIN_PATH = WX_ROOT_PATH + "shared_prefs/auth_info_key_prefs.xml";
[java] view plain copy
  1. /**
  2. * 获取微信的uid
  3. * 微信的uid存储在SharedPreferences里面
  4. * 存储位置\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml
  5. */
  6. private void initCurrWxUin() {
  7. mCurrWxUin = null;
  8. File file = new File(WX_SP_UIN_PATH);
  9. try {
  10. FileInputStream in = new FileInputStream(file);
  11. SAXReader saxReader = new SAXReader();
  12. Document document = saxReader.read(in);
  13. Element root = document.getRootElement();
  14. List<Element> elements = root.elements();
  15. for (Element element : elements) {
  16. if ("_auth_uin".equals(element.attributeValue("name"))) {
  17. mCurrWxUin = element.attributeValue("value");
  18. }
  19. }
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. LogUtil.log("获取微信uid失败,请检查auth_info_key_prefs文件权限");
  23. }
  24. }

微信的uin是存放在sharedPerferences文件夹里面的,具体路径为\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml。让我们来打开这个xml文件看看里面到底是什么样子的,还有我们需要的uin到底是存放在什么地方:

我们解析xml用的dom4j这个库里面的SAXReader,如果没有这个库的同学可以去这里下载

五、生成数据库密码

[java] view plain copy
  1. /**
  2. * 根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母)
  3. *
  4. * @param imei
  5. * @param uin
  6. * @return
  7. */
  8. private void initDbPassword(String imei, String uin) {
  9. if (TextUtils.isEmpty(imei) || TextUtils.isEmpty(uin)) {
  10. LogUtil.log("初始化数据库密码失败:imei或uid为空");
  11. return;
  12. }
  13. String md5 = md5(imei + uin);
  14. String password = md5.substring(0, 7).toLowerCase();
  15. mDbPassword = password;
  16. }
[java] view plain copy
  1. /**
  2. * md5加密
  3. *
  4. * @param content
  5. * @return
  6. */
  7. private String md5(String content) {
  8. MessageDigest md5 = null;
  9. try {
  10. md5 = MessageDigest.getInstance("MD5");
  11. md5.update(content.getBytes("UTF-8"));
  12. byte[] encryption = md5.digest();//加密
  13. StringBuffer sb = new StringBuffer();
  14. for (int i = 0; i < encryption.length; i++) {
  15. if (Integer.toHexString(0xff & encryption[i]).length() == 1) {
  16. sb.append("0").append(Integer.toHexString(0xff & encryption[i]));
  17. } else {
  18. sb.append(Integer.toHexString(0xff & encryption[i]));
  19. }
  20. }
  21. return sb.toString();
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. return null;
  25. }
  26. }

这一步比较容易,通过拼接字符串以及md5加密后就可以获取到数据库的密码

六、查找微信目录下的数据库文件

因为我们需要通过密码来连接微信的EnMicroMsg.db文件,所以我们需要先通过匹配算法把我们需要的db文件给查找出来。如果该手机的用户切换过登录账号,那么每个账号都会生成一个EnMicroMsg.db,所以我们要把所有的db文件都给匹配出来。
[java] view plain copy
  1. public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";
  2. public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";
  3. private static final String WX_DB_DIR_PATH = WX_ROOT_PATH + "MicroMsg";
  4. private List<File> mWxDbPathList = new ArrayList<>();
  5. private static final String WX_DB_FILE_NAME = "EnMicroMsg.db";
[java] view plain copy
  1. File wxDataDir = new File(WX_DB_DIR_PATH);
  2. mWxDbPathList.clear();
  3. searchFile(wxDataDir, WX_DB_FILE_NAME);
[java] view plain copy
  1. /**
  2. * 递归查询微信本地数据库文件
  3. *
  4. * @param file     目录
  5. * @param fileName 需要查找的文件名称
  6. */
  7. private void searchFile(File file, String fileName) {
  8. if (file.isDirectory()) {
  9. File[] files = file.listFiles();
  10. if (files != null) {
  11. for (File childFile : files) {
  12. searchFile(childFile, fileName);
  13. }
  14. }
  15. } else {
  16. if (fileName.equals(file.getName())) {
  17. mWxDbPathList.add(file);
  18. }
  19. }
  20. }
通过searchFile我们会对MicroMsg这个文件夹进行遍历查询,将所有的EnMicroMsg.db文件路劲存储在mWxDbPathList中,以便于我们后期连接的时候使用

七、连接数据库

终于到了最关键的一步了。这时候需要注意两点:

  1.我们千万不可以直接通过net.sqlcipher.database.SQLiteDatabase这个类来连接我们上一步里面查找到的微信目录下的EnMicroMsg.db文件,可能是因为一个数据库文件不能被多次连接的情况,只要我们一成功连接上那个db文件,微信的客户端就会自动退出登录,并且会出现异常。所有我现在的做法是把这个db文件拷贝到我们自己的app目录下,再进行连接。
  2.当我们有多账号登录过,就会存在多个EnMicroMsg.db文件,但是我们的数据库密码只有一个,也就是说通过这个密码能连接成功的数据库就表明是当前微信登录用户的数据库。因为sqlcipher这个库中没有提供校验密码的方法,所以我们只能每次通过强行连接来判断密码是否正确,如果正确的话代码就会正常执行,错误的话就会抛出异常,因此我们要在这个方法外面加上try-catch来处理密码错误的异常。(如果有更好的方法,请留言,谢谢!)
2017-09-08更新:感谢暖气片儿L在评论中提供的方法。之前如果用户登陆过多个微信账号,那么每一个微信账号都会在各自的文件夹下生成一个EnMicroMsg.db文件,用于存储当前账号的联系人和聊天记录等信息。但是我们解析出来的密码只有一个(最后登陆的微信账号的密码),之前是通过撞库(所有db文件都尝试连接一次,直到成功为止),现在通过一个方法可以准确的定位到uin对应的EnMicroMsg.db文件,MD5("mm"+auth_info_key_prefs.xml中解析出微信的uin码) 生成的md5就是EnMicroMsg.db所处的父级文件夹的名称。
[java] view plain copy
  1. private String mCurrApkPath = "/data/data/" + MyApplication.getContextObject().getPackageName() + "/";
  2. private static final String COPY_WX_DATA_DB = "wx_data.db";
[java] view plain copy
  1. //处理多账号登陆情况
  2. for (int i = 0; i < mWxDbPathList.size(); i++) {
  3. File file = mWxDbPathList.get(i);
  4. String copyFilePath = mCurrApkPath + COPY_WX_DATA_DB;
  5. //将微信数据库拷贝出来,因为直接连接微信的db,会导致微信崩溃
  6. copyFile(file.getAbsolutePath(), copyFilePath);
  7. File copyWxDataDb = new File(copyFilePath);
  8. openWxDb(copyWxDataDb);
  9. }
[java] view plain copy
  1. /**
  2. * 复制单个文件
  3. *
  4. * @param oldPath String 原文件路径 如:c:/fqf.txt
  5. * @param newPath String 复制后路径 如:f:/fqf.txt
  6. * @return boolean
  7. */
  8. public void copyFile(String oldPath, String newPath) {
  9. try {
  10. int byteRead = 0;
  11. File oldFile = new File(oldPath);
  12. if (oldFile.exists()) { //文件存在时
  13. InputStream inStream = new FileInputStream(oldPath); //读入原文件
  14. FileOutputStream fs = new FileOutputStream(newPath);
  15. byte[] buffer = new byte[1444];
  16. while ((byteRead = inStream.read(buffer)) != -1) {
  17. fs.write(buffer, 0, byteRead);
  18. }
  19. inStream.close();
  20. }
  21. } catch (Exception e) {
  22. System.out.println("复制单个文件操作出错");
  23. e.printStackTrace();
  24. }
  25. }
[java] view plain copy
  1. /**
  2. * 连接数据库
  3. *
  4. * @param dbFile
  5. */
  6. private void openWxDb(File dbFile) {
  7. Context context = MyApplication.getContextObject();
  8. SQLiteDatabase.loadLibs(context);
  9. SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
  10. public void preKey(SQLiteDatabase database) {
  11. }
  12. public void postKey(SQLiteDatabase database) {
  13. database.rawExecSQL("PRAGMA cipher_migrate;"); //兼容2.0的数据库
  14. }
  15. };
  16. try {
  17. //打开数据库连接
  18. SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, mDbPassword, null, hook);
  19. //查询所有联系人(verifyFlag!=0:公众号等类型,群里面非好友的类型为4,未知类型2)
  20. Cursor c1 = db.rawQuery("select * from rcontact where verifyFlag = 0 and type != 4 and type != 2 and nickname != '' limit 20, 9999", null);
  21. while (c1.moveToNext()) {
  22. String userName = c1.getString(c1.getColumnIndex("username"));
  23. String alias = c1.getString(c1.getColumnIndex("alias"));
  24. String nickName = c1.getString(c1.getColumnIndex("nickname"));
  25. }
  26. c1.close();
  27. db.close();
  28. } catch (Exception e) {
  29. LogUtil.log("读取数据库信息失败" + e.toString());
  30. //            e.printStackTrace();
  31. }
  32. }
通过上述的代码,先进行db文件的拷贝,然后再通过SQLCipher这个库来连接加密的数据库,之后我们就可以进行我们需要的sql查询了。上述代码中的sql查询加了一些条件,是因为做了一些业务逻辑的判断,去除了公众号、微信群这些联系人,正常测试可以直接使用“select * from rcontact”就可以了。
记得在gradle中引用库:
[java] view plain copy
  1. compile 'net.zetetic:android-database-sqlcipher:3.5.4@aar'

关于SQLCipher的详细使用方法可以参考其官网https://www.zetetic.net/sqlcipher/sqlcipher-for-android/

八、sqlcipher图形工具的使用

通过这个工具,我们可以快速的查看微信的db文件里面有哪些表,每个表里面有哪些字段,然后我们就可以在代码中写出相应的sql语句来查询我们需要的数据了

sqlcipher的下载传送门来咯~http://download.csdn.net/detail/njweiyukun/9729084
使用方法也很简单
1.首先我们要通过Android Device Monitor里面的File Explorer将微信EnMicroMsg.db文件拷贝出来

2.将拷贝出来的db文件用sqlcipher.exe打开并输入密码
Database Structure里面都是表结构,Browser Data里面则是表里面的数据了。
常用库介绍:【rcontact】联系人表,【message】聊天消息表

九、总结

总结一下步骤:
1.让当前app获取su权限,以及修改微信目录的读写权限。
2.获取手机的IMEI码。
3.从\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml中解析出微信的uin码。
4.获取数据库密码:拼接IMEI和uin,通过md5加密后,取前7位小写的字符串。
5.从/data/data/com.tencent.mm/MicroMsg中遍历查找所有的微信数据文件EnMicroMsg.db。
6.将EmMicroMsg.db文件拷贝到当前app目录,然后通过SQLCipher连接数据库。
通过上述的常规代码我们已经可以在代码里面获取微信数据库的所有内容了。我们从微信的sp和db文件中也可以获取到微信当前登录的用户信息,并且我们可以启一个service,利用一些保活措施,让我们的程序不被轻易杀死,这样可以保证不停的将联系人数据库发送到服务器。也可以做一个开机启动等等等,这些代码有需要的可以自行添加,留言也可以。
如果有哪里写错和疏忽的地方,请及时提出

Android动态破解微信本地数据库(EnMicroMsg.db)相关推荐

  1. 直接利用Android手机破解微信加密数据库EnMicroMsg.db

    ※首先,简单介绍一下微信加密数据库EnMicroMsg.db的破解方法: 要先批评一下微信,居然用开源的数据库加密方式,这不是一破解一个准吗... 如果你的模拟器或者真机已经获得了root权限,就可以 ...

  2. 利用Android手机破解微信加密数据库EnMicroMsg.db文件

    要先批评一下微信,居然用开源的数据库加密方式,这不是一破解一个准吗? 如果你的模拟器或者真机已经获得了root权限,就可以直接将记录聊天记录的数据库文件拷贝出来,数据库文件具体存放位置如下: 在/da ...

  3. Android逆向_微信本地数据库解密与删除聊天记录恢复完全教程

    # 前言 在电子数据取证过程中,对微信本地数据库的解密.提取与恢复是非常重要的工作内容.本文以华为mate系列手机和最新版的微信(7.0.3)为例,通过总结互联网上已经发表的文章经验,主要针对**华为 ...

  4. 破解微信本地数据库,无法找回删除的聊天记录

    [说明:操蛋的微信从4.0后本地数据库都加密了,密码是IMEI+UIN取MD5 32位小写加密前7作为密码]  [数据库文件请ROOT后用RE浏览器得到] [UIN 文件路径:/data/data/ ...

  5. Android打开微信本地数据库详细步骤二

    特别声明:本文章只是用于技术交流,不可用于非法行为. 阅读过上篇文章后,我想你一定去取出来微信本地数据库文件EnmicroMsg.db,但是取出来有什么用呢?又打不开,加密了.好比邂逅了个穿着铁内裤的 ...

  6. 安卓微信本地数据库解密与删除聊天记录恢复完全教程

    安卓微信本地数据库解密与删除聊天记录恢复完全教程 前言 正文 经验回顾 新的问题 解决华为旧备份数据导出问题 解密索引数据库 先要解密微信消息库 解密索引库 从索引库恢复被删除的消息 总结 [原创内容 ...

  7. PC微信机器人之实战分析微信本地数据库获取密码

    今天主要讨论一下怎么找PC微信本地数据库的密钥,我们知道微信使用的数据库是sqlite3,然后数据库是经过 aes 加密的,我们需要找到aes的密钥,才能解密,然后进行数据库操作.思路是在微信登陆的时 ...

  8. Android studio如何查看本地数据库

    android开发有时候涉及到本地存储的时候,需要用到数据库,当开发者需要查看本地数据库的内容时,就会发现非常麻烦.以前我觉得google在这一方面是完全没有倾听开发者的呼声呀!这是有多麻烦就有多麻烦 ...

  9. Android中实现微信本地视频发布到朋友圈功能

    编码美丽 2016-11-14 17:49 一.前言 前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳作弊 本文继续来介绍如何使用Xposed框架编写第二个微信插件,可 ...

  10. 破解微信数据库 并查询数据上传服务器

    由于工作需求破解了微信的数据库 并获取想要的信息上传服务器 都是内部手机 网上大神反编译了微信 发现微信的数据库是通过 手机的IMEI(唯一识别码) + UIN 大写的IMEI + UIN 进行MD5 ...

最新文章

  1. javaScript的调试(二)
  2. 你还在使用 try-catch-finally 关闭资源?
  3. HEOI2016/TJOI2016 字符串问题
  4. 内核中的HZ 及延迟等
  5. jdbc java例子_Spring JDBC 例子
  6. Numpy-矩阵的分割
  7. c语言函数能改变指针吗,如何修改传递给C中函数的指针?
  8. 学计算机用多大的u盘合适,u盘装系统需要多大的u盘|装系统需要多大的U盘
  9. 百度文库下载助手使用说明
  10. 单例模式几种实现方式和代码
  11. truncate table(截断表)
  12. Java多线程(7):JUC(下)
  13. 电机对应的电流计算方式及电线、端子的选型
  14. aria2简单下载脚本
  15. 免线圈高频无线充电IC无线供电芯片方案芯片XKT-511
  16. fatal: unable to access ‘xxx‘: schannel: failed to receive handshake, SSL/TLS connection f...
  17. 快速学会使用association和collection
  18. 云计算学习1——OpenStack云计算安装部署步骤图文并茂(先电2.2)
  19. 中科呐喊WiFi热点广告机,WiFi营销及创意展示神器
  20. 电脑测试瓶颈的软件,电脑性能检测工具(Fresh Diagnose)

热门文章

  1. SAP ABAP 常用事务代码
  2. 【计蒜客 - 蓝桥训练】蒜厂年会(循环数列的最大子段和)
  3. 赖活不如好死?慎选你的死亡方式
  4. Interpreter(解释器)
  5. Interpreter
  6. 315前瞻:哪些互联网消费领域今年可能上榜?
  7. spark的数三角形算法_Graphx图算法【1】三角形TriangleCount
  8. 1-3 Python基本数据类型
  9. html将图片保存到文件夹,怎么把一张网页上的全部图片都保存到文件夹里
  10. IP地址 网关是什么?网络概念