本帖最后由 huajiqaq 于 2026-3-3 21:14 编辑
国资e学视频想要加速,通过搜索找到之前有相关帖子 |记录||分享|成功实现浏览器插件快速播放国资E学视频
,安装后提示视频播放器异常,这说明网页可能更新了相关检测,以下是本人研究,特此记录。
首先,尝试打开控制台,发现断点到了无限debugger。通过控制台调用回溯,发现这是一个通过构造无限debugger匿名函数,并且检测函数开始时间和结束时间判断是否在控制台下,伪代码大概如下:
// 从配置对象中解构参数,包含默认值
const {
startDelayMs: initialDelay = 3000, // 初始延迟 3秒
thresholdMs: timeThreshold = 100, // 时间阈值 100ms
confirmCount: requiredConfirmations = 0, // 需要连续成功的次数
minIntervalMs: minInterval = 500, // 最小轮询间隔 500ms
maxIntervalMs: maxInterval = 1500, // 最大轮询间隔 1500ms
onDetected: detectionCallback // 检测成功后的回调函数
} = config;
// 状态变量
let consecutiveSuccessCount = 0;
let detectionTriggered = false;
// 最终执行函数 - 当检测条件满足时调用
function executeDetection() {
if (!detectionTriggered) {
detectionTriggered = true;
if (detectionCallback) {
detectionCallback();
} else {
document.open();
document.write('页面内容');
document.close();
}
}
}
// 主要的检测轮询函数
function pollingCheck() {
if (detectionTriggered) return;
const startTime = Date.now();
// 执行debugger指令,触发调试器中断
(function() {
const debugFunction = new Function('debugger');
try {
debugFunction();
} catch (error) {}
})();
const executionTime = Date.now() - startTime;
if (executionTime > timeThreshold) {
consecutiveSuccessCount++;
if (consecutiveSuccessCount >= requiredConfirmations) {
executeDetection();
return;
}
} else {
consecutiveSuccessCount = Math.max(0, consecutiveSuccessCount - 1);
}
scheduleNextPolling();
}
// 安排下一次轮询
function scheduleNextPolling() {
const effectiveMinInterval = Math.max(0, minInterval);
const effectiveMaxInterval = Math.max(effectiveMinInterval, maxInterval);
const randomDelay = effectiveMinInterval +
Math.random() * (effectiveMaxInterval - effectiveMinInterval);
setTimeout(pollingCheck, randomDelay);
}
// 启动检测
setTimeout(pollingCheck, Math.max(0, initialDelay));
删除原始代码的 _0x491ce9['xWwHJ'](setTimeout, _0x2c52fe, Math['max'](0x0, _0xdb73b9)); 即可。
这里因为只是调试脚本,解决脚本无法加速视频的bug,这里只是本地解决反调试,探究如何绕过视频加速限制,为了方便就本地替换js,通过右键js手动替代js文件,并格式化js文件。
重新加载原网页,发现网页处于无限递归,在控制台源代码处点击暂停按钮,发现执行到了:
return _0x31d1f1['toString']()['search'](_0x7da7ff['lAhBM'])['toString']()['constructor'](_0x31d1f1)['search'](_0x7da7ff['lAhBM']);
还原大概逻辑为:
// 最终还原的代码逻辑
(function() {
// 控制标志
let hasExecuted = true;
// 创建只执行一次的函数包装器
function createOnceWrapper(thisArg, fn) {
const wrappedFn = hasExecuted ? function() {
if (fn) {
const result = fn.apply(thisArg, arguments);
fn = null; // 释放
return result;
}
} : function() {}; // 空函数
hasExecuted = false;
return wrappedFn;
}
// 定义反调试函数
let detector = createOnceWrapper(this, function detect() {
// 无限递归陷阱
return detector
.toString()
.search(/(((.+)+)+)+$/) // 灾难性正则
.toString()
.constructor(detector) // 重新创建自身
.search(/(((.+)+)+)+$/); // 再次搜索
});
// 立即执行
detector(); // 触发反调试
})();
发现是通过构造了陷阱正则表达式达到了无限递归。这里很简单,把正则也就是原始代码的 _0x7da7ff['lAhBM'] 替换为普通正则即可,比如 .*。刷新发现网页正常加载。之后删除之前提到的 _0x491ce9['xWwHJ'](setTimeout, _0x2c52fe, Math['max'](0x0, _0xdb73b9));,至此反调试已经成功。
下一步就是视频加速检测的去除。
通过在播放器页面直接搜索"视频播放器运行异常",发现搜索到了一个关键字,很幸运网站没有混淆字符串,直接可以搜索到:
'xKbtO': '视频播放器运行异常,强制退出登录',
再次向上查找 xKbtO 调用,可以发现最终被 NeTjy 调用:
(_0x4f07a5['PnAKD'](_0x179815['value']['_rate'], 0x2) || _0x4f07a5['XwpSO'](_0x179815['value']['_rate'], 0.5)) && _0x4f07a5['Ssaiy'](_0x390d05)['user']['logOut'](_0x4f07a5['SCzUX'])['then'](_0x11af87 => {
_0x45ae2c['$modal']['msgError'](_0x26ddcb['NeTjy']),
window['location']['href'] = _0x26ddcb['wfcxW'](window['location']['origin'], _0x26ddcb['zsGnF']);
}
还原为伪代码是:
// 监听倍速变化事件
player.value.on('ratechange', function(data) {
// 获取当前倍速
const currentRate = player.value._rate;
// 如果倍速大于2.0 或 小于0.5
if (currentRate > 2.0 || currentRate < 0.5) {
// 执行登出
user.logOut().then(() => {
// 显示错误提示
$modal.msgError('视频播放器运行异常,强制退出登录');
// 跳转到首页
window.location.href = window.location.origin + '/home';
});
}
});
解决也很简单,直接拦截 ratechange 事件即可。
最终实现油猴脚本代码:
// ==UserScript==
// @name 国资e学视频加速
// @namespace huajiqaq
// @description 解决切后台暂停、倍速播放被强制登出问题
// @version 1.0
// @match https://elearning.tcsasac.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// 阻止页面可见性检测(切后台不暂停)
const events = ['visibilitychange', 'pagehide', 'blur', 'focus'];
events.forEach(event => {
window.addEventListener(event, e => {
e.stopImmediatePropagation();
}, true);
});
// 拦截 ratechange 事件监听(不让播放器检测倍速变化)
const originalAdd = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
if (type === 'ratechange') {
console.log('拦截倍速检测');
return;
}
return originalAdd.call(this, type, listener, options);
};
// 设置5倍速
setInterval(() => {
const video = document.querySelector('video');
if (video) {
video.playbackRate = 5;
video.muted = true;
if (video.paused) video.play();
}
}, 1000);
})();