声明

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

起因

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

观察后台

一看到网页端,就说明可以用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. 名称改为“破解”(其他名称也没问题)
    破解希沃立知课堂——查看课件篇
  5. 删除URL后的输入框的内容,在里面粘贴复制的代码
    破解希沃立知课堂——查看课件篇
  6. 点击“完成”即可

最终效果

在教室内点击刚才加入的书签,会弹出以下画面,找到想要打开的课件,输入对应序号,点击确定。
破解希沃立知课堂——查看课件篇

看见课程列表:
破解希沃立知课堂——查看课件篇

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

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

发表回复