智慧树逆向
智慧树逆向
目的:
- 登录协议
- 查询共享课程协议
- 查询课程协议
- 提交进度协议
初探
打开智慧树网站, 使用二维码登录, 选择共享课程, 开始学习
这是最基本的浏览路线
而我, 则需要顺着这条路线走下去, 把智慧树的协议解析出来
打开devtool后可以发现, 智慧树网站是由vue 写的, 因此我们只需要找到各个协议对应的接口, 然后将其包装成脚本就可以了
可是, 协议不是那么容易就能搞到手的, 你需要了解网站上js脚本的行为, 然后在非浏览器环境下(也就是python环境)模拟, 有些网站对js进行混淆, 对数据进行加密.
接下来你就看我逆向智慧树的过程吧
本项目已经在github上开源https://github.com/notnotype/zhihuishu-cli
主要时间都花在编写代码上
登录协议
这里使用二维码登录, 不使用其他两种方法的原因是因为其他两种方法有验证码, 比较麻烦, 使用二维码相对而言比较简单
通过devtool可以发现几个线索
- 一个websocket
- 二维码接口
https://passport.zhihuishu.com/qrCodeLogin/getLoginQrImg
- 登录接口
https://passport.zhihuishu.com/login?pwd=
首先通过二维码接口获取二维码可二维码id
然后通过websocket连接来获取扫码状态, 如果扫码成功则返回一个临时密码
最后面使用临时密码登录.
访问登录接口之后会进行3次重定向, 每次重定向都带有cookies
因为这是一个微服务项目, 有三个服务:
- passport
- onlineservice
- studyservice
由子域名区分
例如https://passport.zhihuishu.com/qrCodeLogin/getLoginQrImg
属于passport
服务
每个服务都有自己的cookie, 所以要进行多次重定向设置cookies
登陆界面是passport
服务
用户面板是onlineservice
服务
学习界面是studyservice
服务
进行二维码登录之后跳转到了studyservice
服务
查询共享课程
登录studyservice
后通过这个接口获取
https://onlineservice.zhihuishu.com/student/course/share/queryShareCourseInfo
比较简单
查询课程协议
这个虽然比较简单获取, 但处理比较麻烦
有3个重要的接口
- videolist接口
https://studyservice.zhihuishu.com/learning/queryUserRecruitIdLastVideoId
- queryStuyInfo接口
https://studyservice.zhihuishu.com/learning/queryStuyInfo
- prelearningNote接口
https://studyservice.zhihuishu.com/learning/prelearningNote
这三个接口计划贯穿了studyservice
,几乎所有有关studyservice
都需要用到这三个接口
我们来看一下videolist里面有些什么?
几乎获取了这门课程所有课程的元数据(例如id, 名字, 学习状态等等数据), 也就是智慧树学习界面右侧的那一个列表
然后通过在video_list
中获取到的chapter_id
, 或者lesson_id
获得study_info
这里要说明一下course
, chapter
, section
, lesson
的关系了
如图, 这是一门course
下的列表
1.1代表了第一chapter
, 第一section
1.1.2代表了第一chapter
, 第一section
, 第二lesson
video_list
接口就是用来获取这些信息的
然后在通过在video_list
中获取到的chapter_id
, 或者lesson_id
获得study_info
study_info
是查询各个chapter
, lesson
观看记录的接口
现在来总结以下目前获取到的信息
-
登录接口
-
获取共享课程接口 (chare course)
-
查询一门课程下所有章节(chapter), 小节(section), 小课(lesson)的接口 (videolist)
-
查询小课学习进度的接口(prelearingnote)
有了这些接口我们就能做很多有意思的事情了
例如, 模拟学习, 也就是大家熟知的刷课啦
现在流行的刷课工具一般都是在浏览器层下手, 例如一些浏览器刷课插件之类的
而我现在则是在api(接口)层下手. 二者的好处不言而喻
基于浏览器的简单, 稳定
基于接口的效率高, 不需要依赖浏览器
这个不需要依赖浏览器是最大的一个优点
它不仅仅提供了可自定义性, 而且还提供了便捷性
使其可以在服务器端运行, 这也为后面的刷平时分打下了基础
学习协议
终于写到了最精彩的部分了
有了前面的接口作为铺垫, 我接下来就要介绍本文的重头戏啦 – 学习协议
这也是我们最关心的地方
你是不是现在很好奇你在智慧树学习的时候, 你的浏览器到底做了些什么事情呢? 你的学习进度是怎么提交到服务器上去的呢?
别急, 接下来听我娓娓道来
提交学习进度接口
首先我要想你介绍一个接口, 提交学习进度
,
https://studyservice.zhihuishu.com/learning/saveDatabaseIntervalTime
这就是这个接口的表单
watchPoint长这样, 没截全
0,1,2,2,5,5,8,8,9,9,10,10
首先watchPoint是什么不知道, 应该和播放进度有关
ev完全不知道
learningTokenId: 这一看就是一个base64编码(看到后面2个=
号了), 解码后是4982757276
可以在https://studyservice.zhihuishu.com/learning/prelearningNote^data.studiedLessonDto.id
拿到
uuid可以在登录时拿到
dateFormate不用说了, 就是当前时间片前13位
关键是这个watchPoint和这个ev
在console面板可以看到它的调试信息, 点进去发现豁然开朗
是使用vue+webpack开发的, 在vue组件的method声明处发现了一些重要的方法
这些都是有用的(这是屁话)
但是最重要的是这个方法
名字叫saveDatabaseIntervalTime
翻译过来叫做保存数据库间断的
每隔300秒保存进度到数据库
我看了一下, 要的参数还蛮多的, 各种奇奇怪怪的参数都要, 我这里就不一一列出来祸害大家的脑壳了
反正最后面就是通过看源代码很容易就把watchPoint算出来了
watchpoint就是每隔5秒就把总观看时间//5 + 2加在 "0,1"的后面, 然后就变成了 "0,1,2,2,3,3,4,4"的样子啦
关键的是这个ev参数, 它被加密了, 被这个this.D2666.Z
函数加密了
找不到这个加密函数
但是没有关系, 断点打法, 打个断点, 然后就能定位到函数的位置啦
大概长这个样子
OK啦
至此, 智慧树的逆向就完成啦, 还是很有收获滴, 更加深入的了解了webpack的原理, 还有浏览器devtool的使用
最后的ev参数时使用python的一个包pyexecjs
计算出来的
最后, 代码已经在github上开源https://github.com/notnotype/zhihuishu-cli欢迎围观
支持以上全部协议
-
获取共享课程
-
获取章节列表
-
获取小节列表
-
获取小课程列表
-
开始学习
-
使用mirai部署
可以高度自定义