声明
这个程序只是用来演示查看后台和对接后台的原理和过程,供代码学习参考,请勿滥用!
起因
因为上海疫情,我们所有的活动都得在线上完成,作为一个新时代好学生,我该做点什么了。
我的学校选择的上课平台是希沃的立知课堂,教师端是希沃白板电脑客户端,学生端是网页端、微信小程序端、移动设备软件端。
观察后台
一看到网页端,就说明可以用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接口,兼容性不行还编起来难。
我选择了以下两种方案:
- Tampormonkey 油猴脚本
- 直接在浏览器书签栏加入带有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"> 共` + PageCnt + `页 </div><center>` + dochtml + `<button οnclick="this.remove();printmode();" class="ctrl printBtn">打印 & 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"> 共` + PageCnt + `页 </div><center>` + dochtml + `<button οnclick="this.remove();printmode();" class="ctrl printBtn">打印 & 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();
使用方法和最终效果
使用方法
- 将完整代码复制
- 将本页内容加入书签栏(不要放在其他收藏夹)
3. 右键加入的书签,点击”编辑“
4. 名称改为“破解”(其他名称也没问题)
5. 删除URL后的输入框的内容,在里面粘贴复制的代码
6. 点击“完成”即可
最终效果
在教室内点击刚才加入的书签,会弹出以下画面,找到想要打开的课件,输入对应序号,点击确定。
看见课程列表:
这个程序只是用来演示查看后台和对接后台的原理和过程,供代码学习参考,请勿滥用!