上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

国资e学反调试绕过以及视频加速

[复制链接]

该用户从未签到

1

主题

0

回帖

5

积分

助理工程师

积分
5
发表于 2 小时前 | 显示全部楼层 | 阅读模式

本帖最后由 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);
})();

发表回复

本版积分规则

快速回复 返回顶部 返回列表