想要hook微信的消息并不难,网络上也有相关文章,但大多版本停留在6.x,已经不太适用新版微信。
前戏
- 电脑安装好frida,手机传输并运行frida-server
- 准备一个安卓手机,并且安装微信 7.0.22 +
- 与手机安装的同款安装包给jadx一份,并反编译
- 微信提前关注自己的公众号,用于推送测试
- 准备好 Android Studio环境(要用到Android SDK)
说明: 这里我使用的微信版本为7.0.22,大于或者近似这个版本的应该都行。事实上,经过我对安卓 6.x, 7.x, 8.x 反编译后的源码研究发现,它们都有insertWithOnConflict
函数,6.x与7.x往后的有所不同,7.x往后的几乎没有变化
开淦
鉴于站在巨人的肩膀上hook,前人已经为我们探索好了要hook哪里,我们直接jadx中定位到insertWithOnConflict
函数。
不难发现,这个函数接受四个参数,并拼接成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
, 然后我们在公众号后台新建一个图文素材,并且推送到指定公众号上。
紧接着,控制台出现了数据
根据前人资料总结,content
字段,里面有我们想要的数据,包括标题,文章的链接,封面的地址,等等等。但是我们这里只看到了~SEMI_XML~
,这是什么玩意?之前看文章,好歹给的乱码的xml,我们解析一下便是了。从微信源码中看到过一个xmlParse的方法,调用后返回null, 失败了。
追踪 ~SEMI_XML~
用jadx搜索 SEMI_XML
。
可以看到这里有个静态常量MAGIC_HEAD
,看名字就知道你不是个好东西,追踪之。
可以看到,这里有个decode
方法非常的刺眼,而且参数是字符串。
进去以看,奥,原来就是解码的,结果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;
};
});
可以看到结果是对象形式的,到这里我们基本上成功一半了,说明解码很大概率成功了。
使用阿里巴巴fastjson进一步解码
这里是对象,我尝试使用.KeySet
也无法获得所有键,干脆直接用fastjson解码算了。
- 打开
https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.76/
- 下载
fastjson.jar
- 重新打包为
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;
};
});
再次推送文章并测试解码
可以看到这次,非常成功的解码出了数据,并且数据非常的详细。
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
作者: Austin 发表日期:2021-07-28 20:49
这个有点硬核
大佬太厉害了,但是实测发现了两个问题,1是如果一个公众号一次来好几条消息,数据是乱的,没法标题对应url,2是frida hook不知道怎么能把数据拿出来.
刚发现数据不是乱的,标签不一样。。
不乱啊,你用xposed写,把数据用post传给python服务,然后python过滤出url就行了,这样比较稳定。frida只是用来测试的。
调用insertWithOnConflict函数好像会被执行两遍,就是hook到2条消息就会返回4条结果有重复的。
对,恭喜你成功了,后续你去一下重就行了,比如用集合。
大佬,反编译那一步过不去了,能指点一下吗。直接用 apk-tool + dex2jar + jadx ,基本弄不出来
反编译只用 jadx 就行了,去查查基本使用教程。
大佬我把fastjson.dex放到/data/local/tmp还是会报Didn't find class "com.alibaba.fastjson.JSONObject" 找不到这个方法,大佬方便加个微信不