如何使用JSBridge Hook与UA伪装实现学习通Web端参加手机考试

Mr哥达 发布于 22 天前 175 次阅读


LEN's Web

本文转载自博客LEN's Web,点击阅读原文

本文文末有非常重要的博主补充的部分,下面是原文正文


什么叫只有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_TYPEURL类型检查
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_...
提示:UA 必须包含 ChaoXingStudy 关键字,普通手机浏览器 UA 无效
学习通访问外部链接的小技巧:在笔记里直接写一个链接,点开访问

操作步骤

第一步:提取课程参数

  1. 电脑登录学习通,进入目标课程页面
  2. F12 打开开发者工具 → Elements 面板
  3. 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
enc 参数有时效性,过期需重新获取

第三步:进入考试页面

  • 构造最终链接:
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 已注入,可以点击开始考试了');

第五步:开始考试

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

参数速查表

步骤链接模板
获取 taskrefIdhttps://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 代码需要在每次刷新页面后重新执行

参考资料

  1. JSBridge 原理分析
  2. Chrome DevTools 设备模拟
  3. User-Agent Switcher 插件

博主有补充

功能实测

2026年5月中旬实测,本方案可以解除大部分的限制,包括但不限于 强制手机答题强制刷脸进入考试禁止截图前后摄像头采集录屏采集

对线上期中考试、线上期末考试的效果奇佳

警告
虽然该方案可以屏蔽这些防作弊的信息采集功能,但目的仅限于使自动化防作弊判断失效
如果学校会查看这些记录的话,由于使用该方案的同学后台视频文件为空,可能会被学校强制零分
所以我们建议可以先试用本方案快速完成答题,然后使用手机接管考试,制造正常的记录(可能会被判断为离开考试)

准备工作

原文中使用的UA模拟插件是 User-Agent Switcher and Manager

在实际操作中,博主使用的是操作和界面都相对简单且支持中文的 User-Agent模拟器

另外,我们注意到有些文章提出 AdGuard 也提供UA模拟的功能,如果你有 AdGuard 也可以使用 AdGuard 提供的UA模拟功能,但使用方法需自行探索

操作步骤

第二步

原文中说

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

但实际上 encopenc 均无时效性,有时效性的是第二步中获取到的 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,等到我们完善完成后再开放给同学们使用

开放后我们会在第一时间更新在本文章的前段