什么叫只有Web端没有被邀请参加派对? 这不公平!
学习通有许多考试只允许手机参加,而本文通过 UA 伪装、参数构造与 JSBridge Hook 实现了在电脑浏览器上参加这类考试。
实验原理简析
| 层级 | 检测方式 | 绕过方法 |
|---|---|---|
| HTTP层 | User-Agent 判断 | UA伪装插件 |
| 前端路由 | URL参数校验 | 手动构造URL |
| JS层 | 设备特征检测 | Chrome设备模拟 |
| Native层 | JSBridge通信 | Hook模拟APP响应 |
| 安全层 | 签名验证 | 构造签名响应 |
我们发现:服务端未严格验证签名来源,仅依赖前端+JSBridge限制
技术细节
JSBridge 通信机制
学习通 APP 使用 JSBridge 实现网页与原生 APP 的通信:
网页 → 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 回调期望的响应格式:
{
success: true,
result: 0,
typeFlag: '{"type":"startExam","funckey":"startExam_xxx"}',
cxcid: '设备ID',
cxtime: '时间戳',
paramToken: '参数签名',
signToken: '签名Token'
}
测试环境
- 时间:2025年12月
- 浏览器:Edge / Chrome
- 学习通版本:6.7.1
准备工作
安装浏览器插件
安装 User-Agent Switcher and Manager(Chrome/Edge/Firefox 均可)
获取学习通手机端 UA
在手机学习通内置浏览器中访问:
https://www.whatismybrowser.com/detect/what-is-my-user-agent
复制显示的 User-Agent 字符串,格式类似:
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_...
学习通访问外部链接的小技巧:在笔记里直接写一个链接,点开访问

操作步骤
第一步:提取课程参数
- 电脑登录学习通,进入目标课程页面
- F12 打开开发者工具 → Elements 面板
- Ctrl+F 搜索 courseId、classId 等参数

也可以在 URL 中找到:

第二步:获取考试ID
- 启用 UA 伪装(设置为手机端 UA)
- 构造并访问以下链接(填写自己的参数):
https://mooc1.xuexi365.com/exam-ans/exam/test?courseId=【courseId】&classId=【classId】&ut=s&cpi=【cpi】&enc=【enc】&openc=【openc】

- F12 → Ctrl+F 搜索 TestRelationInfo
- 找到类似内容:

TestRelationInfo = {"7707727":{"limitTime":"60","expiredAutoSubmit":"1"}};
- 记录其中的数字 ID(如
7707727),这就是 taskrefId
第三步:进入考试页面
- 构造最终链接:
https://mooc1-api.chaoxing.com/exam-ans/exam/phone/task-exam?taskrefId=【taskrefId】&courseId=【courseId】&classId=【classId】&ut=s&cpi=【cpi】
- 确保 UA 伪装已启用
- 访问链接

- F12 打开开发者工具 → 点击左上角 设备模拟图标(或 Ctrl+Shift+M)
第四步:注入 Hook 代码
在 Console 中执行以下代码:
// ========== 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');
})();
console.log('全部 Hook 已注入,可以点击开始考试了');
第五步:开始考试
- 按
Ctrl+F8禁用所有断点(学习通网页检测到F12打开会强制debugger) - 点击”开始考试”按钮
- 完成验证码验证
- 成功进入考试!

参数速查表
| 步骤 | 链接模板 |
|---|---|
| 获取 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= |
注意事项
- enc 参数有时效性,过期需重新获取
- UA 必须是学习通 APP 的,普通手机浏览器 UA 无效
- 考试过程中不要关闭 UA 伪装和设备模拟
- Hook 代码需要在每次刷新页面后重新执行
参考资料
博主有补充
功能实测
2026年5月中旬实测,本方案可以解除大部分的限制,包括但不限于 强制手机答题 、 强制刷脸进入考试 、 禁止截图 、前后摄像头采集 、录屏采集
对线上期中考试、线上期末考试的效果奇佳
虽然该方案可以屏蔽这些防作弊的信息采集功能,但目的仅限于使自动化防作弊判断失效
如果学校会查看这些记录的话,由于使用该方案的同学后台视频文件为空,可能会被学校强制零分
所以我们建议可以先试用本方案快速完成答题,然后使用手机接管考试,制造正常的记录(可能会被判断为离开考试)
准备工作
原文中使用的UA模拟插件是 User-Agent Switcher and Manager
在实际操作中,博主使用的是操作和界面都相对简单且支持中文的 User-Agent模拟器

另外,我们注意到有些文章提出 AdGuard 也提供UA模拟的功能,如果你有 AdGuard 也可以使用 AdGuard 提供的UA模拟功能,但使用方法需自行探索
操作步骤
第二步
原文中说
enc 参数有时效性,过期需重新获取
但实际上 enc 和 openc 均无时效性,有时效性的是第二步中获取到的 taskrefId ,存活时间为1分钟左右
但是只要第一次构造完成并成功访问,后续刷新页面不会受到影响
第四步
考试准备页
当老师设置禁止使用低版本客户端答题时,会弹出版本过低的弹窗,无法进入考试

最初在尝试时我们尝试直接清楚前端代码中script代码块内相关内容,但是可能与后续脚本覆盖、动态事件绑定或页面初始化流程有关的问题导致无法生效,于是我们直接使用控制在运行后覆盖相关函数
此时我们需要在 Console 中执行以下脚本:
(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 已绕过");
})();
此时再次点击 开始考试 就会正常弹出验证码,进入考试
考试页
我们在考试中发现文本输入框是禁止粘贴的,这就导致论文或大段的简答题只能手动打字,非常占用我们的宝贵的摸鱼考试时间
此时我们只需要在控制台执行以下脚本即可解除禁止粘贴:
// 1. 拦截并阻止外层网页的防粘贴事件
document.addEventListener('paste', function(e) {
e.stopImmediatePropagation();
}, true);
// 2. 拦截并阻止编辑器内部自带的原生防粘贴事件
if (window.editors) {
window.editors.forEach(function(editorObj) {
if(editorObj.ueditor && editorObj.ueditor.document) {
editorObj.ueditor.document.addEventListener('paste', function(event) {
event.stopImmediatePropagation();
}, true);
// 清理原生 onpaste 属性
editorObj.ueditor.document.body.onpaste = null;
}
});
}
console.log("🚀 终极暴力粘贴解除已运行完毕!");
如果这场考试非常严格,停留时间过长可能会被后台判定为“页面失去焦点”或“切屏”导致被判定为作弊,所以我们就需要解除切屏检测相关逻辑
在控制台中执行:
// 暴力覆盖超星/学习通内部的切屏和异常日志上报函数
window.exitCountAndExitTip = function() { console.log("成功拦截:切屏警告弹窗"); };
window.switchScreenLog = function() { console.log("成功拦截:切屏日志后台上报"); };
window.switchScreenSuspendedWindowLog = function() {};
window.screenMonitorLog = function() {};
window.screenMonitorStopLog = function() {};
window.clientSnapShotLog = function() {};
window.snapshotMonitorLog = function() {};
if (window.jsBridge && typeof window.jsBridge.unbind === 'function') {
window.jsBridge.unbind('CLIENT_WEB_LIFECYCLE');
window.jsBridge.unbind('CLIENT_SCREEN_MONITOR');
console.log("成功解绑:原生客户端切屏通讯");
}
// 强制让系统认为网页永远处于“可见”状态
Object.defineProperty(document, 'hidden', { value: false, writable: false });
Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: false });
// 拦截失去焦点和隐藏事件
var blockVisibilityEvent = function(e) {
e.stopImmediatePropagation();
};
window.addEventListener('visibilitychange', blockVisibilityEvent, true);
window.addEventListener('blur', blockVisibilityEvent, true);
document.addEventListener('visibilitychange', blockVisibilityEvent, true);
document.addEventListener('blur', blockVisibilityEvent, true);
// 清理现有的 onblur 属性
window.onblur = null;
document.onblur = null;
document.body.onblur = null;
console.log("切屏检测、焦点监控已全部被阉割!你可以自由切换窗口了。");
此时,考试时的大部分限制我们就成功解除了
自动化执行
博主目前正在和博主的小AI一起做一个可以自动化完成某些步骤的油猴脚本,尽可能地缩小同学们的使用门槛,目前已经有了一个基本可用的demo,等到我们完善完成后再开放给同学们使用
开放后我们会在第一时间更新在本文章的前段

Comments NOTHING