前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >某习通小助手

某习通小助手

作者头像
愧怍
发布2023-10-21 13:17:16
5190
发布2023-10-21 13:17:16
举报

前言

声明,本文与该软件仅用于学习技术交流,请勿将软件用于非法途径,本作者不承担一切法律责任,望使用者须知。若影响到贵公司正常运行,请联系本人删除。

信息

注:本文适用于有编程基础的,会 POST 请求那更好,不会也不妨碍,多了解总没错。

使用语言

有人会很好奇这个软件到底是怎么运行的,为啥可以实现自动完成视频,作业。借此写个完整的该软件开发过程,供各位学习,整个开发过程真不算的难,听我慢慢道来(尽可能详细),但你看完后,写不出来,大概率也不会想写

既然是写软件,那怎么能不说说编程语言,首先这个软件是基于易语言开发的,初学易语言的三个月所写的练手项目,本是写来给我自用的,不过确实好用,那为啥不分享出去呢。

实际上我也考虑过开源,奈何多次审核不通过,累了,就懒得开源了。

首先,说说为啥会选择易语言,有一部分原因是因为我那时候正好在学易语言,哪怕现在如果要开发一个类似于这样的软件,我也会优选易语言(在不考虑兼容与报毒情况下)。原因其实非常简单,好写,太好写了,我记得那时候的第一版超星刷课只花了一周时间,实现了自动完成视频,那还只是我没什么开发经验的前提下(借鉴了外面的一份源码)。再者也是最主要的一部分,写的软件是基于什么平台,很显然,桌面级应用 Windows 平台。那就少不了交互界面了,而正是这个交互界面,让我劝退了 javascrpit 与 python。不是说他们不行,而是写起来绝对比易语言复杂。如果你有接触过这两者相关的估计会知道,尤其还是实现自动完成任务的功能,基本上是不提供界面而言。也就让代码的可操作性少了非常多,这还不是最致命的,致命的是使用者不是人人都学程序了,即使发你一个 python 文件,但他大概率是不会听你大费周章的安装,输入,原因就是麻烦(如果这份代码好用的话当我没说过)。

说这些都不如直接来一个 exe 可执行文件,让用户去点击操作,然后通过一个日志输出显示给用户,告知用户当前程序执行进度。可能有人又会问,那为啥不用 C#,VB.net,QT 等,我 tm 要是会的话,也不会用易语言来写了,易语言敲代码体验很差,如果用过其他的文本编辑器,就特别不想用易语言(反正我是这样,真的难用),毕竟易语言都是 20 年前的产物了,能活到现在就不错了。但不得不说,易语言是真的好写,好用,好上手。

语言不分贵贱,能写出好的程序都是好语言,所以本文都是以本人从易语言开发角度来讲述,如果你恰好有程序开发经验,或有想接触的,本文或许能给予你一些帮助。

找源码

既然介绍完所用语言,那么就开始编写代码吧,不过在此之前先别急,这一步尤为关键,能极大的节省你所开发的效率,那就是搜索是否的相关源码或者软件。最好是与自己所开发的语言一致。

这是我当时编写软件前在吾爱破解论坛上搜索到的相关源码,如下:

image-20200925173801271
image-20200925173801271

看看软件源码的界面

image-20200925173832002
image-20200925173832002

在比对一下我的修改了数十次的。

image-20201220072514909
image-20201220072514909

没错,我就是基于这个软件改的,还是有点相似之处的。但事实上这个原作者的代码在我翻阅到时就已经不能用了,并且还有很多弊端,例如还需要输入学校名称,输入验证码,这对用户体验来说的是非常烦人的。

同时在这份源码上只能说是一份临时品,几乎没有维护可言(虽然易语言写的软件多半都不好维护),不过有一个核心加密算法,也就是最终提交视频的一个核心算法,让我省去 JS 逆向分析的时间(后文会说到,不过以我那时候来看,这个 JS 自行解决也不成问题)

那时候搜索到的还有其他的相关脚本,例如大多数人都了解过的油猴插件。后文有简单讲述到,因为和本文涉及到的不相关。

执行流程

找到相关源码或软件,就已经离项目完成快了一半了,接着只需要在该软件上进行修改,已达到自己的目的。当然,如果要补充一些功能还需要花费很多时间的。

页面设计

我优先做的就是修改 UI 界面,做到竟可能的不丑,且符合个人风格。而这部分就是拖拽组件,移动组件,微调组件,平行垂直等操作,没啥可言的。我所用的都是 windows 自带的组件,加上我不会自绘组件,只好借助皮肤模块来美化界面了。美化的效果如下图

image-20201226062612827
image-20201226062612827

实际上,页面设计相关就到此结束了,我能做的也只是尽量不丑,毕竟不会自绘组件,用原生自带的组件就这样了。当然,后面关于怎么数据渲染到组件这些会写到的。

登录

接着就是要说实现原理,首先回想一下,我们如果手动去一个个看视频,答题,需要干嘛,那肯定是登录了,不登录学习通那边怎么知道是你,那么在浏览器中,登录只是输入下账号,密码,然后点击登录按钮就完事了。然而实际原理不只是点击按钮这么简单,实则是发送一个 http 请求给后端,后端进行效验结果比对,返回结果,我简单叙述一下,放 js 代码来看看:

image-20201226063607140
image-20201226063607140

具体看图片

image-20201226064003045
image-20201226064003045

完整关键代码如下:(已删除不必要代码)

代码语言:javascript
复制
//手机号+密码登录
function loginByPhoneAndPwd() {
  var phone = $('#phone').val().trim()
  var pwd = $('#pwd').val()
  var fid = $('#fid').val()
  var refer = $('#refer').val()
  if (util.isEmpty(phone)) {
    util.showMsg(true, 'phoneMsg', '请输入手机号', true)
    return
  }
  if (util.isEmpty(pwd)) {
    util.showMsg(true, 'pwdMsg', '请输入密码', true)
    return
  }
  var t = $('#t').val()
  if (t == 'true') {
    pwd = $.base64.btoa(pwd, 'UTF-8')
  }
  // --------------------------------------------------------
  $.ajax({
    url: '/fanyalogin',
    type: 'post',
    dataType: 'json',
    data: {
      fid: fid,
      uname: phone,
      password: pwd,
      refer: refer,
      t: t,
    },
    success: function (data) {
      if (data.status) {
        var url = ''
        if (data.tochaoxing) {
          var path = window.location.protocol + '//' + window.location.host
          url =
            path +
            '/towriteother?name=' +
            encodeURIComponent(data.name) +
            '&pwd=' +
            encodeURIComponent(data.pwd) +
            '&refer=' +
            data.url
        } else {
          url = decodeURIComponent(data.url)
        }

        if (top.location != self.location && $('#_blank').val() == '1') {
          top.location = url
        } else {
          window.location = url
        }
      } else {
        if (data.weakpwd) {
          window.location =
            '/v11/updateweakpwd?uid=' +
            data.uid +
            '&oldpwd=' +
            encodeURIComponent($('#pwd').val()) +
            '&refer=' +
            refer
        } else {
          var msg = util.isEmpty(data.msg2) ? '登录失败' : data.msg2
          msg =
            '密码错误' == msg || '用户名或密码错误' == msg
              ? '手机号或密码错误'
              : msg
          util.showMsg(true, 'err-txt', msg)
        }
      }
    },
  })
}

代码并不长,一个很简单的 post 登录,这里我会一一进行分析

代码语言:javascript
复制
var phone = $('#phone').val().trim()
var pwd = $('#pwd').val()
var fid = $('#fid').val()
var refer = $('#refer').val()
if (util.isEmpty(phone)) {
  util.showMsg(true, 'phoneMsg', '请输入手机号', true)
  return
}
if (util.isEmpty(pwd)) {
  util.showMsg(true, 'pwdMsg', '请输入密码', true)
  return
}
var t = $('#t').val()
if (t == 'true') {
  pwd = $.base64.btoa(pwd, 'UTF-8')
}

在分割符的前一部分,获取我们表单中的手机号(phone),密码(pwd),学校 id(fid),以及不重要的 refer,同时判断手机号,密码是否为空,并给出相应提示,同时这里的 pwd 还进行了 base64.btoa,也就是 Base64 编码处理过。这里我就模拟一下这些数据

代码语言:javascript
复制
phone = '15212345678'
pwd = 'a123456'
pwd = 'YTEyMzQ1Ng==' // Base64编码后的结果
fid = '12345' // 也可以不指定学校 填-1
refer = 'http://passport2.chaoxing.com'

然后再看剩余的一部分,主要就关注这些:

代码语言:javascript
复制
$.ajax({
  url: '/fanyalogin',
  type: 'post',
  dataType: 'json',
  data: {
    fid: fid,
    uname: phone,
    password: pwd,
    refer: refer,
    t: t,
  },
  success: function (data) {
    //....
  }
}

也正是因为这几行代码,将我们的数据发送给了学习通的服务端,并将数据返回给我们,这里我抓个数据包看看数据是怎么样的

代码语言:javascript
复制
POST /fanyalogin HTTP/1.1
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded; Charset=UTF-8
Accept: */*
Referer: https://passport2.chaoxing.com/login?&newversion=true
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Origin: https://passport2.chaoxing.com
x-requested-with: XMLHttpRequest
Host: passport2.chaoxing.com
Content-Length: 94

fid=12345&uname=15212345678&password=YTEyMzQ1Ng==&refer=http://passport2.chaoxing.com&t=true

认真看上面最后一行,有没有发现这些数据不就是我们刚刚上面模拟的数据。再来看返回数据

代码语言:javascript
复制
HTTP/1.1 200 OK
Server: Tengine
Date: Fri, 25 Sep 2020 11:50:30 GMT
Content-Type: text/html;charset=utf-8
Connection: keep-alive
Set-Cookie: JSESSIONID=625EBEBB8C9A307910975B8A6306EE13; Path=/; HttpOnly
Set-Cookie: lv=3; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/
Set-Cookie: uf=b2d2c93beefa90dcc0dd308bdb4e3ac7c15d612bf3f08318fdb57793f3e0b0e8e06d6354b86e5f8c6733e63a87a57410913b662843f1f4ad6d92e371d7fdf644cb407fe2f4a1b7e3102289339c6dea121471850d8bf7e34cbde8ab62ef4efbfc29d661c57520821b; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/
Set-Cookie: _d=1601034630583; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/
Set-Cookie: vc=D117659BD1295E4489AED8ED14E8A8D8; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/; HttpOnly
Set-Cookie: vc2=5B73047EF8C636D19D282B878FC42D4A; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/; HttpOnly
Set-Cookie: vc3=Lptknj6gO2EVnnAWOW0A1O0d0RGWzgO1jVDtKiGtxlqX7dH5uAz84KoWDf2Y9v%2Biw2V3RyKd2gNXf%2BMVKt2HKmJzYK1vt%2BBHu%2B%2BXwG3NtJAWvXygxxcRYlSwCt%2BDv0r8JkrhqgJxJQV2VkMVMon8PABIuJdJKudVTQR%2FP6u2pfY%3D2dd5ab18abc4e7b47fdeb363a13a7c64; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/; HttpOnly
Set-Cookie: xxtenc=0fd26095d768e519a53edd2f4ba4c9e9; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/
Set-Cookie: DSSTASH_LOG=C_38-UN_9502-US_42736002-T_1601034630584; Domain=.chaoxing.com; Expires=Sun, 25-Oct-2020 11:50:30 GMT; Path=/
Set-Cookie: route=1b37a788fe3a8c39de935217be0d9f7a;Path=/
Content-Length: 56

{"url":"http%3A%2F%2Fi.mooc.chaoxing.com","status":true}

现在只要注意最后一行,这是登录成功的返回结果,那如果密码错误呢,返回的结果如下

代码语言:javascript
复制
{ "msg2": "用户名或密码错误", "status": false }

那么既然简单了明白了一下基本的登录实现原理,能不能模拟一下这样的请求,替换一下其他人的账号密码,然后发送给服务端,当然可以,看看用易语言代码是怎么实现的这样的登录功能。

注意我划线的两个部分,实际上在 POST 请求中,要做的也就是替换一系列的数据,已达到模拟请求,并接受服务端接收的数据。

不过这里我还得小提一句,看到上面的返回响应中,有好几个 Set-Cookie,这里 Cookie 是服务端返回的,并且加密处理了,而正是这个 Cookie,保存了我们的登录凭证,使得下次我们请求的时候会将这些 Cookie 携带上去,这样服务端才能知道哪个请求是谁发送的。这样才能获取到哪个学生的信息,如课程信息,作业信息等等。

凭什么我发送一个请求给服务端,然后就能登录成功,就可以不用借用浏览器,来实现登录账号?或者说本质上,浏览器数据交互也是通过 HTTP 协议,而 HTTP 协议就是这样,至于实现原理,这里我不涉及太多相关的,你只需要知道可以就行,但有时候并不像上面这么简单(请接着看下文例子)。

从上面的例子中,也许你已经能知道,只要发送一个请求的事情,就可以登录,就能获取对应的信息,实际上,你只要知道下面这一句话:

浏览器本质的操作,就是向服务器发送请求,而软件所做的就是模拟请求,已达到获取数据。

而操作流程,先抓包获取到发送的数据,然后再模拟数据发送给服务端,就可以达到相对应的操作后文都将以这样的操作为例,以至于实现原理,就涉及到相关专业的知识了,同时模拟请求,能绕过浏览器自身的限制(JS 的限制).

获取课程列表

这里我就以获取课程列表数据,并通过 DOM 解析,并将其显示在软件上来演示。

现在已经登录成功了,那么我现在就向服务器发送获取课程信息的请求,在这里我封装成一个易语言的函数,如下几个部分:

代码语言:javascript
复制
url = “http://mooc1-1.chaoxing.com/visit/courses/study?isAjax=true&debug=false”
http.Open (“GET”, url)
http.Send ()
response = http.GetResponseTextU2A ()

此时 response 为 html 文件文本,像这样的

代码语言:javascript
复制
<ul class="clearfix">

 	<li style="position:relative;" class="courseItem curFile">
		<input type="hidden" name="courseId" value="215497310" />
		<input type="hidden" name="classId" value="34423626" />

		<div class="Mcon1img httpsClass" >
			<a     href='/visit/stucoursemiddle?courseid=215497310&clazzid=34423626&vc=1&cpi=166457817'  target="_blank"   >
				<img src= https://p.ananas.chaoxing.com/star3/270_160c/56d94e43e4b0dfadae7a3437.jpg    onerror="nofind(event)" imagedata='https://p.ananas.chaoxing.com/star3/270_160/56d94e43e4b0dfadae7a3437.jpg' width="270" height="169" />
								</a>
						<a href="javascript:void(0)" class="move"  style="right: 0px;"  title="移动到">
				移动到			</a>
		</div>
		<div class="Mconright httpsClass">
			 			<h3 class="clearfix" >
				<a class="courseName"  href='/visit/stucoursemiddle?courseid=215497310&clazzid=34423626&vc=1&cpi=166457817' target="_blank" title="创业基础">创业基础</a>
			<i> </i>
			</h3>
										<p title="王艳茹">王艳茹  </p>
				<p title="中国青年政治学院">中国青年政治学院  </p>
				<p title="默认班级">默认班级</p>
					    		   <p class="">课程时间:2020年11月12日-2021年01月10日</p>
									<p class="Mconrightp3" style="display:none;">
			</p>
		</div>
	</li>
   <li class="addLi">
	<div class="">
		<a href=http://www.fanya.chaoxing.com/schoolcourse/zixuan2?fid=11231   target="_top"  class="Mdelc2dt" style="height: 202px;padding-top: 76px;" title="添加课程"><span></span></a>
	</div>
 </li>
 </ul>

可以看到课程信息以及相关链接,接下来要做的就是根据 DOM 对象,提取这些课程数据,下为我那时候解析的代码:

image-20210102035545981
image-20210102035545981

根据 CSS 类名 courseItem,获取课程单元块以及课程数,然后通过遍历 courseItemList,同时通过 CSS 选择器选择到对应的 HTML 标签,获取到我们想要的数据,通过一个自定义数据类型(这里非对象),将其存在课程列表数组内,最后将这些数据通过超级列表框设置到页面上。也就是如下图这样

image-20210102035927028
image-20210102035927028

同样的获取章节列表,作业列表,考试列表,甚至是一些评论列表,也都是通过 DOM 解析,获取其数据,存储到数组内,然后根据章节名或者 id 来获取数组成员,已达到指定课程完成任务。

开始刷课(重点)

如果只是获取数据那怎么能够,而刷课才是软件的主要目的,首先要刷课,就必须要指定课程,这里指定课程也就是 列表框中选中即可,此时点击开始刷课便能开始任务,这里来看看刷课的代码

image-20210102040540378
image-20210102040540378

就是判断用户有无登录,有无任务在执行,有无选课,然后将配置写入到配置文件,方便下次打开还是上次配置,同时设置模式,是要完成那一部分任务,最后将按钮设置为禁止,不可点击(所以为啥我不一开始就直接禁止开始刷课为假呢),最后启动一个线程来执行,在主线程执行会导致窗口卡顿等现象。然后下面才是正在的执行逻辑了。

获取章节列表
image-20210102041501299
image-20210102041501299

添加了注释,就懒得在打字一个个说明了,执行逻辑并不难,也就是判断,然后执行,接着要到刷视频和题这个方法,因为最主要还是这个代码在干什么。(后面也会将写上代码注释,方便大家理解)

开始刷视频和题
image-20210102044334946
image-20210102044334946

开始循环访问选择夹,接下来代码有点多,执行流程也就是循环,判断,我简化了很多,认真看

image-20210102050722756
image-20210102050722756

这里我要提一下,上面的 mArg 数据是什么,是一段 JSON 数据文本,长下面这样

代码语言:javascript
复制
{
  "attachments": [
    {
      "headOffset": 663000,
      "jobid": "1596706035431101",
      "otherInfo": "nodeId_349140314-cpi_159793445",
      "isPassed": true,
      "property": {
        "jobid": "1596706035431101",
        "switchwindow": "true",
        "size": 443706433,
        "fastforward": "true",
        "hsize": "423.15 MB",
        "module": "insertvideo",
        "name": "12.13.mp4",
        "mid": "8562913227181596706035139",
        "type": ".mp4",
        "doublespeed": 1,
        "objectid": "902ca19c673c7fa256702b6211c9df07",
        "_jobid": "1596706035431101"
      },
      "mid": "8562913227181596706035139",
      "playTime": 663000,
      "type": "video",
      "aid": 600168224,
      "objectId": "902ca19c673c7fa256702b6211c9df07"
    }
  ],
  "defaults": {
    "fid": "1617",
    "ktoken": "ac0308fc1f7a84019740f1bebfd0b733",
    "mtEnc": "a70ce1e7dac8d0d2e6082e2a95d002a3",
    "isFiled": 0,
    "ignoreVideoCtrl": 0,
    "reportUrl": "https://mooc1-2.chaoxing.com/multimedia/log/a/159793445",
    "chapterCapture": 0,
    "userid": "157041903",
    "reportTimeInterval": 60,
    "initdataUrl": "https://mooc1-2.chaoxing.com/richvideo/initdatawithviewer",
    "knowledgeid": 349140314,
    "schooldoublespeed": 0,
    "qnenc": "b2a727eed024085321062c005680e1ef",
    "defenc": "bca5e389669154f0fd1fb0208b2ad655",
    "clazzId": 34189060,
    "cardid": 311180449,
    "imageUrl": "https://p.ananas.chaoxing.com/star3/270_169c/f01bc30632e023f83b3e8879cdeea2c7.jpg",
    "lastmodifytime": 1609462354000,
    "state": 0,
    "courseid": 215403857,
    "cpi": 159793445,
    "subtitleUrl": "https://mooc1-2.chaoxing.com/richvideo/subtitle"
  },
  "control": true
}

通过 JSON 解析工具,可以获取到章节下的课件内容信息。比如视频时长,视频通过状态,视频 id,等等

image-20210102074018606
image-20210102074018606

取到我们想要的数据,并存为变量即可,接着才是关键所在,获取到了课件信息,同时判断课件类型为视频,并且视频的通过状态为 false,那么接下来就是要提交视频了。相关代码如下

提交视频
image-20210102052457877
image-20210102052457877

其中这里提交视频就一个请求,也就是这个请求,服务端才知道你视频看了多少,并且将你的观看时长记录到数据库中,最终拼接的 url 比如这样的 https://mooc1-2.chaoxing.com/multimedia/log/a/159793445/66ee5f706ccc9e58ab0ea383a83e665c?clazzId=34189060&playingTime=935&duration=935&clipTime=0_935&objectId=34ad66ae9778a00c6bfde810f12431ac&otherInfo=nodeId_349140316-cpi_159793445&jobid=1595816983931186&userid=257041903&isdrag=4&view=pc&enc=f26c618c0e0af1147fe2f4ce7b5e8f95&rt=0.9&dtype=Video&_t=160954150532

当然这个请求没这么好伪装出来,你在上面这几行就可以看到这些参数的复杂了,并且还有相关的加密,如果你随便发送一个请求,服务器鸟都不会鸟你的。伪装还算简单,照葫芦画瓢就完事了,而加密你就需要了解对应的加密算法和 JS 逆向了。这里如果我改掉其中一个必要的请求,那么将会出现如下界面

代码语言:javascript
复制
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>403</title>
<style>
.main{height:255px;margin:0 auto;margin-top:15%;font-size:14px;color:#999; width: 350px;}
.font_top{padding-top:45px;display:inline;}
</style>
</head>
<body>
<div class="main">
<p class="font_top">>_< 很抱歉,您没有权限访问这个页面!  (403)</p>
<p style="font-size: 12px; color: #ddd;">112.48.28.255<br/>mooc-2166199849-8f1c1</p>
</div>
</body>
</html>

所以,POST 请求最主要的之一,就是拼接参数,去模拟请求,在说到这里的加密 enc,先看看原文本长啥样

代码语言:javascript
复制
[34189060][157041903][1595816983931186][34ad66ae9778a00c6bfde810f12431ac][935000][d_yHJ!$pdA~5][935000][0_935]

将上面这段数据通过MD5加密即可获取enc为
f26c618c0e0af1147fe2f4ce7b5e8f95

通过易语言拼接如下
web参.enc = 取数据摘要 (到字节集 (“[” + web参.classId + “]” + “[” + web参.userid + “]” + “[” + web参.jobid + “]” + “[” + web参.objectId + “]” + “[” + 到文本 (web参.playingTime × 1000) + “]” + “[d_yHJ!$pdA~5]” + “[” + web参.duration + “000]” + “[0_” + web参.duration + “]”))

至于我怎么知道加密点的,就涉及到 JS 逆向,就需要看网页内的 JS 文件,这里就不在赘述了。

然后我们每提交一次视频的请求,只要将 playingTime 播放时长,改为视频时长,就能实现秒刷,原理就是通过协议发送请求。但也有问题所在,我提交一次,服务端记录一次,并且间隔为 1 分钟,我 1 分钟内在提交一次视频完成的请求都将不会记录时长,所以这里的秒刷也只是将视频章节内的视频秒刷了,实际上要挂时长,还得每隔一分钟提交一次才行。(学习通是这样的)

顺便抓个提交测验的请求的包,看看模拟请求有多烦躁。

代码语言:javascript
复制
// url (请求地址)
https://mooc1-2.chaoxing.com/work/addStudentWorkNewWeb?_classId=34189060&courseid=215403857&token=5219a574b7571639ea6b9770bbc23da2&totalQuestionNum=cd8c29480d9c16f5a675e8c2c459245c&ua=pc&formType=post&saveStatus=1&pos=841bf6c56a6d8179e801f5743b&rd=0.5328632906746906&value=(285|546)&wid=11020928&_edt=1609545107317285&version=1

// post的data数据(拼接题号,选项,课程id,班级id,测验id等等......)
pyFlag=&courseId=215403857&classId=34189060&api=1&workAnswerId=50407707&totalQuestionNum=cd8c29480d9c16f5a675e8c2c459245c&fullScore=100.0&knowledgeid=349140316&oldSchoolId=&oldWorkId=62df20e345ff464d9bbe4ea021025cc5&jobid=work-62df20e345ff464d9bbe4ea021025cc5&workRelationId=11020928&enc=&enc_work=5219a574b7571639ea6b9770bbc23da2&userId=157041903&answercheck209790637=A&answercheck209790637=B&answer209790637=AB&answertype209790637=1&answer209790638=false&answertype209790638=3&answer209790639=true&answertype209790639=3&answerwqbid=209790637%2C209790638%2C209790639%2C

// 最后提交请求的响应文本(根据status 是否为true来判断是否提交成功,msg为返回结果文本)
{"msg":"success!","stuStatus":4,"backUrl":"","url":"/api/work?courseid=215403857&workId=ca62b882b279427b9d24876daba4e2ba&clazzId=34189060&knowledgeid=349140316&ut=s&type=&submit=true&jobid=work-62df20e345ff464d9bbe4ea021025cc5&enc=061df255135b657c3aef35c5afc711c4&ktoken=c09603936670a079289c0d1488ab0f63","status":true}

想这样的自动完成任务软件(协议),要做的就是抓包(截包),分析数据,模拟数据,然后在通过代码方式生成出来,最终提交给服务器。

通过这样的流程,就能实现自动刷视频,类似的章节测验与考试无非也就是判断,然后执行发送请求。对这个软件而言,就封装了好几个方法了

image-20210102073628331
image-20210102073628331

而这还只是超星提交的操作,软件界面相关的我就不多演示了。总之,整体执行的流程就是像上面那样,软件怎么编写,就看开发人员了。

关于协议与脚本

上面说了一大堆,相信还是有大多数人迷迷糊糊的,正常,不过听不听得懂无所谓,了解即可,这里我需要说一下,协议与脚本的一些区别,实际上,简单比较下也能很明显的感受到两者的区别,甚至可以说,这两者的本质毫不相干。

  • 脚本

需要依托在宿主上(浏览器),不然也无法执行代码(js 或填表),来实现点击提交操作。脚本要做的,也就是将人手动操作的,通过自动化方式来操作。

  • 协议

则是基于 HTTP,只需要发送请求,就可以做到脱机(浏览器),以达到高效执行,而这是脚本做不到的,同时编写难度也是高于脚本。

发送完成视频的请求,就能绕过浏览器内置的拖拽视频进度条,倍数等限制,而浏览器本身也是基于协议来实现的,也就是将这些提交请求的代码(JS)放在浏览器客户端,然后判断执行。而你的所有操作最终都将通过 HTTP 请求来发送,来达到数据交互的目的。

实际上协议能做的远非如此,我这里简单举几个例子吧,例如抢购,机器人,等等,而这些用脚本都是执行不了,要么执行效率贼差。

实际上在一开始写这款软件时(学易语言和 JS 三个月左右),我是知道脚本与协议的区别,并且一开始想使用脚本来写,奈何,那时候的技术,只停留在使用按键精灵或大漠插件,来实现 PC 端操作,而要操作浏览器内置 DOM 元素,则需要网页填表以及一些前端基础,也正是因为不会这些相关的,但又想写个练手项目,于是就选择使用协议去完成,并且最终成功写成。

现在想想,也正应该感谢这款软件的开发经历,对我技术提升以及后续的学习兴趣至关重要。可以说没有这个软件给我带来的成就感,也许就不会有这篇文章了。(貌似有点提早感慨了)

关于风险

这也是很多人可能会担心的,毕竟软件自动执行嘛,我咋知道安全不安全,会不会导致我账号异常等等。不过这实际上要看情况的。

就先这么说,你在 10 秒内,同时请求了 20 条视频,而后端是知道你请求了 20 条视频,因为你获取了后端的资源,然后后端一看,这丫的不对劲啊,手速这么快,有可能吗?后端不会觉得你手速快,而只会觉得,你像上面代码那样来短时间内批量提交视频的。所以就会给记录异常

而且这也要看平台的

  • 学习通

学习通目前提交过快,就会出现验证码,并未有使账号异常冻结等操作。

  • 职教云

视频提交间隔要在 5 秒以上(我目前测试情况下是这样),课件等无限制。

  • 其他平台(没具体了解过,目前手头就写了这两个)

那如果是正常速度提交,尽可能的模拟人为的操作呢,那也未必就没风险,只要他们后端想,修改一下接口,就能知道你操作是否异常了,比如我更改了加密算法,导致了你提交请求中和服务端效验失败,那我就可以认为你是通过外来手段篡改了请求数据,也就非法操作,就认为你不是非人为(非浏览器)操作。

然而这种情况很少,一般来说,网站部署运行了,除非特别大的改动,基本上是不会频繁的更换源码,需要不断测试。基本上也就是看平台了

我一开始只写了刷视频的,后面对接了题库,就开始写刷题的,然后有验证码就开始过验证码,最终还为软件添加一个网络验证,这每一步的过程都是深夜在电脑前,望着别人完全看不懂的代码,想,改,不过我庆幸我学了这些技术,因为它确实让我目前能写很多能用的项目。从这个超星学习通助手还学到的其他技术,下面一一列举

一些相关技术

完美验证系统

这个是用于验证码识别的,我当初为了解决超星的登录与提交题目时出现的验证码,就必须要识别出该验证码,于是我找到了完美验证码系统,我先放一张图

首先说下识别的实现原理:获取到验证码的图片,比如下面这张

ABCWF
ABCWF

那么我先把干扰线去掉,并且二值化处理一下,变成下面这样

image-20200924232042428
image-20200924232042428

然后这时候开始抠图,抠出 A,B,C,W,F 字符,比如抠出 A 字符

image-20200924232357263
image-20200924232357263

接着,做几百张这样的抠图图片,如

demo4
demo4

然后交给系统识别就行了,他会比对你做的字模,然后进行图片相似度比对,最终将识别结果返回给你,看似很简单,的确也很简单,但是我扣这些图,扣了一周,最终识别效果也就只有百分之 50 这样,真的吐了。但是没办法,那时候学的浅,哪里还知道深度学习和 ocr 的识别,就这样坚持硬着头皮扣了一周,然后将这些字模全部导出置识别库用于调用。最终整体识别效果,如下图

image-20200924233820001
image-20200924233820001

甚至还看了一个图象识别的教程手写一个类似这样的识别系统,最终效果如下图。

demo5
demo5

我要是当时会深度识别和调用 OCR,我用的着这么麻烦吗,这个是真的浪费我太多没用的时间。

自写网络验证

作为软件开发商,肯定不希望自己的辛辛苦苦写的软件,给别人一破解,修改了版权,并借此牟利,于是就不得不对软件进行一个操作,一般来说外面都有专门的对软件进行保护的厂商,但要钱的嘛,与其如此不如自己写一个,虽然防御上面可能没别人好,但至少一些破解小白肯定没那么轻松搞定。

一般的网络验证,你会看到如下界面

image-20200926161606528
image-20200926161606528

你需要注册一个账号使用,如果时间到期了就需要充值卡密使用,但你如果不注册的话,你就无法使用这个软件,能有效的防止破解者,提高破解门槛(该破解都能破解,就看想不想了),这是一个客户端,对应的肯定有服务端

image-20200926161958435
image-20200926161958435

这里只记录了一些用的上的,实际上可以记录更多,只是我懒得再记录了,通过这个网络验证,可以有效的防止软件被篡改,同时也能利用这个来获取用户引流等等,至于网络验证相关代码就不提及了。

实际上我防破解意识很浅,原因也很简单,这个软件都不收费,别人闲着没事破解啥,原本是加了网络验证,但是由于太麻烦,我就懒得加了。

实际上超星这个软件从头到尾就没主动收取用户费用。那时候也是本着写来自用,并未想过去接单,帮别人啥的,对我来说没必要。软件写来不就是分享给更多的用户吗,话说的绝对,并不是所有开发者都有时间和精力去免费维护一个软件,我之所以能免费分享,主要还是我学校正好就是超星学习通的,加上也是我的练手项目,于是不分享白不分享,到时候一些其他相关软件的合作者也能找我(已经在合作了),所以这个软件对我来说有必要收费吗,反正我是没打算过。但后续平台发展了,就不得不停了该软件,没办法,为了平台而着想。

浏览器插件

我在搜索的超星刷课源码的同时也搜索到一个浏览器插件,油猴插件。相信学校是超星的肯定被同学安利过这样的一款插件,在这个插件你能看到很多脚本,其中你一搜就能搜到有关超星学习通和其他的,并且免费使用,也就是这个原因,我就没打算在我自写的超星学习通助手上进行收费。但实际上它们只是脚本,我当初准备写超星刷课的时候一开始是想写网页自动操作脚本的,但是随便一搜就有了相关的,并且别人的还有一些打码收费什么的,于是就放弃用脚本,而是通过 POST 请求(其实就是我那时候压根就不会浏览器的脚本,搜了一些网页填表的没学明白,而 js 那时候只会分析算法,ES6 语法都不会,于是就选择了协议),然后就有了上面的超星学习通助手,在疫情期间深入学习 web 方面就接触到了两个浏览器脚本,一个是 Puppeteer 与 selenium,另一个是 Chrome 插件开发,而这里的油猴插件就是基于 Chrome 插件开发出来的。

但我没学过油猴插件,而是直接学了 Chrome 插件开发,有关 Chrome 插件开发可以看我写过一篇 Chrome 插件开发的文章,这里的话我就放一个网络上开源的插件 超星慕课小工具 毕竟我的调用题库接口还是基于这个插件的。(现转储为我自己的服务器)

总结

作为作者,能看到更多的人使用自己所写的软件,非常欣慰,估计认识我的一部分也是因为这个软件原因,由于要搞平台,所以就要停滞该项目了。

在一开始编写时,都未曾想到能写的出来,并且优化成这样,在经历了这段编写过程,让我感受的编程的强大,也正是如此,让我会去尝试新鲜的技术,去编写新鲜的项目。不过还要说一些相关的问题

说说这软件一些问题

  • 报毒 :易语言的自身的原因
  • 无法做到适配:有的 win7 系统是获取不到课程,别问,问就是易语言。
  • 无法热更新:每次更新都需要在对应的下载链接下载,到现在我还不会热更新,哭了哭了。
  • 题库不全:有概率是搜不到题目的,所以提供了随机选项。

从写这个软件开始,我能感受到易语言带来的便携,但也看到了易语言的不足之处。以至于我曾最喜欢的编程语言,也渐渐的开始放弃。(补: 我已不再从事易语言开发)

不断更新,维护

没有软件一上来就是完美无瑕的,都是经过多次的修改,更新,最终展现给用户,这个也不例外,从我 去年 10 月 10 号开始一直到现在,中间陆陆续续修改了几十遍,从 1.0 版本更新到 4.1.0(最终版,已不再更新)

感慨

因为这款软件,因为易语言让我感觉到编程是多么无敌的感觉,算是我目前为止还能拿的出手的软件之一,真的完全可以说,没有易语言,我也写不出来这样软件,更别说接下来的学习了。成就感与自信心油然而生,随后的学习更是得心应手。

网络上也很少有类似这种文章,道理想必都懂,最后还是要说下

本文仅作为技术交流,希望更多的人利用技术去方便自己,而不是利用技术从事违规行为

请勿利用本文相关技术与软件从事违法行为,否则后果自负!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-1-2,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言​
  • 使用语言​
  • 找源码​
  • 执行流程​
    • 页面设计​
      • 登录​
        • 获取课程列表​
          • 开始刷课(重点)​
            • 获取章节列表​
            • 开始刷视频和题​
            • 提交视频​
        • 关于协议与脚本​
        • 关于风险​
        • 一些相关技术​
          • 完美验证系统​
            • 自写网络验证​
              • 浏览器插件​
              • 总结​
                • 说说这软件一些问题​
                  • 不断更新,维护​
                    • 感慨​
                    相关产品与服务
                    验证码
                    腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档