【逆向】【Android微信】获取微信聊天记录

【逆向】【Android微信】获取微信聊天记录_第1张图片

2020-02-23 晴 郑州

出场人物

  • 沉瓶 - 产品经理
  • 饭咸 - 程序员

工作环境

  • 硬件:MacBook Pro (Retina, 13-inch, Early 2015)
  • 硬件相应系统:macOS Catalina,10.15.3
  • 微信版本7.0.4

需求

  • 微信好友头像照片墙

故事

沉瓶:在网上看到一些人微信头像做成一张图片,看着不错,既好玩又能吸引用户,这个能不能做。
饭咸:获取微信的信息方法很多,以前网页版微信能用的时候,可以直接网页请求获取,网上很多开源代码。
沉瓶:我看了,很多代码都不更新了,是不是不能用了。
饭咸:是的,微信关闭了服务器,这些请求自然不能做了。不过有些人直接通过对PC版本的微信进行HOOK,实现很多有意义的功能,你可以找找。
沉瓶:找到了一个,不过没有提供可以运行的程序,还有别的方法木有了。
饭咸:微信本身就是手机系统的,对手机进行分析就可以了,而且分析微信的文章也很多,微信程序没有加壳,分析难度也不大,可以试试。
沉瓶:那你试试,我研究下怎么拼接图片去。
饭咸:好的,盘它。

需求分析

    1. 获取微信头像
    • 1.1 获得数据库文件路径和密匙
    • 1.2 将数据库文件导出到本地
    • 1.3 打开加密的数据库文件
    1. 将头像拼接成照片墙

实现步骤

1. 获取微信头像

关于获取微信聊天记录的方案网络上有很多资料,本博文就是根据下面的文章学习而来的:

  • Android动态破解微信本地数据库(EnMicroMsg.db)
  • 记微信数据库解密

结合上面两篇博文,我执行了下面的方案。

1.1 获得数据库文件路径和密匙

根据资料,我们找到一份基于Xposed的源码https://github.com/adamyi/AndroidWechatSQLiteDecrypt,该源码可以获得EnMicroMsg.db文件的路径和密码,但是未能正确的获取加密方式,可能是因为代码长时间不更新的原因吧(代码是两年前的)。

源码逻辑不复杂,hook了数据库打开的操作,在这个地方,腾讯执行了数据库的打开操作,该操作将路径、密码、加密方式都作为参数传了进来。具体代码如下所示:

public class XModule implements IXposedHookLoadPackage {
    static private String TAG = "wxSqlPwdHook";

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if (loadPackageParam.packageName.equals("com.tencent.mm")) {

            XposedHelpers.findAndHookMethod(
					"com.tencent.wcdb.database.SQLiteDatabase", 														// 被HOOK对象名
					loadPackageParam.classLoader, 																		// classLoader,固定的值,不用关心
					"openDatabase", 																					// 被HOOK对象的函数名
					String.class,																						// 参数0:数据库全路径
                    byte[].class, 																						// 参数1:用户名密码(一个加密后的7位值)
					loadPackageParam.classLoader.loadClass("com.tencent.wcdb.database.SQLiteCipherSpec"),				// 参数2:是一个SQLiteCipherSpec对象,该对象中包含了加密方式
                    loadPackageParam.classLoader.loadClass("com.tencent.wcdb.database.SQLiteDatabase$CursorFactory"),	// 参数3:某工厂对象
					int.class,																							// 参数4:Flags,未知标记
                    loadPackageParam.classLoader.loadClass("com.tencent.wcdb.DatabaseErrorHandler"),					// 参数5:错误处理的句柄
					int.class,																							// 参数6:加密方式的某个参数PoolSize
                    new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
							// 参数0:数据库全路径
                            Log.i(TAG, "Path: " + param.args[0]);
							// 参数1:用户名密码(一个加密后的7位值)
                            Log.i(TAG, "Password: " + new String((byte[]) param.args[1], "UTF-8"));
                            Formatter formatter = new Formatter();
                            for (byte b : (byte[]) param.args[1]) {
                                formatter.format("%02x", b);
                            }
                            Log.i(TAG, "Password (hex): 0x" + formatter.toString());
							// 参数2:是一个SQLiteCipherSpec对象,该对象中包含了加密方式
                            Log.i(TAG, "CipherSpec - Cipher: " + XposedHelpers.getObjectField(param.args[2], "cipher"));
                            Log.i(TAG, "CipherSpec - KdfIteration: " + XposedHelpers.getIntField(param.args[2], "kdfIteration"));
                            Log.i(TAG, "CipherSpec - Hmac Enabled: " + XposedHelpers.getBooleanField(param.args[2], "hmacEnabled"));
                            Log.i(TAG, "CipherSpec - Page Size: " + XposedHelpers.getIntField(param.args[2], "pageSize"));
							// 参数4:Flags,未知标记
                            Log.i(TAG, "Flags: " + param.args[4]);
							// 参数6:加密方式的某个参数PoolSize
                            Log.i(TAG, "PoolSize: " + param.args[6]);
                        }
                    });
        }
    }
}

代码使用的是XPosed框架,使用XposedHelpers.findAndHookMethod函数做的HOOK操作,HOOK点为com.tencent.wcdb.database.SQLiteDatabase::openDatabase,该函数的参数分别为:

  • 参数0:数据库全路径
  • 参数1:用户名密码(一个加密后的7位值)
  • 参数2:是一个SQLiteCipherSpec对象,该对象中包含了加密方式
  • 参数3:某工厂对象
  • 参数4:Flags,未知标记
  • 参数5:错误处理的句柄
  • 参数6:加密方式的某个参数PoolSize

我使用的微信版本为7.0.4,上述代码未能正确的解析SQLiteCipherSpec对象值,为了获得该值,我对微信进行调试,发现SQLiteCipherSpec对象的成员名称变了,如下所示:

p2 = {SQLiteCipherSpec@9102} 
 hmacAlgorithm = 0
 hmacEnabled = false
 kdfAlgorithm = 0
 kdfIteration = 4000
 pageSize = 1024
 shadow$_klass_ = {Class@8246} "class com.tencent.wcdb.database.SQLiteCipherSpec"
 shadow$_monitor_ = -2105857932

我们将这些值修改即可正常的打印出来,打印调试日志如下所示(我这边使用的是VirtualApp进行试验的):
【逆向】【Android微信】获取微信聊天记录_第2张图片

最终代码已经上传到github仓库,请自取。

1.2 将数据库文件导出到本地

将数据库文件导出到本地,思路也很多,我的方案是,将android手机作为ftp服务器(Servers Ultimate Pro),在本机(PC)使用FileZilla软件连接手机的ftp服务器,直接将文件下载到本地。

ps:

  • Servers Ultimate Pro是一款集成了ssh、ftp、http等数十种协议的服务器app,可以轻松的将android手机变为服务器使用,收费软件,价格不美丽。
  • 本博文使用的是VirtualApp进行XPosed测试,需要将Servers Ultimate Pro服务器程序也安装在VirtualApp中,这样以来,微信和服务器程序就处于同一用户权限了,所以PC上的FileZilla客户端就可以访问微信的数据文件了。

1.3 打开加密的数据库文件

实用开源项目SQLiteStudio,在github上下载最新的软件即可,该软件跨平台(Mac、Windows已经测试通过)、支持多种加密方式(这里选择SQLCipher选项),使用方法如下图所示:
【逆向】【Android微信】获取微信聊天记录_第3张图片
【逆向】【Android微信】获取微信聊天记录_第4张图片
【逆向】【Android微信】获取微信聊天记录_第5张图片

选择数据库软件的过程经历了很多次尝试,尝试过程请参考另一篇文章:【程序员日记】2020-02-19__踩坑微信加密后的Android数据库,

2. 将头像拼接成照片墙

这里不详细介绍了,参考下面资料即可

  • 使用 Python + Pillow 完成图片墙拼图
  • 用python的PIL库轻松拼接一百张照片
  • 提取微信/QQ聊天记录并生成词云
  • https://github.com/yangxuanxc/wechat_friends

声明

本文章仅供用于技术研究用途,请勿利用文章内容操作用于违反法律的事情。

关于作者

欢迎各位关注公众号和QQ群进行技术交流,关注有福利喔。

微信公众号:

【逆向】【Android微信】获取微信聊天记录_第6张图片

qq群:IT技术控/953949723

逐梦中原技术交流QQ群

你可能感兴趣的:(杂货铺)