前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Frida爬虫分析流程——以微信视频号下载为例

Frida爬虫分析流程——以微信视频号下载为例

作者头像
mythsman
发布2022-11-14 14:03:53
9.3K2
发布2022-11-14 14:03:53
举报
文章被收录于专栏:mythsman的个人博客

前言

微信的通信协议没有使用传统的https,而是采用 mmtls 和 quic 协议结合的方案(可能),导致常用的抓包方案完全无效。因此我们考虑使用逆向 hook 的方式,对微信视频号的数据进行获取。

Frida 是目前几乎最好跨平台hook工具,深受广大牢友的喜爱。因此我们考虑用这个工具来进行 hook 。

提前声明,以下操作有被封号的风险,各位看官可以尽量用小号操作。

准备

  1. 准备一个解锁了 bootloader 、刷了 TWRP 并安装了 Magisk 的手机。(当然,也有无需root权限的方法,但是用起来会不方便,还是建议 root )
  2. 准备好 adb 环境。
  3. 参考 FRIDA 安装 frida 用于 hook。并最好把官网的 Tutorials 看下。
  4. 准备 Pycharm 作为开发环境。
  5. 准备好 wechat.apk 。
  6. 参考 JADX 安装好 jadx 用于代码静态分析。

思路

Frida 爬虫的思路如下:

  1. 利用 adb 的 dumpsys 工具定位到我们关心的 Activity 页。
  2. 利用 jadx 的静态分析工具在代码中定位到解密后的后的数据对象。
  3. 利用 frida 的 hook 能力重写数据对象的构造、拷贝等关键方法,提取出入参出参等。
  4. 将提取到的数据序列化成json,并持久化。

流程

启动 frida-server

首先需要在 Magisk 商店中找到 MagiskFrida 插件。这个插件会在手机启动时以高权限启动 frida-server 服务器用于后续注入 hook 代码。

如果一不小心 frida-server 跪了,只需要重启手机即可。

定位Activity

首先我们需要大概了解我们关注的页面的一些信息,方便我们后续定位代码。因此我们先打开感兴趣的页面(我这里是微信视频号的 tab),并执行 dumpsys 命令。

这样我们知道了,这个页面对应的是 FinderHomeUI 这个 activity。

定位数据对象

打开 jadx-gui ,定位到 FinderHomeUI 这个类。(可能loading一段时间,如果报OOM,则需要 通过 $ mdfind jadx-gui 找到启动脚本,并修改 JVM 参数)。

简单的四下观望,就可以找一个叫 FeedData 的类,也找到类这个类的一个类似 Builder 模式的静态内部类。

看起来这个 i 方法大概就是构造 FeedData 这个类的方法了,因此我们可以考虑下 hook 这个 i 方法。

提取重要参数

找到了上面的 com.tencent.mm.plugin.finder.storage.FeedData$a  对象,我们就可以简单编写一个hook脚本看看。

代码语言:javascript
复制
# -*- coding: UTF-8 -*-
import frida
import sys


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


jscode = """
Java.perform(function () {

  var a = Java.use('com.tencent.mm.plugin.finder.storage.FeedData$a');

  var i = a.i;
  i.implementation = function (finderItem) {

    var res = i.call(this, finderItem);

    try{
        send(JSON.stringify(res))
    }catch (e){
        console.log('Error: ' + e);//自己的逻辑要做好catch防止脚本有问题导致app崩溃。
    }
    return res;//注意保证函数输出不变。
  };

});
"""

process = frida.get_usb_device().attach('com.tencent.mm')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running Hook')
script.load()
sys.stdin.read()

执行这个脚本,并滑动一下可以看到如下输入:

显然,这里的 payload 并没有正确的被序列化,因此我们需要再做一个用序列化工具。

数据序列化输出

由于安卓自带的 org.json.JSONObject 不支持json序列化,而 Javascript 的方法也无法序列化 java 对象。因此我们需要自己引入一个java包用于序列化,这里我选用无脑的fastjson。

  1. 首先需要下载fastjson的jar包,我在本地的maven仓库中找到了: /Users/myths/.gradle/caches/modules-2/files-2.1/com.alibaba/fastjson/1.2.69/6cb063f1d527ff65bdbb9ea74888a5ffc3f92197/fastjson-1.2.69.jar
  2. 然后利用 adb 的 build-tools 中的 dx 工具将 jar 包重新打包成 dex 包:$ /Users/myths/Library/Android/sdk/build-tools/26.0.2/dx --dex --output=fastjson.dex fastjson.jar
  3. 将上述生成的 dex 包 push 到手机中,例如 /data/local/tmp/fastjson.dex  下。
  4. 更改下脚本,再滑动下页面:
代码语言:javascript
复制
# -*- coding: UTF-8 -*-
import frida
import sys


def on_message(message, data):
    print(message['payload'])


jscode = """
Java.perform(function () {

  var a = Java.use('com.tencent.mm.plugin.finder.storage.FeedData$a');
  Java.openClassFile('/data/local/tmp/fastjson.dex').load();
  var JSONObject = Java.use('com.alibaba.fastjson.JSONObject')
  
  var i = a.i;
  i.implementation = function (finderItem) {

    var res = i.call(this, finderItem);

    try{
        send(JSONObject.toJSONString(res));
    }catch (e){
        console.log('Error: ' + e);
    }
    return res;
  };

});
"""

process = frida.get_usb_device().attach('com.tencent.mm')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running Hook')
script.load()
sys.stdin.read()

得到的 json 如下:

代码语言:javascript
复制
{
    "commentCount": 62,
    "description": "人心换人心,谁都有底线!更多情感内容点击关注@疗伤情感 #晏子情感",
    "expectId": -4877419130498574272,
    "feedId": -4877419130498574272,
    "hasBgmInfo": false,
    "id": -4877419130498574272,
    "likeCount": 2951,
    "liveId": 0,
    "liveStatus": 0,
    "localId": 0,
    "longVideo": false,
    "mediaList": [
        {
            "NMD": false,
            "NMq": 0,
            "NMt": false,
            "NMv": 28000,
            "NMw": "http://wxapp.tc.qq.com/251/20350/stodownload?encfilekey=RBfjicXSHKCOONJnTbRmmlD8cOQPXE48ibNoPibrzxbICjN0mdDNRj71nM2TJfVYGhIxX0WCMf74biaftFqLW2zGtdmXTJ8wibeesB7n8Ntyu5uOic8136mUibM44fnge8amjDu6BLmlSE59icicdjIrI7KtpP8FxXibG6LwzicQMlmaVaAicD0&adaptivelytrans=0&bizid=1023&dotrans=0&hy=SH&idx=1&m=5190cada35800678ed0b1f917a2a32b5",
            "NMx": "&token=x5Y29zUxcibDjE9JYkmdS0shbZ7djRmsC99U0UibtNT1u9hlAxSyM01icQZXHkptzicv",
            "bitrate": 0,
            "coverUrl": "http://wxapp.tc.qq.com/251/20304/stodownload?filekey=30350201010421301f020200fb040253480410d8869ba74f63ba490ac4069f994280f202030180d8040d00000004627466730000000131&storeid=323032313034303531303235343030303064316634353761356264386134653730353566363430303030303066623030303034663530&adaptivelytrans=0&bizid=1023&dotrans=0&hy=SH&m=d8869ba74f63ba490ac4069f994280f2",
            "decodeKey": "2065249527",
            "fileSize": 18284035,
            "full_bitrate": 0,
            "full_file_size": 0,
            "full_height": 0,
            "full_width": 0,
            "height": 1264,
            "hot_flag": 0,
            "includeUnKnownField": false,
            "md5sum": "f9f9eceb-6982-4e4d-bfa6-8db01fa9b522",
            "mediaId": "a6b5fac7d510c532a4376934a31015a4",
            "mediaType": 4,
            "spec": [
                {
                    "MZJ": 3141665,
                    "Nbp": 637,
                    "efu": "xV0",
                    "includeUnKnownField": true,
                    "vKg": "h264"
                },
                {
                    "MZJ": 1646534,
                    "Nbp": 345,
                    "efu": "xV2",
                    "includeUnKnownField": true,
                    "vKg": "h265"
                },
                {
                    "MZJ": 1037647,
                    "Nbp": 226,
                    "efu": "xV4",
                    "includeUnKnownField": true,
                    "vKg": "h265"
                },
                {
                    "MZJ": 472808,
                    "Nbp": 109,
                    "efu": "xV8",
                    "includeUnKnownField": true,
                    "vKg": "h265"
                },
                {
                    "MZJ": 418280,
                    "Nbp": 97,
                    "efu": "xV9",
                    "includeUnKnownField": true,
                    "vKg": "h264"
                },
                {
                    "MZJ": 295747,
                    "Nbp": 66,
                    "efu": "xV10",
                    "includeUnKnownField": true,
                    "vKg": "h265"
                }
            ],
            "thumbUrl": "http://wxapp.tc.qq.com/251/20304/stodownload?encfilekey=RBfjicXSHKCOONJnTbRmmlD8cOQPXE48ib0TrgC9GMRrlchGCNdXCyD2Pu6YbIWudBh6BngIDXS3M8Y18doicwuaXmAiblJJG5s2ib1XR3KMredUlbax6ZQvhQ77Ntoekw0O4VfpbFuHrhjIyEDd78AKe5GGybePkVA1jP75HyKHEyNc&adaptivelytrans=0&bizid=1023&dotrans=0&hy=SH&idx=1&m=d8869ba74f63ba490ac4069f994280f2",
            "thumb_url_token": "&token=x5Y29zUxcibCadRELU5qibEtIicbNZqQqzxGicvUUexAbqribwZDAdDicOa5koiawnrKUtV",
            "url": "http://wxapp.tc.qq.com/251/20302/stodownload?encfilekey=G83YYE2iciaib491UK8yGibLXOdhNpDoPpG748uNIa5DNuyuonSofEYDt1yf8eDoibNty4U8UXvSG2micv4HaEUcErdibfTOiaKKalN8FrUibibrfVqnPOh8sZFWl5oDALZajdFJsTg7Sqd4bPIyWib5CehDW4NbxzzdLUpoDvYBVjDkfp9C7Q&adaptivelytrans=0&bizid=1023&dotrans=906&hy=SH&idx=1&m=6b3f33e50bfc1c5c5f6f6c14e06b7d03&scene=0",
            "url_token": "&token=AxricY7RBHdWhPYjkduXw4angAXxhu8UMIxGebhCliaYtTT7dCtwIxRibXyGodLxZxcPJYzq9CN5dU",
            "videoDuration": 28,
            "width": 1080
        }
    ],
    "mediaType": 4,
    "nickName": "疗伤情感",
    "onlineNum": 0,
    "rvFeedList": [],
    "sessionBuffer": "eyJzZXNzaW9uX2lkIjoic2lkXzIzNjY4ODU5NDVfMTYxNzc4MDIwOTk3NzYzNl8xNDk3MjAwODg4IiwicmVjb21tZW5kX3R5cGUiOjMsInJlY29tbWVuZF9zeXN0ZW0iOjIsInJlY29tbWVuZF93b3JkaW5nIjoiIiwiY3VyX2xpa2VfY291bnQiOjI5NTEsImN1cl9jb21tZW50X2NvdW50Ijo2MiwicmVjYWxsX3R5cGVzIjpbMTAxMTM1XSwiZGVsaXZlcnlfc2NlbmUiOjEzLCJkZWxpdmVyeV90aW1lIjoxNjE3NzgwMjEwLCJzZXRfY29uZGl0aW9uX2ZsYWciOjIsInRvdGFsX2ZyaWVuZF9saWtlX2NvdW50IjowLCJuZXdfZnJpZW5kX2xpa2VfY291bnQiOjAsInJlY2FsbF9pbmRleCI6WzBdLCJ0YWdfaWQiOiIwOyIsInJlcXVlc3RfaWQiOjE2MTc3ODAyMDg4MTc5MTMsIm1lZGlhX3R5cGUiOjQsInZpZF9sZW4iOjI4LCJjcmVhdGVfdGltZSI6MTYxNzU4OTU4NiwidGFiX3R5cGUiOjQsInJlY2FsbF9pbmZvIjpbeyJyZWNhbGxfdHlwZSI6MTAxMTM1LCJyZWNhbGxfc2NvcmUiOjAuNzI2MjMyOTQ1OTE5LCJyZWNhbGxfaW5kZXgiOjAsInJlcG9ydF9pbmZvIjoiNDA2XzEwMzVfMF8wXzEifV0sInJhbmtfc2NvcmUiOjEwNC40NzExNDU2Mywic2VjcmV0ZV9kYXRhIjoiQmdBQUFmMmliZTJFMXIrRFRHc2gwbzB6RGJVbnpvTkUwZDZncjliOVdKdyttakM2NkhnM3VXY0phOTg9IiwidGFiX3Nlc3Npb25faWQiOjE2MTc3ODAyMDg5MjE3ODQsImZyaWVuZF9saWtlZF9saXN0IjoiIiwiZGV2aWNlX3R5cGVfaWQiOjIsImRldmljZV9wbGF0Zm9ybSI6IlJlZG1pIDYiLCJkb3dubG9hZF9zcGVlZF9rYnBzIjoxMzE1OTUsIm5ldF90eXBlIjoxLCJ2aWRlb19pZCI6MCwiaXNfY2hpbGQiOnRydWUsInBhcmVudF9tZWRpYV90eXBlIjowLCJwYXJlbnRfaWQiOjAsImZlZWRfcG9zIjoyLCJwdWxsX3R5cGUiOjEsInBhZ2VfbnVtIjowLCJjbGllbnRfcmVwb3J0X2J1ZmYiOiJ7XCJzZXNzaW9uSWRcIjpcIjE0M18xNjE3NzEwNzYyNTY4IyQyXzE2MTc3MTA3NjEyNjgjXCJ9IiwiaXNfbGl2ZV9mZWVkIjowLCJpc19saXZlX2ZpbmRlcnVzZXIiOjAsImV4dF9mbGFnIjowLCJjb21tZW50X3NjZW5lIjoyMCwib2JqZWN0X2lkIjoxMzU2OTMyNDk0MzIxMDk3NzM0NCwiZmluZGVyX3VpbiI6MTMxMDQ4MDgwNjQ2MDA0ODYsInBvaW5hbWUiOiIiLCJjaXR5IjoiIiwiZ2VvaGFzaCI6MzM3NzY5OTcyMDUyNzg3Mn0=",
    "timestamps": 1617780210295,
    "urlValidDuration": 172800,
    "userName": "v2_060000231003b20faec8cae38f1dc5d5ce00e432b077b11d4f3ce9c011535e0fff9bba95dcc6@finder"
}

这里要小心,过长的 long 在转 json 的时候可能会丢失精度,如果发现这种情况要特殊处理下,把 long 转成 string 。

数据处理

视频问题

检查了下数据,发现通过 url+url_token 拼接的视频流虽然能下载,但是下载下来是加密后的,无法直接播放。

后来发现 url+thumb_url_token 是可以播放的,高兴了一段时间。但是4月26号左右微信好像做了什么操作,导致这个渠道不能播放了。经过简单分析后发现视频解码的流程是放在native方法中,一时半会难以破解,那就只能想办法下载缓存了。

(当然,这些加密算法在安全组的同学面前都不算问题,三下五除二就发现原来是某个比较小众的流式加密算法🤭)

文件追踪

利用 frida-trace 可以追踪app的系统调用,因此我们可以尝试看下 app 写文件的 open 方法。

$ frida-trace -U -i open com.tencent.mm

滑动下页面,我们发现了下面的日志:

看起来非常像是视频的缓存文件。打开一看,果然是。。。

那么,照着这个格式搜索下代码,稍微拼接下就能搞定这个缓存路径。

其他

调试过程中还发现一个查看当前调用堆栈的方法,可以辅助分析:

代码语言:javascript
复制
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))

总结

头一次使用 frida 还是很有新鲜感的,用 Python 向 Android 中插入调用 Java api 的 Javascript 代码。。。

参考资料

基于Frida的全平台逆向分析

APP逆向神器之Frida

基于TLS1.3的微信安全通信协议mmtls介绍

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 准备
  • 思路
  • 流程
    • 启动 frida-server
      • 定位Activity
        • 定位数据对象
          • 提取重要参数
            • 数据序列化输出
            • 数据处理
              • 视频问题
                • 文件追踪
                  • 其他
                    • 总结
                    • 参考资料
                    相关产品与服务
                    文件存储
                    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档