# 华为云考试绕过切屏检测方法(非虚拟机)
> 在浏览器中安装油猴拓展
> 在油猴拓展里添加下面的脚本即可
> 实测可用:
> (除了华为云考试,雨课堂应该也可以用)
>
- > // ==UserScript==
- > // [url=home.php?mod=space&uid=5839]@name[/url] 通用阻止切屏检测
- > // @namespace http://tampermonkey.net/
- > // [url=home.php?mod=space&uid=73703]@version[/url] 0.1.0
- > // @description 尝试阻止各类网站的切屏、焦点丢失等检测
- > // @author nodeseek@小号 && Gemini
- > // @match http://*/*
- > // @match https://*/*
- > // @run-at document-start
- > // @grant unsafeWindow
- > // @license GPL-3.0
- > // ==/UserScript==
- >
- > (function () {
- > 'use strict';
- > const window = unsafeWindow; // 使用原始 window 对象
- >
- > // 黑名单事件,这些事件的监听器将被阻止
- > const blackListedEvents = new Set([
- > "visibilitychange", // 页面可见性改变
- > "blur", // 元素或窗口失去焦点
- > "focus", // 元素或窗口获得焦点 (某些检测可能反向利用focus)
- > "pagehide", // 页面隐藏(例如导航到其他页面)
- > "freeze", // 页面被冻结 (较新的事件)
- > "resume", // 页面从冻结状态恢复 (较新的事件)
- > "mouseleave", // 鼠标移出元素(通常是 document 或 body)
- > "mouseout", // 鼠标移出元素(更通用的移出,但要小心副作用)
- > // "focusout", // 元素将要失去焦点(与blur类似,但更通用,看情况添加)
- > // "focusin", // 元素将要获得焦点(与focus类似,看情况添加)
- > ]);
- >
- > // 白名单属性,这些属性在 document 对象上将被伪造
- > const spoofedDocumentProperties = {
- > hidden: { value: false, configurable: true },
- > mozHidden: { value: false, configurable: true }, // Firefox (旧版)
- > msHidden: { value: false, configurable: true }, // Internet Explorer
- > webkitHidden: { value: false, configurable: true }, // Chrome, Safari, Opera (旧版 Blink/WebKit)
- > visibilityState: { value: "visible", configurable: true },
- > hasFocus: { value: () => true, configurable: true }
- > };
- >
- > // 需要清空/置空的事件处理器属性 (on-event handlers)
- > const eventHandlersToNullifyDocument = [
- > "onvisibilitychange",
- > "onblur",
- > "onfocus",
- > "onmouseleave",
- > "onmouseout",
- > // "onfocusout",
- > // "onfocusin",
- > "onpagehide",
- > "onfreeze",
- > "onresume"
- > ];
- >
- > const eventHandlersToNullifyWindow = [
- > "onblur",
- > "onfocus",
- > "onpagehide",
- > "onpageshow", // 有些检测可能通过 pageshow 结合 persisted 属性判断
- > "onfreeze",
- > "onresume",
- > "onmouseleave", // window 也有 onmouseleave
- > "onmouseout"
- > ];
- >
- > const isDebug = false; // 设置为 true 以启用调试日志
- > const scriptPrefix = "[通用阻止切屏检测]";
- > const log = console.log.bind(console, `%c${scriptPrefix}`, 'color: #4CAF50; font-weight: bold;');
- > const warn = console.warn.bind(console, `%c${scriptPrefix}`, 'color: #FFC107; font-weight: bold;');
- > const error = console.error.bind(console, `%c${scriptPrefix}`, 'color: #F44336; font-weight: bold;');
- > const debug = isDebug ? log : () => { };
- >
- > /**
- > * 伪装函数的 toString 方法,使其看起来像原始函数。
- > * @param {Function} modifiedFunction 被修改的函数
- > * @param {Function} originalFunction 原始函数
- > */
- > function patchToString(modifiedFunction, originalFunction) {
- > if (typeof modifiedFunction !== 'function' || typeof originalFunction !== 'function') {
- > warn("patchToString: 传入的参数不是函数。", modifiedFunction, originalFunction);
- > return;
- > }
- > try {
- > const originalToStringSource = Function.prototype.toString.call(originalFunction);
- > modifiedFunction.toString = () => originalToStringSource;
- >
- > // 进一步伪装 toString.toString
- > const originalToStringToStringSource = Function.prototype.toString.call(originalFunction.toString);
- > Object.defineProperty(modifiedFunction.toString, 'toString', {
- > value: () => originalToStringToStringSource,
- > enumerable: false,
- > configurable: true, // 保持可配置,以防万一
- > writable: false
- > });
- > debug(`patchToString applied for: ${originalFunction.name || 'anonymous function'}`);
- > } catch (e) {
- > error("patchToString failed:", e, "for function:", originalFunction.name);
- > }
- > }
- >
- >
- > /**
- > * 劫持并修改对象的 addEventListener 方法。
- > * @param {EventTarget} targetObject 要劫持的对象 (window, document, Element)
- > * @param {string} objectName 用于日志记录的对象名称
- > */
- > function patchAddEventListener(targetObject, objectName) {
- > if (!targetObject || typeof targetObject.addEventListener !== 'function') {
- > warn(`Cannot patch addEventListener for invalid target: ${objectName}`);
- > return;
- > }
- > const originalAddEventListener = targetObject.addEventListener;
- >
- > targetObject.addEventListener = function (type, listener, optionsOrCapture) {
- > if (blackListedEvents.has(type.toLowerCase())) {
- > log(`BLOCKED ${objectName}.addEventListener: ${type}`);
- > return undefined; // 阻止添加黑名单中的事件监听器
- > }
- > debug(`ALLOWED ${objectName}.addEventListener: ${type}`, listener, optionsOrCapture);
- > return originalAddEventListener.call(this, type, listener, optionsOrCapture);
- > };
- >
- > patchToString(targetObject.addEventListener, originalAddEventListener);
- > log(`${objectName}.addEventListener patched.`);
- > }
- >
- > /**
- > * 劫持并修改对象的 removeEventListener 方法 (可选,但建议一起修改)。
- > * @param {EventTarget} targetObject 要劫持的对象
- > * @param {string} objectName 用于日志记录的对象名称
- > */
- > function patchRemoveEventListener(targetObject, objectName) {
- > if (!targetObject || typeof targetObject.removeEventListener !== 'function') {
- > warn(`Cannot patch removeEventListener for invalid target: ${objectName}`);
- > return;
- > }
- > const originalRemoveEventListener = targetObject.removeEventListener;
- >
- > targetObject.removeEventListener = function (type, listener, optionsOrCapture) {
- > if (blackListedEvents.has(type.toLowerCase())) {
- > log(`Original call to ${objectName}.removeEventListener for blacklisted event '${type}' would have been ignored by our addEventListener patch anyway. Allowing native call if needed.`);
- > // 即使我们阻止了 addEventListener,原始的 removeEventListener 仍然应该能安全调用
- > // 因为如果监听器从未被添加,调用 remove 也无害。
- > }
- > debug(`PASSTHROUGH ${objectName}.removeEventListener: ${type}`, listener, optionsOrCapture);
- > return originalRemoveEventListener.call(this, type, listener, optionsOrCapture);
- > };
- > patchToString(targetObject.removeEventListener, originalRemoveEventListener);
- > log(`${objectName}.removeEventListener patched.`);
- > }
- >
- >
- > /**
- > * 修改对象上的属性,使其返回伪造的值。
- > * @param {object} targetObject 目标对象 (e.g., document)
- > * @param {object} propertiesToSpoof 属性描述对象
- > * @param {string} objectName 对象名称
- > */
- > function spoofProperties(targetObject, propertiesToSpoof, objectName) {
- > if (!targetObject) {
- > warn(`Cannot spoof properties for invalid target: ${objectName}`);
- > return;
- > }
- > for (const prop in propertiesToSpoof) {
- > if (Object.prototype.hasOwnProperty.call(propertiesToSpoof, prop)) {
- > try {
- > Object.defineProperty(targetObject, prop, propertiesToSpoof[prop]);
- > debug(`Spoofed ${objectName}.${prop}`);
- > } catch (e) {
- > error(`Failed to spoof ${objectName}.${prop}:`, e);
- > }
- > }
- > }
- > log(`${objectName} properties spoofed.`);
- > }
- >
- > /**
- > * 清空或置空对象上的事件处理器属性。
- > * @param {object} targetObject 目标对象
- > * @param {string[]} eventHandlerNames 事件处理器名称数组
- > * @param {string} objectName 对象名称
- > */
- > })();
复制代码 |