智慧树逆向

目的:

  • 登录协议
  • 查询共享课程协议
  • 查询课程协议
  • 提交进度协议

初探

打开智慧树网站, 使用二维码登录, 选择共享课程, 开始学习

这是最基本的浏览路线

而我, 则需要顺着这条路线走下去, 把智慧树的协议解析出来

打开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里面有些什么?

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观看记录的接口

现在来总结以下目前获取到的信息

  1. 登录接口

  2. 获取共享课程接口 (chare course)

  3. 查询一门课程下所有章节(chapter), 小节(section), 小课(lesson)的接口 (videolist)

  4. 查询小课学习进度的接口(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部署

可以高度自定义