🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>文章背景: 公司最近在做现在很火的一个短视频数据分析的项目,其中有一个功能需要快手直播间的数据,比如观看直播间的人数,用户弹幕数据,用户的打赏信息。本来是有专门做python爬虫的小哥,这个小哥是懵的,前端的事情还得前端来解决,没办法事情分配到我这里来了,一步步一分析的过程比较枯燥且乏味,不过别有一番乐趣,本文较长,将全程记录此次难题的处理。 接到这个需求,我看了看快手直播间的代码,webpack打包混淆后的js代码就是一座屎山啊,那特么是给机器看的啊。 一开始我是拒绝的,毕竟压缩加混淆的3W5千行代码的文件就有好多个,看一眼就想打人,咋分析。 但是,作为一名孱弱无力的弱小前端,再苦再难也要坚强,毕竟要洽饭的嘛。 分析下此次目的: 1.骗过快手服务器,连接到快手直播间的websocket,让它乖乖把消息推送到我们自己的服务器 2. 找到快手是采用何种方式对websocket传输的blob数据进行编码与解码 ## 先来看看快手的websocket ![](https://img.kancloud.cn/8b/00/8b00ccde7e8b7486246d958bd2190875_1711x790.png) 可以看到不停的闪烁,服务器再不停地往客户端推消息 ## 首先找到对应需要分享的js文件 既然是分析websocket那我们首先得知道在那座屎山里游泳,打开快手直播间,F12查看源码,搜索websocket关键字 ![](https://img.kancloud.cn/c9/e2/c9e29923324ca9619647429c5d642b52_752x912.png) 可以看到,有三个文件出现了websocket关键字。 1.app.js 2.common.js 3.vendor.js 从我们以往的经验可以知道 app.js应该是主要的逻辑,vendor.js应该是一些依赖库的打包,common.js不知道做什么的,但是从命名应该可以猜出是封装了共用的库。 然后websocket关键字出现最多地方就是common.js,通过本人的对每个文件的分析,果然如此common.js这个32623行混淆过的js就是我们要打的大boss。 ## 添加代理 将快手的js代理到本地来调试 这个时候我们打开这个common.js将压缩后的代码 展开 ![](https://img.kancloud.cn/15/2f/152fc545a0c6a9186af91896adac5329_771x782.png) 因为代码3W多行代码,我们发现无法编辑,除了打个断点 好像什么都不能做,我们此刻要分析就迫切需要编辑调试这个代码,所以这个时候我们需要用到一个利器charles > Charles其实是一款代理服务器,通过过将自己设置成系统(电脑或者浏览器)的网络访问代理服务器,然后截取请求和请求结果达到分析抓包的目的。 具体使用不再累述,检索下到处都是教程。 安装好charlses后 ,因为快手直播间采用的https协议,需要安装下证书,如下图 ![](https://img.kancloud.cn/12/aa/12aa807451bb17ed44462ef653ab3940_890x381.png) 点下就完事了,安装好证书,可以设置下只监听https的443端口 然后将快手的common.js下载到本地文件夹,在charsles中如下设置 ![](https://img.kancloud.cn/73/02/7302708e90933cb71efd0c24c34cd419_452x791.png) ![](https://img.kancloud.cn/11/98/119843e22db86b4443d63791a1515f1b_1191x498.png) 这个时候,我们刷新页面,即可以在本地就可以随意用vscode编辑调试线上的快手直播间的js啦。 ##猜测关键字找到关键的函数 这个时候通过一些猜测,比如从websocket关键字 一步一步往上推 是的 虽然混淆打包了,但是有很多的很多的关键字,需要耐着心去一步一步的看什么 t 函数 a函数 b函数,这期间的过程没什么可写的,但是这个过程是极其乏味与痛苦。 通过2天半的艰苦分析,总算将大致的关键字逻辑理顺了,所有的socket消息发出去通过以下方法 ![](https://img.kancloud.cn/4e/39/4e39678cd87de9ad27fa5e4eaeeef009_628x399.png) 我们将他console出来看看,找到了对应的关键字 ![](https://img.kancloud.cn/a7/83/a7837bff85752a89c76f3c067b991a77_726x130.png) 这里对应说以下,通过之前的分析,我们大概梳理了快手直播间的websocket大致流程: 1.任何连接 任何端都可以连接,但是连接后必须要在50秒内发送第一个包以建立连接,不然服务器会主动断开 2.每隔20秒发送一次心跳包 我们看到第一个包的数据如下: ``` . payload: . liveStreamId:"lG02idsTiKU" . pageId:"RFwi38MUiJQVqiUF\_1567150731045" 3token:"+bNZZ0V7XPjk5Eq7bHNxlGNPJUVkhBVKcmiE4cP+AkAxQaN5Fe17fqap9aCC9VNP97C3VboO2oIjjIgPOuI2qw==" . type:"CS\_ENTER\_ROOM" ``` 其中liveStreamId,pageId可以在页面seesion中找到,token可以在页面中找到。 嗯这里看起来好像就做完啦。但是我们拿到的这个数据是在3W5千行代码中未经编码前的数据, 快手在websocket中并不会以json的格式传输数据,而是采用二进制编码后数据传输。 我们在调试中将其输出编码后的 ![](https://img.kancloud.cn/e5/d0/e5d0c05d5b30fb4e0c0a8d083538a4cd_740x119.png) 可以看到,被编码成了arrayBuffer了,我们需要知道她如何编码的,我们才能自由的将对应的数据编码发送发哦快手的服务器,且之后收到了数据,也需要将对于的数据,还原成我们能看懂的json数据。 ## 解码与编码 事情到了这里 又卡住了 通过分析 ![](https://img.kancloud.cn/e4/20/e4203f849d4e8c707835bcda81a26b6e_547x388.png) 可以看到 调用了 _.encode _.decode 两个方法,我们猜测 就是使用_这个对象的方法,但是我们将他输出来,却发现如下: ![](https://img.kancloud.cn/43/f8/43f89cf941a3f90b83062bde774d6fa4_353x342.png) ![](https://img.kancloud.cn/ef/c4/efc4aa12b34c82ad972981cc20eccf90_289x272.png) 这个时候又是懵的,压根看不懂,为啥调用下这个方法就将对象编码成arrayBuffer了啊 再上推也找不,感觉线索断了(但是如果使用过谷歌protobuf 应该能从y.lookupType这个关键字得到启发瞬间明白使用了什么,但是当时压根没用过,所以自然看到这个会很敏感,但当时我没用过所以没让我敏感起来)。 接着继续往上反推 推到了这个方法, ``` $Writer.create() ``` 这个方法我当时不知道什么意思,但是这个函数必然是某个框架的构造create方法,所以我们需要知道 $Writer是什么框架的方法,于是整个百度谷歌了个遍,找到一篇文章 > [Angular2+ 使用 Protocol Buffers 和 websocket]([https://www.jianshu.com/p/cdb12cbde506?utm\_campaign=studygolang.com&utm\_medium=studygolang.com&utm\_source=studygolang.com](https://www.jianshu.com/p/cdb12cbde506?utm_campaign=studygolang.com&utm_medium=studygolang.com&utm_source=studygolang.com)) 其中作者在文末,将**Protocol.js 文件源码:**上传上了,然后被检索匹配到了,这个时候豁然开朗,原来就用的这个框架哦。 接着我们找到[protocol.js对应的github地址]([https://github.com/protobufjs/protobuf.js](https://github.com/protobufjs/protobuf.js)) 学习一边昨用这个框架 这个时候我们知道他用了什么框架去编码这个格式,所以我们在看看对应的代码即可,然后在起打包的文件中找到 类似于文件的map 的对象,根据这个对象去写对应的代码,此处不再累述。 简单建立链接的数据编码过程如下: ![](https://img.kancloud.cn/e0/25/e0257225fec359ca02adb9016ac1a3b8_866x883.png) ## 最终结果 我们在node中直接起个websocket服务,并对应我们之前折腾了很久的编码与解码,直接连接快手服务端,让它把消息数据乖乖推过来。 ![](https://img.kancloud.cn/24/1b/241b5b98ce6b058c3a734dda3a8e0172_1009x380.png) 解码后用node写入在data.json中数据: ![](https://img.kancloud.cn/78/3a/783a688c31d532fcc37ede00007b2bc6_877x877.png) 完美! ## 总结 限于文字的表现力,与自己的精力时间,不能把每一步都详细的写出来,实际做了大概3天,遇到的困难远比稳重简单的描述多 这次问题的总结的话就是: 1. 冷静分析 2. 耐心,耐心