破解希沃立知课堂——查看课件篇

声明

这个程序只是用来演示查看后台和对接后台的原理和过程,供代码学习参考,请勿滥用!

起因

因为上海疫情,我们所有的活动都得在线上完成,作为一个新时代好学生,我该做点什么了。
我的学校选择的上课平台是希沃的立知课堂,教师端是希沃白板电脑客户端,学生端是 网页端 、微信小程序端、移动设备软件端。

观察后台

一看到网页端,就说明可以用F12查找后台,那么我破解前期,就做了

观察
这一个工作。
用了大概两天时间,找到了主要的两个后台:

 /*
后文将此API称为:Api I
这个后台被官方用来获取课件略缩图,因为可以调整分辨率,所以就能用来浏览课件。
这个后台无需Cookies
需要通过GET方式传递数据, 等于号后的是实例数据:
1. 课件ID: coursewareId=xxxx-xxxx-xxxx-xxxxxxx
2. 课件版本: version=19
3. 返回图片的分辨率: resolution=960_640
以上所有字段必须需传递
*/
GetCoursewareApi = "https://s2.imlizhi.com/slive/pc/enow/thumbnail/api/v1/courseware";
 /*
后文将此API称为:Api II
这个后台被官方用来获取课件列表和所需数据,为了能准确获取课件ID等信息,需要用到这个后台。
这个后台需要立知的Cookies
需要通过POST方式传递数据, 包含实例数据:
1. 教室的Uid '{"courseUid":"a57ee0d4f7fc4e39b0e95e3d53de2574"}'
所有字段必须需传递
*/
var ServerTime = new Date().getTime();
GetCoursewareListApi = "https://s2.imlizhi.com/slive/pc/apis.json?actionName=GET_COURSE_ACCESS_CODE_LIST&t=" + ServerTime;
/*或者:*/
GetCoursewareListApi = "https://s.imlizhi.com/slive/pc/apis.json?actionName=GET_COURSE_ACCESS_CODE_LIST&t=" + ServerTime;


那怎么实现呢?

选择平台

因为后台需要使用Cookies,所以很难实现桌面客户端。
原先想到浏览器扩展,但扩展只能用于同内核浏览器,且需要调用浏览器API接口,兼容性不行还编起来难。
我选择了以下两种方案:

  1. Tampormonkey 油猴脚本
  2. 直接在浏览器书签栏加入带有JavaScript脚本的书签,编起来简单,也能获取网站一切内容的访问权限,同时AJAX跨域的问题也能解决。
    为了方便给读者看到代码实现原理,我选择了后者。
    (*注:为后者编写的代码可以快速地移植到前者,不需要改动代码)

对接API

因为对接的后台处于网页教室的同目录或子目录,所以我使用了相对路径。

先对接Api II

我使用了原生AJAX代码进行POST数据传递:

获取Uid

获取courseUid的方法很简单,下面是网页端教室链接:

https://s2.imlizhi.com/slive/pc/room?courseUid=534617396df542e0a09fd01f80e1ef2a&appCode=en-easilive

只需要用split,拿到courseUid即可。

 var uid = window.location.href.split("?")[1].split("&")[0].split("=")[1]; 

获取ServerTime

通过观察,可以发现传递的

t
对应以下代码:

 var ServerTime = new Date().getTime(); 

课件列表实现的完整代码

 function GetList(){
var uid = window.location.href.split("?")[1].split("&")[0].split("=")[1];
var ServerTime = new Date().getTime();

var xhttp = null;
if(window.XMLHttpRequest){xhttp = new XMLHttpRequest();}
else if(window.ActiveXObject){xhttp = new ActiveXObject('Microsoft.XMLHTTP');}
/*浏览器兼容性*/

xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var jstr = eval("(" + this.responseText + ")");
if(jstr["error_code"] == 0){
var OPStr = "";
for(var i = 0; i <= 10; i ++) {
try{ OPStr += (i + 1) + ". " + jstr.data[i].name + "\n"; } catch(e){}
}

let k = prompt("请选择需要打开的课件(填数字编号)\n" + OPStr, "1");
//GetCourseware(jstr.data[k - 1].cid, jstr.data[k - 1].version);
} else {
alert("课程未开始或课程已结束!");
}
}
};
xhttp.open("POST", "./apis.json?actionName=GET_COURSE_ACCESS_CODE_LIST&t=" + ServerTime, true);
xhttp.setRequestHeader('Content-Type','application/json');
xhttp.send('{"courseUid":"' + uid + '"}');
}

再对接Api I

 function GetCourseware(cid, ver){
var xhttp = null;
if(window.XMLHttpRequest){xhttp = new XMLHttpRequest();}
else if(window.ActiveXObject){xhttp = new ActiveXObject('Microsoft.XMLHTTP');}

xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//ShowCourseware(this.responseText);
}
};
xhttp.open("GET", "./enow/thumbnail/api/v1/courseware?coursewareId=" + cid + "&version=" + ver + "&resolution=960_640", true);
xhttp.send();
}

最后将获取的课件图片列表输出到浏览器新标签页

传入的filelist是字符串类型,不是JSON

 function ShowCourseware(filelist){
let dochtml = "";
let MaxPageNum = 200;
let PageCnt = 0;
let result = filelist;
let obj = [];
filelist = eval("("+result+")");
var win = window.open();
if(filelist.message != "error"){
for(var i = 0; i <= MaxPageNum; i ++){
try{ obj[filelist.data[i].pageIndex] = filelist.data[i].downloadUrl; }catch{}
}
for(i = 0; i <= MaxPageNum; i ++){
if(obj[i] != undefined) { dochtml += "<img src='" + obj[i] + "' title='第" + (i + 1) + "页'><br>"; PageCnt += 1; }
}
var script = `<script>function printmode(){
document.getElementsByClassName("msg")[0].remove();
document.getElementsByTagName("style")[0].innerText = "img{width:100%;margin-top:15px;}";
}</script>`;

var header = `<title>课件浏览器</title>
<meta charset="utf-8">
<style>body{background-color: rgb(50, 54, 57); user-select: none;margin:0px;}
.ctrl{font-size:14px; border-radius:15px; padding:5px; padding-left:13px; padding-right:13px; background-color: rgb(51, 51, 51); color:#FFF; border:1px solid #CCC;}
.msg{position:fixed; top:15px; left:15px; z-index:5;} img{width:60%; min-width:600px; margin-top:10px; border-radius:4px;}
.printBtn{margin:10px;} a{color:#FFF; text-decoration:none;}</style>`;

var body = `<body>
<div class="ctrl msg">
&nbsp;共` + PageCnt + `页&nbsp;
</div>
<center>` + dochtml + `<button onclick="this.remove();printmode();" class="ctrl printBtn">打印&nbsp;&amp;&nbsp;PDF存储模式</button></center></body>`;

win.document.write(header + script + body);
} else {
win.document.write("<title>出现问题</title><h2>抱歉,无法为您获取课件</h2><hr><b>你可以联系该课件的分享者,以取得解决方案。</b>");
}
}

我在里面加入了 "打印&PDF" 存储模式,方便使用Firefox、Chrome和Edge的PDF存储模式将课件存为文件。

完整代码

给大家加上完成后的代码:

 javascript:
function ShowCourseware(filelist){
dochtml = "";
MaxPageNum = 200;
PageCnt = 0;
result = filelist;
obj = [];
var filelist = eval("("+result+")");
var win = window.open();
if(filelist.message != "error"){
for(var i = 0; i <= MaxPageNum; i ++){
try{
obj[filelist.data[i].pageIndex] = filelist.data[i].downloadUrl;
}catch{}
}
for(i = 0; i <= MaxPageNum; i ++){
if(obj[i] != undefined) {dochtml += "<img src='" + obj[i] + "' title='第" + (i + 1) + "页'><br>"; PageCnt += 1;}
}
var script = `<script>function printmode(){
document.getElementsByClassName("msg")[0].remove();
document.getElementsByTagName("style")[0].innerText = "img{width:100%;margin-top:15px;}";
}</script>`;

var header = `<title>课件浏览器</title>
<meta charset="utf-8">
<style>body{background-color: rgb(50, 54, 57); user-select: none;margin:0px;}
.ctrl{font-size:14px; border-radius:15px; padding:5px; padding-left:13px; padding-right:13px; background-color: rgb(51, 51, 51); color:#FFF; border:1px solid #CCC;}
.msg{position:fixed; top:15px; left:15px; z-index:5;} img{width:60%; min-width:600px; margin-top:10px; border-radius:4px;}
.printBtn{margin:10px;} a{color:#FFF; text-decoration:none;}</style>`;

var body = `<body>
<div class="ctrl msg">
&nbsp;共` + PageCnt + `页&nbsp;
</div>
<center>` + dochtml + `<button onclick="this.remove();printmode();" class="ctrl printBtn">打印&nbsp;&amp;&nbsp;PDF存储模式</button></center></body>`;


win.document.write(header + script + body);
} else {
win.document.write("<title>出现问题</title><h2>抱歉,无法为您获取课件</h2><hr><b>你可以联系该课件的分享者,以取得解决方案。</b>");
}
}
function GetCourseware(cid, ver){
var xhttp = null;
if(window.XMLHttpRequest){xhttp = new XMLHttpRequest();}
else if(window.ActiveXObject){xhttp = new ActiveXObject('Microsoft.XMLHTTP');}

xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
ShowCourseware(this.responseText);
}
};
xhttp.open("GET", "./enow/thumbnail/api/v1/courseware?coursewareId=" + cid + "&version=" + ver + "&resolution=960_640", true);
xhttp.send();
}
function GetList(){
var uid = window.location.href.split("?")[1].split("&")[0].split("=")[1];
var ServerTime = new Date().getTime();

var xhttp = null;
if(window.XMLHttpRequest){xhttp = new XMLHttpRequest();}
else if(window.ActiveXObject){xhttp = new ActiveXObject('Microsoft.XMLHTTP');}

xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var jstr = eval("(" + this.responseText + ")");
if(jstr["error_code"] == 0){
var OPStr = "";
for(var i = 0; i <= 10; i ++) {
try{
OPStr += (i + 1) + ". " + jstr.data[i].name + "\n";
} catch{}
}

let k = prompt("请选择需要打开的课件(填数字编号)\n" + OPStr, "1");
GetCourseware(jstr.data[k - 1].cid, jstr.data[k - 1].version);
} else {
alert("课程未开始或课程已结束!");
}
}
};
xhttp.open("POST", "./apis.json?actionName=GET_COURSE_ACCESS_CODE_LIST&t=" + ServerTime, true);
xhttp.setRequestHeader('Content-Type','application/json');
xhttp.send('{"courseUid":"' + uid + '"}');
}

GetList();

使用方法和最终效果

使用方法

  1. 将完整代码复制
  2. 将本页内容加入书签栏(不要放在其他收藏夹)
    加入书签
  3. 右键加入的书签,点击“编辑”
    右键
  4. 名称改为“破解”(其他名称也没问题)
    编辑1
  5. 删除URL后的输入框的内容,在里面粘贴复制的代码
    在这里插入图片描述
  6. 点击“完成”即可

最终效果

在教室内点击刚才加入的书签,会弹出以下画面,找到想要打开的课件,输入对应序号,点击确定。
在这里插入图片描述

看见课程列表:
在这里插入图片描述


这个程序只是用来演示查看后台和对接后台的原理和过程,供代码学习参考,请勿滥用!

该文章并非转载,只是我自己有一个CSDN的账户

标签: Javascript

添加新评论