什么叫只有Web端没有被邀请参加派对? 这不公平!

学习通有许多考试只允许手机参加,而本文通过 UA 伪装、参数构造与 JSBridge Hook 实现了在电脑浏览器上参加这类考试。

免责声明:本文仅供技术学习交流,请遵守学校相关规定,不要做违规的事情! (那就是要做啊我看)


感谢与补充(2026年5月更新)

本文最初写于 2025年12月14日。感谢 mrgeda 的修正建议与补充内容!以下补充方案已在 2026年5月 的学习通版本中验证有效。

补充一:APP版本检测绕过

问题说明:原方案在当前版本会触发”APP版本过低”弹窗,阻断考试入口。直接修改页面中的 <script> 内容不会稳定生效,推测与后续脚本覆盖或动态事件绑定有关。

解决方案:在考试入口页、点击”开始考试”前,于浏览器控制台执行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 覆盖确认弹窗函数并重新绑定按钮事件
(function () {
// 覆盖确认弹窗函数
window.confirmEnterPop = function () {
preEnter();
};

// 重新绑定按钮点击事件
const startBtn = $("#start");
startBtn.off('tap');
startBtn.off('click');
startBtn.on('tap', preEnter);
startBtn.on('click', preEnter);

console.log("confirmEnterPop 已绕过");
})();

原理:通过覆盖 confirmEnterPop 函数并重新绑定按钮事件,绕过客户端版本检测逻辑,直接调用 preEnter() 进入考试流程。

补充二:防粘贴与切屏监控绕过

执行时机更正:防粘贴与切屏监控脚本需要在进入正式答题页面、题目和输入框已经渲染出来之后再执行。学习通会在题目组件初始化完成后动态绑定限制逻辑,如果过早注入,后续 SPA 初始化或题目切换很可能会把前面改掉的事件与 Hook 覆盖掉。

问题说明:进入考试后,系统在答题环节设置了以下限制:

  1. 防粘贴机制:简答题的 UEditor 富文本编辑器在 beforePaste 事件中强行清空剪贴板数据
  2. 切屏与F12监控:页面绑定了 CLIENT_WEB_LIFECYCLE 和 DOM 焦点事件,切换窗口或打开控制台会触发日志上报甚至强制收卷
  3. 反调试死循环:部分页面打开控制台时被 debugger; 语句卡死

解决方案:进入正式答题页面并看到输入框后,在控制台执行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 1. 解除UEditor防粘贴拦截
if (window.editors && window.editors.length > 0) {
window.editors.forEach(function(e) {
var ue = e.ueditor;
if (ue) {
if (ue._listeners && ue._listeners['beforepaste']) ue._listeners['beforepaste'] = [];
ue.options.pasteplain = false;
ue.options.disablePasteImage = false;
}
});
}

// 2. 暴力阉割切屏、监控与日志上报函数
window.exitCountAndExitTip = window.switchScreenLog = window.switchScreenSuspendedWindowLog = window.screenMonitorLog = window.screenMonitorStopLog = window.clientSnapShotLog = window.snapshotMonitorLog = function() {};

// 3. 解除原生客户端通讯中的生命周期探测
if (window.jsBridge && typeof window.jsBridge.unbind === 'function') {
window.jsBridge.unbind('CLIENT_WEB_LIFECYCLE');
window.jsBridge.unbind('CLIENT_SCREEN_MONITOR');
}

// 4. 强制固定可见状态,拦截原生失焦事件
Object.defineProperty(document, 'hidden', { value: false, writable: false });
Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: false });
var blockVisibility = function(e) { e.stopImmediatePropagation(); };
['visibilitychange', 'blur'].forEach(evt => {
window.addEventListener(evt, blockVisibility, true);
document.addEventListener(evt, blockVisibility, true);
});
window.onblur = document.onblur = document.body.onblur = null;

console.log("粘贴限制与切屏监控已全部解除!");

原理

  • 粘贴解禁:清空编辑器底层 beforepaste 事件数组,剥夺其修改剪贴板数据的能力
  • 事件劫持:重定义 visibilityState 属性,利用 stopImmediatePropagation() 在捕获阶段拦截切屏事件
  • 上报阻断:重写所有异常记录函数,切断 JSBridge 层面的状态轮询

实验原理简析

学习通的仅手机端限制主要依赖以下机制:

层级 检测方式 绕过方法
HTTP层 User-Agent 判断 UA伪装插件
前端路由 URL参数校验 手动构造URL
JS层 设备特征检测 Chrome设备模拟
Native层 JSBridge通信 Hook模拟APP响应
安全层 签名验证 构造签名响应

我们发现:服务端未严格验证签名来源,仅依赖前端+JSBridge限制


技术细节(逆向分析)

JSBridge 通信机制

学习通 APP 使用 JSBridge 实现网页与原生 APP 的通信:

1
2
3
4
5
网页 → jsBridge.postNotification('CLIENT_FORM_SIGN', data)
→ iframe.src = 'jsbridge://PostNotificationWithId-1'
→ APP拦截并处理
→ APP 调用 jsBridge.trigger('CLIENT_FORM_SIGN', response)
→ 网页收到回调,继续执行

在电脑浏览器中,如果我们只通过修改UA与构造URL来访问考试
jsbridge:// 将协议无人处理,导致流程中断。

关键 JSBridge 事件

事件名 用途
CLIENT_DEVICE_ID 获取设备唯一标识
CLIENT_FORM_SIGN 表单签名验证
CLIENT_CHECK_URL_TYPE URL类型检查
CLIENT_EVERISK_INFO_CHECK 风险检查

签名回调格式

CLIENT_FORM_SIGN 回调期望的响应格式:

1
2
3
4
5
6
7
8
9
{
success: true,
result: 0,
typeFlag: '{"type":"startExam","funckey":"startExam_xxx"}',
cxcid: '设备ID',
cxtime: '时间戳',
paramToken: '参数签名',
signToken: '签名Token'
}

测试环境

  • 时间:2025年12月(原方案)/ 2026年5月(补充脚本,感谢 mrgeda 验证)
  • 浏览器:Edge / Chrome
  • 学习通版本:6.7.1(原方案)/ 最新版(补充脚本)

准备工作

安装浏览器插件

安装 User-Agent Switcher and Manager(Chrome/Edge/Firefox 均可)

获取学习通手机端 UA

在手机学习通内置浏览器中访问:

1
https://www.whatismybrowser.com/detect/what-is-my-user-agent

复制显示的 User-Agent 字符串,格式类似:

1
2
Mozilla/5.0 (iPhone; CPU iPhone OS 26_1like Mac OS X) AppleWebKit/605.1.15
(KHTML, like Gecko) Mobile/15E148 ... com.ssreader.ChaoXingStudy/ChaoXingStudy_3_6.7.1_ios_phone_...

提示:UA 必须包含 ChaoXingStudy 关键字,普通手机浏览器 UA 无效

学习通访问外部链接的小技巧:在笔记里直接写一个链接,点开访问

查看ua方法.png

操作步骤

第一步:提取课程参数

  1. 电脑登录学习通,进入目标课程页面
  2. F12 打开开发者工具 → Elements 面板
  3. Ctrl+F 搜索 courseIdclassId 等参数
第一次1.png

也可以在 URL 中找到:

第一次2.png

第二步:获取考试ID

  1. 启用 UA 伪装(设置为手机端 UA)

  2. 构造并访问以下链接(填写自己的参数):

1
https://mooc1.xuexi365.com/exam-ans/exam/test?courseId=【courseId】&classId=【classId】&ut=s&cpi=【cpi】&enc=【enc】&openc=【openc】
第一次构造效果.png
  1. F12 → Ctrl+F 搜索 TestRelationInfo

  2. 找到类似内容:

查看Info.png
1
TestRelationInfo = {"7707727":{"limitTime":"60","expiredAutoSubmit":"1"}};
  1. 记录其中的数字 ID(如 7707727),这就是 taskrefId

提示:enc 参数有时效性,过期需重新获取


第三步:进入考试页面

  1. 构造最终链接:
1
https://mooc1-api.chaoxing.com/exam-ans/exam/phone/task-exam?taskrefId=【taskrefId】&courseId=【courseId】&classId=【classId】&ut=s&cpi=【cpi】
  1. 确保 UA 伪装已启用

  2. 访问链接

效果.png
  1. F12 打开开发者工具 → 点击左上角 设备模拟图标(或 Ctrl+Shift+M

第四步:注入入口页 Hook 代码(JSBridge / APP版本检测)

在点击”开始考试”前,先在入口页 Console 中执行以下代码。这里保留的是反调试、验证码回调、JSBridge 与 APP 版本检测绕过;防粘贴与切屏监控脚本不要在这里提前执行,等进入题目页面后再执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// ========== 1. 禁用反调试 ==========
(function() {
var _Function = Function;
Function = function() {
var args = Array.from(arguments);
var body = args[args.length - 1];
if (typeof body === 'string' && body.includes('debugger')) {
args[args.length - 1] = body.replace(/debugger/g, '');
}
return _Function.apply(this, args);
};
Function.prototype = _Function.prototype;

var _setInterval = setInterval;
setInterval = function(fn, time) {
if (typeof fn === 'function' && fn.toString().includes('debugger')) {
return null;
}
return _setInterval.apply(this, arguments);
};
})();

// ========== 2. 验证码回调 ==========
window.cx_captcha_function = function(data) {
console.log('[验证码回调]', data);
if (data.result === true && data.extraData) {
var extra = JSON.parse(data.extraData);
console.log('验证码通过,validate:', extra.validate);
window._captchaValidate = extra.validate;
}
};

// ========== 3. JSBridge Hook ==========
(function() {
var fakeDeviceId = 'IMEI_' + Math.random().toString(36).substr(2, 16).toUpperCase();
var originalPost = jsBridge.postNotification.bind(jsBridge);

jsBridge.postNotification = function(name, userInfo) {
console.log('[发送]', name, userInfo);

setTimeout(function() {
var response = {success: true, result: 0};

if (name === 'CLIENT_FORM_SIGN') {
var typeFlag = '';
try {
typeFlag = userInfo.typeFlag;
} catch(e) {}

response = {
success: true,
result: 0,
typeFlag: typeFlag,
cxcid: fakeDeviceId,
cxtime: String(Date.now()),
paramToken: 'pt_' + Date.now(),
signToken: 'st_' + Date.now(),
checkCount: '0',
extraInfo: {code: ''}
};
} else if (name === 'CLIENT_DEVICE_ID') {
response = {deviceId: fakeDeviceId, imei: fakeDeviceId, success: true};
} else if (name === 'CLIENT_CHECK_URL_TYPE') {
response = {success: true, result: 0, type: 0};
}

console.log('[响应]', name, response);
jsBridge.trigger(name, response);
}, 100);
};

jsBridge.setDevice('ios');
})();

// ========== 4. APP版本检测绕过(感谢 mrgeda 补充)==========
window.confirmEnterPop = function () {
preEnter();
};
const startBtn = $("#start");
startBtn.off('tap');
startBtn.off('click');
startBtn.on('tap', preEnter);
startBtn.on('click', preEnter);

console.log('入口页 Hook 已注入(含 mrgeda 补充的 APP 版本绕过),可以点击开始考试了');

第五步:开始考试

  1. Ctrl+F8 禁用所有断点(学习通网页检测到F12打开会强制debugger)
  2. 点击”开始考试”按钮
  3. 完成验证码验证
  4. 成功进入考试!
最后成功.png

第六步:进入题目后解除防粘贴与切屏监控

进入正式答题页面,看到具体题目和输入框之后,再执行上文”补充二:防粘贴与切屏监控绕过”中的脚本。不要在开始考试按钮页提前执行这一段,因为学习通会在题目渲染完成后重新绑定 UEditor、焦点监听和生命周期监控。

如果考试使用的是单题切换模式,每切换一次题目都可能重新渲染 DOM 与事件监听,因此需要在切题后重新执行一次防粘贴与切屏监控脚本。

参数速查表

步骤 链接模板
获取 taskrefId https://mooc1.xuexi365.com/exam-ans/exam/test?courseId=&classId=&ut=s&cpi=&enc=&openc=
进入考试 https://mooc1-api.chaoxing.com/exam-ans/exam/phone/task-exam?taskrefId=&courseId=&classId=&ut=s&cpi=

注意事项

  1. enc 参数有时效性,过期需重新获取
  2. UA 必须是学习通 APP 的,普通手机浏览器 UA 无效
  3. 考试过程中不要关闭 UA 伪装和设备模拟
  4. 入口页 Hook 代码需要在每次刷新页面后重新执行
  5. 防粘贴与切屏监控脚本需要在进入题目并看到输入框后执行;单题切换模式下,每次切题后也可能需要重新执行

参考资料