[转]Android动态破解微信本地数据库(EnMicroMsg.db)
Android动态破解微信本地数据库(EnMicroMsg.db)
发表于2017/1/5 0:21:20 3341人阅读
分类: android
最近在公司接了一个任务,需要在几百台手机上安装一个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权限的手机
一、大致浏览下微信的目录
二、授予当前app管理员权限以及修改微信目录的读写权限
public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";
execRootCmd("chmod 777 -R " + WX_ROOT_PATH);
/** * 执行linux指令 * * @param paramString */ public void execRootCmd(String paramString) { try { Process localProcess = Runtime.getRuntime().exec("su"); Object localObject = localProcess.getOutputStream(); DataOutputStream localDataOutputStream = new DataOutputStream((OutputStream) localObject); String str = String.valueOf(paramString); localObject = str + "\n"; localDataOutputStream.writeBytes((String) localObject); localDataOutputStream.flush(); localDataOutputStream.writeBytes("exit\n"); localDataOutputStream.flush(); localProcess.waitFor(); localObject = localProcess.exitValue(); } catch (Exception localException) { localException.printStackTrace(); } }
每次准备读取数据库之前都需要执行一次该命令。Process localProcess = Runtime.getRuntime().exec(“su”)先通过这个命令,使得当前app获取到root权限,然后再通过chmod命令来修改微信的data目录的读写权限,因为我们需要操作读取微信的数据库文件以及sp文件,所以必须要有微信文件的操作权限。
三、获取手机IMEI
/** * 获取手机的imei码 * * @return */ private void initPhoneIMEI() { TelephonyManager tm = (TelephonyManager) MyApplication.getContextObject().getSystemService(TELEPHONY_SERVICE); mPhoneIMEI = tm.getDeviceId(); }
记得添加权限
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
四、获取微信的uin
private static final String WX_SP_UIN_PATH = WX_ROOT_PATH + "shared_prefs/auth_info_key_prefs.xml";
/** * 获取微信的uid * 微信的uid存储在SharedPreferences里面 * 存储位置\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml */ private void initCurrWxUin() { mCurrWxUin = null; File file = new File(WX_SP_UIN_PATH); try { FileInputStream in = new FileInputStream(file); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(in); Element root = document.getRootElement(); List<Element> elements = root.elements(); for (Element element : elements) { if ("_auth_uin".equals(element.attributeValue("name"))) { mCurrWxUin = element.attributeValue("value"); } } } catch (Exception e) { e.printStackTrace(); LogUtil.log("获取微信uid失败,请检查auth_info_key_prefs文件权限"); } }
微信的uin是存放在sharedPerferences文件夹里面的,具体路径为\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml。让我们来打开这个xml文件看看里面到底是什么样子的,还有我们需要的uin到底是存放在什么地方:
五、生成数据库密码
/** * 根据imei和uin生成的md5码,获取数据库的密码(去前七位的小写字母) * * @param imei * @param uin * @return */ private void initDbPassword(String imei, String uin) { if (TextUtils.isEmpty(imei) || TextUtils.isEmpty(uin)) { LogUtil.log("初始化数据库密码失败:imei或uid为空"); return; } String md5 = md5(imei + uin); String password = md5.substring(0, 7).toLowerCase(); mDbPassword = password; }
/** * md5加密 * * @param content * @return */ private String md5(String content) { MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); md5.update(content.getBytes("UTF-8")); byte[] encryption = md5.digest();//加密 StringBuffer sb = new StringBuffer(); for (int i = 0; i < encryption.length; i++) { if (Integer.toHexString(0xff & encryption[i]).length() == 1) { sb.append("0").append(Integer.toHexString(0xff & encryption[i])); } else { sb.append(Integer.toHexString(0xff & encryption[i])); } } return sb.toString(); } catch (Exception e) { e.printStackTrace(); return null; } }
这一步比较容易,通过拼接字符串以及md5加密后就可以获取到数据库的密码
六、查找微信目录下的数据库文件
public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/"; public static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/"; private static final String WX_DB_DIR_PATH = WX_ROOT_PATH + "MicroMsg"; private List<File> mWxDbPathList = new ArrayList<>(); private static final String WX_DB_FILE_NAME = "EnMicroMsg.db";
File wxDataDir = new File(WX_DB_DIR_PATH); mWxDbPathList.clear(); searchFile(wxDataDir, WX_DB_FILE_NAME);
/** * 递归查询微信本地数据库文件 * * @param file 目录 * @param fileName 需要查找的文件名称 */ private void searchFile(File file, String fileName) { if (file.isDirectory()) { File[] files = file.listFiles(); if (files != null) { for (File childFile : files) { searchFile(childFile, fileName); } } } else { if (fileName.equals(file.getName())) { mWxDbPathList.add(file); } } }
七、连接数据库
终于到了最关键的一步了。这时候需要注意两点:
private String mCurrApkPath = "/data/data/" + MyApplication.getContextObject().getPackageName() + "/"; private static final String COPY_WX_DATA_DB = "wx_data.db";
//处理多账号登陆情况 for (int i = 0; i < mWxDbPathList.size(); i++) { File file = mWxDbPathList.get(i); String copyFilePath = mCurrApkPath + COPY_WX_DATA_DB; //将微信数据库拷贝出来,因为直接连接微信的db,会导致微信崩溃 copyFile(file.getAbsolutePath(), copyFilePath); File copyWxDataDb = new File(copyFilePath); openWxDb(copyWxDataDb); }
/** * 复制单个文件 * * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public void copyFile(String oldPath, String newPath) { try { int byteRead = 0; File oldFile = new File(oldPath); if (oldFile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[1444]; while ((byteRead = inStream.read(buffer)) != -1) { fs.write(buffer, 0, byteRead); } inStream.close(); } } catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); } }
/** * 连接数据库 * * @param dbFile */ private void openWxDb(File dbFile) { Context context = MyApplication.getContextObject(); SQLiteDatabase.loadLibs(context); SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { public void preKey(SQLiteDatabase database) { } public void postKey(SQLiteDatabase database) { database.rawExecSQL("PRAGMA cipher_migrate;"); //兼容2.0的数据库 } }; try { //打开数据库连接 SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, mDbPassword, null, hook); //查询所有联系人(verifyFlag!=0:公众号等类型,群里面非好友的类型为4,未知类型2) Cursor c1 = db.rawQuery("select * from rcontact where verifyFlag = 0 and type != 4 and type != 2 and nickname != \'\' limit 20, 9999", null); while (c1.moveToNext()) { String userName = c1.getString(c1.getColumnIndex("username")); String alias = c1.getString(c1.getColumnIndex("alias")); String nickName = c1.getString(c1.getColumnIndex("nickname")); } c1.close(); db.close(); } catch (Exception e) { LogUtil.log("读取数据库信息失败" + e.toString()); // e.printStackTrace(); } }
compile \'net.zetetic:android-database-sqlcipher:3.5.4@aar\'
关于SQLCipher的详细使用方法可以参考其官网https://www.zetetic.net/sqlcipher/sqlcipher-for-android/
八、sqlcipher图形工具的使用
通过这个工具,我们可以快速的查看微信的db文件里面有哪些表,每个表里面有哪些字段,然后我们就可以在代码中写出相应的sql语句来查询我们需要的数据了
使用方法也很简单
九、总结
0