通过Frida创建API进行爬取内容——记一次爬取某视频App直播源的过程...
发布网友
发布时间:2024-09-25 20:58
我来回答
共1个回答
热心网友
时间:2024-10-04 06:38
马上要过年了,过年了肯定是需要有电视作为背景音乐的,但是否有稳定的直播源呢?这时突然看到了“某视频App”里的电视功能,虽然也可以一键投屏到电视上,但突然想,是否可以抓包获取它的直播源?毕竟它在不登录的情况下,也是可以观看几十个频道的直播(虽然大部分只有720P的清晰度)。因为自己也是第一次尝试去解包App,很多内容都是现查现学的,所以便做此记录,一是分享二是备忘。
尝试抓包,工具:Charles、ApiPost7、Android手机、IPhone。首先要做的,肯定是抓包,看看他们之间都做了哪些勾当,我选用的是Charles。毫不意外,在抓包的时候就遇到了各种问题。在使用Android手机抓包的过程中,最开始遇到的是安装证书后,依旧SSL连接失败的问题。查了各种资料,发现Android7以上的系统,App可能不再信任用户自己添加的凭证,所以需要root手机后将凭证移到系统目录下。保存Charles证书到本地,使用openssl查询文件hash名,将证书文件重命名为:68dd95e9.0,使用MT管理器或者adb工具,复制到手机 /system/etc/security/cacerts目录下。正常情况下,这时候在 加密与凭据 → 信任的凭据,“系统”标签下就能看到刚刚添加进去的证书了。如果你的Charles很长时间没用了,一定要看一下证书的过期时间,如果证书已经过期了,那最好重置一下证书。这时候会发现安装了证书后,打开网页或使用其他App都可以正常抓包,但打开某视频App,依旧是没有网络。之后又查资料,有可能某视频App使用了SSL Pinning技术,就是开发者只信任自己的证书。又在手机上安装了LSPosed以及JustTrustMe模块并启用后问题依旧。使用IPhone抓包,IPhone上安装证书的方式与Android不同,将证书文件下载到手机内后,在设置的“VPN与设备管理”中,先安装描述文件。然后再去 通用 → 关于本机 → 证书信任设置 中,启用证书。相对来说,iPhone的安装便简单了很多,并且打开某视频App后一切正常,Charles中也出现了各种请求。但这时会发现,Charles拦截的请求中,几乎全部都是乱码,但所幸有一条https://liveinfo.ysp.cctv.cn 的请求是可以被正常解码的,而这条请求也正包含了我们需要的直播源信息。多尝试几次,并分析一下请求的参数,从直觉上来看,最重要的就是cKey这个值。
再使用ApiPost,反复测试大概猜出了defn、cnlid、livepid等几个参数代表的意义,也得知了platform、sdtfrom、appVer、encryptVer、cmd、cnlid、cKey等几个值为必填,所幸的是必填项里,也只有cKey是动态的,所以接下来我们就要看看怎么获取这个cKey值。
使用Frida Hook获取cKey。如果是在网页端,那直接用开发者工具进行断点调试就可以了。但是如果是App端该如何呢?不论如何从逻辑上来讲,总归的思路是需要反编译App。查了些资料,反编译Android App比iOS App要方便些,所以便从Android入手。使用jadx反编译App,映入眼帘的都是a、b、c、d、e、f、g啥的。虽然不懂,但很明显已经是被混淆过的,可能这个已经是App的标配了吧,还好jadx有反混淆功能。反混淆之后,至少一切稍微可读性高了些。找到cKey相关的方法,既然请求的关键字是cKey,那么代码中总归是有和cKey相关的字眼吧?尝试全局搜索一下。能找到很多,甚至可以找到名为 com.tencent.qqlive.tvkplayer.vinfo.ckey 的包,但说实话到目前为止还没什么思路。再尝试搜索下抓包到的域名“liveinfo.ysp.cctv.cn”,这次相对目标就明确了一些,进去看一看。虽然不太懂Java,但看包名(com.tencent.qqlive.tvkplayer.tools.config)以及这段的大概意思应该是建立了一个索引,其他地方只要引用“zb_cgi_host”便代表着要对这条url搞点什么事。再继续搜索“zb_cgi_host”,这就有意思了,确实搜索到了有地方在使用“zb_cgi_host”,看大概得意思就是判断是使用主地址还是备用地址,随手向下一翻,就看到了一个HashMap,简单理解这个就是Python里的字典,类似于键值对。下面建立了一个cKey的键值对,跟着这个赋值,我们进去 C13016e.m42151a() 看看。这里的代码没有进行混淆,意思也很明显了,我们跟进去再看看。来到 com.tencent.qqlive.tvkplayer.vinfo.ckey.CKeyFacade 这个类下面。这大段的代码,对于毫无经验的我来说,看着确实有些吃力,但其实可以借助chatGPT的力量来进行分析,直接让GPT告诉你答案就行。所以跟着GPT的指引,我们去看一下GenCKey函数。这里说明了这是一个原生方法,简单理解就是这个方法是包含在App引用的so库中,也确实在资源文件里找到了libckeygenerator.so文件,用IDA反编译后,确实也能找到一个GenCKey的方法。那么是否可以调用libckeygenerator.so文件里的GenCKey方法呢?从理论上来说应该是可以的,但这里面会涉及到处理器架构不同、so依赖等问题。我也尝试过在树莓派上调用、在手机里调用以及用 AndroidNativeEmu库来调用,均遇到了不同的问题……奈何水平及知识储备不足,只能放弃这个方法。使用Frida Hook getCKey方法,后续又在查资料的时候,了解到了Frida这个神器,它可以hook App在运行中使用的方法,并进行修改。那么我们是否可以用它做点什么呢?答案当然是肯定的,Frida功能很强大,作为初学者我也只能针对这个案例去逐步了解Frida。Frida的原理很简单,其实就是会在手机上运行一个server,然后可以在电脑上使用frida与之进行沟通。沟通的方式有两种,一种就是直接命令行用命令把JavaScript脚本发送到手机上,另外一种方式就是用Python脚本将JavaScript脚本发送到手机上。(目前我只了解这两种方式)。所以我们分别需要“服务端”与“客户端”。【服务端】在Frida的Github页面上直接下载对应的frida-server就行。在下载之前,可以使用adb命令查一下手机的cpu架构。将下载的文件解压出来,得到可执行文件,并复制到手机里运行。为了方便,我将下载的可执行文件重命名为frida-server64。如果没有报错那么frida server就已经启动了。【客户端】可以使用Python的pip工具来安装,安装 frida 和 frida-tools。如果服务端和客户端都准备完毕了,那么就可以用一个简单的命令来使用frida。其中 frida-ps 是frida的工具之一, -U 指的是使用USB连接的设备。通过该命令,就可以看到手机中当前正在运行的进程。一切都准备好了,我们结合前面反编译的结果,来准备一个hook脚本。根据之前的分析结果,我们猜测cKey是由 com.tencent.qqlive.tvkplayer.vinfo.ckey.CKeyFacade 类下面的 getCKey 方法生成的,这个方法接受9个参数,并返回1个字符串,这个返回的字符串很可能就是我们需要的cKey。那么这9个参数分别是什么呢?有两种方式,一种方式使通过jadx一点点去分析,另外一种方式就是通过Frida Hook getCKey方法,来直接看下这9个参数都是什么。根据以上,我们准备一个脚本,并命名为test.js。脚本中 getCkey 中的 overload() 里的参数如果不知道怎么填,可以先不填。然后Frida会报错,报错信息中会包括几种可能的参数,选择对应的复制进去即可。然后我们可以将脚本注入程序中,试一下。其中“某视频”为进程名,可以通过 frida-ps -U 来查看进程(不知道为什么有的时候进程是包名,有的时候是中文的App名……)。如果没什么报错,那就说明Frida已经注入成功了,接着在手机上刷新下页面,或者切换直播流试试。紧接着,我们就可以看到控制台中有log输出了,得到了我们想要的东西,并且这输出的内容很可能就是我们需要的cKey。同时我们也可以多试几次以及根据反编译的结果分别得出这输入的9个参数分别是什么。
到此为止,我们已经通过Frida Hook了getCKey方法,获得了其输入以及输出。其实jadx可以直接生成frida脚本,找到最开始找到的那个名为 m42096a() 的那个方法,鼠标右键,复制为frida片段。要注意的是,m42096a() 这个是jadx反混淆后的方法名,实际上App运行的时候这个方法名为 a() ,jadx在注释中也会注明原方法名,并且在自动生成的 frida片段 中也都进行了替换。但实际上运行这个脚本会发现,没有什么输出。其实是因为这个方法的输出是一个hashMap,我们需要把代码片段再加工一下,使控制台可以输出hashMap里的内容。如此,我们便可以获得所有的请求参数,然后进行分析了。那么,是否能获取到服务器响应的内容呢?回到jadx,来继续碰碰运气。试想一下,从逻辑上讲,直播源的请求与解析应该属于同一个模块,我们现在找到了请求的代码,那么解析的代码应该就在请求的代码附近。还是 com.tencent.qqlive.tvkplayer.vinfo.p543c.C13028d 这个类,我们找到这个类的文件位置。然后在同文件夹下翻一翻,很幸运地,我们翻到了 C13029e 这个类,在这个类里,我们又看到了类似于json解析的日志输出。那么,我们有理由相信 com.tencent.qqlive.tvkplayer.vinfo.p543c.C13029e.m42080a 方法的作用就是解析响应json字符串的。根据以上的内容,我们替换成反混淆前的方法名,整理成脚本变为:到此为止,我们已经通过使用Frida Hook了请求和响应,现在我们就可以很方便地进行分析了。通过同样的方法,我们也可以获得请求直播源地址时所有参数的含义了。
上面的所有案例中,Frida都是通过被动调用的方式来执行的,那么它可以主动调用对应的方法吗?这样就可以直接使用Frida创建一个api来获取cKey,然后使用Python来进行更多操作。Frida官方也提供了这样一个案例:简单说,就是通过frida提供的 rpc.exports ,来导出方法,以方便python可以随时调用。Python脚本远程调用Frida在实际应用的环境中,手机一直用USB连着电脑似乎也不太靠谱,而Frida也提供了远程调用的方式。让frida-server监听0.0.0.0那么之前注入脚本的命令也变成了在python中调用的方式,也变成了甚至也可以在虚拟机或者树莓派等设备中安装安卓系统作为专门的api服务器非实体Android系统若在树莓派中的Android系统,主要有两种选择。其他方案也可以参考大佬分享的安卓容器化部署方案:不管使用哪种方式,都需要自动启动应用以及frida,可以使用 Script Manager 来实现。
最后可以将上述所有的整理成脚本即可根据自己的需求去获取各个直播流的地址了。WaterRequests.py config.py getCkey.js m3u8.j2 main.py
最早在测试的时候,发现每个直播流的地址有效期大概是4小时左右,但后面开始批量获取直播流地址的时候每个直播流地址的有效期时间变得很短,大概十几分钟就失效了,猜测有可能和请求参数里的guid短时间内发送大量请求有关,但这些现在已经不重要了,更重要的是通过这次实践了解到的App爬虫思路。在我以往的应用中,一般只是爬取网页端的内容,所以下意识会认为如果要爬取App里的内容,需要分析大量的网络请求,甚至要破解各种加密算法。但通过这次实践,了解到了Frida神器,便完全可以换一种思路来爬取App中的内容,让App自己来成为核心算法的API服务器,借助这些api再去爬取App的内容,这样便可以省去很大一部分精力。另外,众所周知,某视频App本质上是由鹅厂的团队来开发的,所以在反编译过程中看到了大量的鹅厂使用的算法和框架。之所以Charles抓包的时候请求和响应都是乱码,也是因为其使用是jce协议,也就是使用的腾讯tars框架。在 com.tencent.videolite.android.datamodel.cctvjce 能看到大量的请求和响应的结构体,所有的请求数据都会经过这些结构体进行序列化成二进制流与服务器沟通,响应数据也同样经过结构体的反序列化后呈现在App中。