1_200221094923_1.png

想要hook微信的消息并不难,网络上也有相关文章,但大多版本停留在6.x,已经不太适用新版微信。

前戏

  1. 电脑安装好frida,手机传输并运行frida-server
  2. 准备一个安卓手机,并且安装微信 7.0.22 +
  3. 与手机安装的同款安装包给jadx一份,并反编译
  4. 微信提前关注自己的公众号,用于推送测试
  5. 准备好 Android Studio环境(要用到Android SDK)

说明: 这里我使用的微信版本为7.0.22,大于或者近似这个版本的应该都行。事实上,经过我对安卓 6.x, 7.x, 8.x 反编译后的源码研究发现,它们都有insertWithOnConflict函数,6.x与7.x往后的有所不同,7.x往后的几乎没有变化

开淦

鉴于站在巨人的肩膀上hook,前人已经为我们探索好了要hook哪里,我们直接jadx中定位到insertWithOnConflict函数。
a91b4b1d-08de-4356-b57b-ff7e8a9d5823.png
不难发现,这个函数接受四个参数,并拼接成sql语句(微信的很多东西都是存到本地sqlite数据库中,包括消息推送)。

编写frida python脚本

import frida, sys

jscode = open("hook.js", encoding='utf-8').read()

def on_message(message, data):
    print(message)

# 要Hook的软件包名
process = frida.get_usb_device().attach('微信')
script = process.create_script(jscode)
script.on('message', on_message)
print('运行完毕!')
script.load()
sys.stdin.read()

上面差不多是标准模板了,我们重点编写hook.js文件

Java.perform(function () {
    // Hook插入数据库
    var SQLiteDatabase = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
    var Set = Java.use("java.util.Set");
    var ContentValues = Java.use("android.content.ContentValues");

    SQLiteDatabase.insertWithOnConflict.implementation = function (arg1, arg2, arg3, i) {

        // 调用此函数,让其正常执行
        var ret = this.insertWithOnConflict.call(this, arg1, arg2, arg3, i);
        
        // 我们重点关注一下参数,因为参数中包含着我们想要的数据
        var values = Java.cast(arg3, ContentValues);
        var sets = Java.cast(values.keySet(), Set);
        var arr = sets.toArray().toString().split(",");
   
        for (var i = 0; i < arr.length; i++){
            var key = arr[i];
            var value = values.get(key);
            // 打印一下 键值对
            console.log(`key: ${key} ----  value ${value}`);
            console.log("____________________________________");
        }
        return ret;
    };
});

执行python hook.py, 然后我们在公众号后台新建一个图文素材,并且推送到指定公众号上。

4d08eaf0-5fe9-4633-9b6d-fc10d831bd6d.png
紧接着,控制台出现了数据
89ce25a6-cf10-4bd7-9d48-9237549e5ee1.png
根据前人资料总结,content字段,里面有我们想要的数据,包括标题,文章的链接,封面的地址,等等等。但是我们这里只看到了~SEMI_XML~,这是什么玩意?之前看文章,好歹给的乱码的xml,我们解析一下便是了。从微信源码中看到过一个xmlParse的方法,调用后返回null, 失败了。

追踪 ~SEMI_XML~

jadx搜索 SEMI_XML
fa84160d-d7b3-49c3-94cd-9372abeede08.png
可以看到这里有个静态常量MAGIC_HEAD,看名字就知道你不是个好东西,追踪之。

5fd604b3-9ad5-41d3-8a85-a2b5d6a6b7f0.png
可以看到,这里有个decode方法非常的刺眼,而且参数是字符串。

1c053bce-0f3b-47f7-9466-a6bb6de253bd.png
进去以看,奥,原来就是解码的,结果return一个hashMap,但实际上是 java.util.Map,找到其所在类com.tencent.mm.sdk.platformtools.SemiXml,调用之,我们用他们来解码~SEMI_XML~

尝试解码 ~SEMI_XML~

基于hook.js,新增一下代码

var xml = Java.use("com.tencent.mm.sdk.platformtools.SemiXml");
// 然后就可以调用xml.decode(),解码了

完整代码如下:

Java.perform(function () {
    // Hook插入数据库
    var SQLiteDatabase = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
    var Set = Java.use("java.util.Set");
    var ContentValues = Java.use("android.content.ContentValues");

    var xml = Java.use("com.tencent.mm.sdk.platformtools.SemiXml");

    SQLiteDatabase.insertWithOnConflict.implementation = function (arg1, arg2, arg3, i) {

        // 调用此函数,让其正常执行
        var ret = this.insertWithOnConflict.call(this, arg1, arg2, arg3, i);
        
        // 我们重点关注一下参数,因为参数中包含着我们想要的数据
        var values = Java.cast(arg3, ContentValues);
        var sets = Java.cast(values.keySet(), Set);
        var arr = sets.toArray().toString().split(",");

        for (var i = 0; i < arr.length; i++){
            var key = arr[i];
            var value = values.get(key);
            if(key === "content"){
                console.log(xml.decode(value));
            }
        }
        console.log("____________________________________");
        return ret;
    };
});

ac81a83b-e6df-4f81-bcd6-b3abc33f872d.png
可以看到结果是对象形式的,到这里我们基本上成功一半了,说明解码很大概率成功了。

使用阿里巴巴fastjson进一步解码

这里是对象,我尝试使用.KeySet也无法获得所有键,干脆直接用fastjson解码算了。

  1. 打开https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.76/
  2. 下载 fastjson.jar

638c4f7e-f44f-4c70-bf81-bcd82254e246.png

  1. 重新打包为dex
    将下载的文件名改为fastjson.jar并拖至Android SDK的目录,我的是C:\Users\Austin\AppData\Local\Android\Sdk\build-tools\30.0.3

然后该目录下执行如下命令

dx --dex --output=fastjson.dex fastjson.jar

得到的 fastjson.dex通过adb push到手机 /data/local/tmp目录。

然后我们在hook.js,就可以使用fastjson了。
关键相关代码:

Java.openClassFile('/data/local/tmp/fastjson.dex').load();
var JSONObject = Java.use('com.alibaba.fastjson.JSONObject');
JSONObject.toJSONString(obj);

完整代码:

Java.perform(function () {
    // Hook插入数据库
    var SQLiteDatabase = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
    var Set = Java.use("java.util.Set");
    var ContentValues = Java.use("android.content.ContentValues");
    var xml = Java.use("com.tencent.mm.sdk.platformtools.SemiXml");
    Java.openClassFile('/data/local/tmp/fastjson.dex').load();
    var JSONObject = Java.use('com.alibaba.fastjson.JSONObject')

    SQLiteDatabase.insertWithOnConflict.implementation = function (arg1, arg2, arg3, i) {

        // 调用此函数,让其正常执行
        var ret = this.insertWithOnConflict.call(this, arg1, arg2, arg3, i);
        
        // 我们重点关注一下参数,因为参数中包含着我们想要的数据
        var values = Java.cast(arg3, ContentValues);
        var sets = Java.cast(values.keySet(), Set);
        var arr = sets.toArray().toString().split(",");

        for (var i = 0; i < arr.length; i++){
            var key = arr[i];
            var value = values.get(key);
            if(key === "content"){
                var str_ = xml.decode(value)
                var res = JSONObject.toJSONString(str_);
                console.log(res);
            }
        }
        console.log("____________________________________");
        return ret;
    };
});

再次推送文章并测试解码

bcb17975-75f7-4a04-b3b1-df0f4901af9d.png

可以看到这次,非常成功的解码出了数据,并且数据非常的详细。

总结

勇敢coder,不怕困难,冲冲冲。大胆猜测,多做调试。
欢迎各位长按下面图片关注我的公众号:Code之禅, 我会不定期分享前后端编程,逆向、爬虫等相关文章。
请大家观看的时候自行准备一瓶水,因为真的很干。

ffdbdb77-b9e4-4a6b-a1db-835b353673f8.png

标签: frida, 逆向, 安卓, 微信公众号, hook

分类: 所有文章,爬虫与逆向,WEB前端

相关文章

2021.07.19   Frida 安装与使用踩坑

仅有一条评论

  1. vector vector

    这个有点硬核

添加新评论