Greasy Fork

来自缓存

Greasy Fork is available in English.

通用失焦反检测

相当激进的失焦检测对抗,仅在需要时启用!

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         通用失焦反检测
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  相当激进的失焦检测对抗,仅在需要时启用!
// @author       Klready
// @match        *://*/*
// @run-at       document-start
// @grant        unsafeWindow
// @license      AGPL
// ==/UserScript==

(function() {
    'use strict';

    const LOG_PREFIX = '[Anti-Detect]';
    const win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
    const processedWindows = new WeakSet();


    const NativeUtils = (function() {
        const nativeMap = new WeakMap();
        const originalToString = Function.prototype.toString;
        const originalGetDescriptor = Object.getOwnPropertyDescriptor;
        const originalDefineProperty = Object.defineProperty;

        const fakeToString = function() {
            const func = this;
            return nativeMap.has(func) ? nativeMap.get(func) : originalToString.call(func);
        };

        nativeMap.set(fakeToString, originalToString.call(originalToString));

        try {
            const protoDesc = originalGetDescriptor(Function.prototype, 'toString');
            originalDefineProperty(Function.prototype, 'toString', { ...protoDesc, value: fakeToString });
        } catch (e) {}

        function makeNative(fakeFunc, sourceFunc, customName) {
            let nativeString = typeof sourceFunc === 'function' ? originalToString.call(sourceFunc) : sourceFunc;
            let nativeName = typeof sourceFunc === 'function' ? sourceFunc.name : 'anonymous';
            let nativeLength = typeof sourceFunc === 'function' ? sourceFunc.length : 0;
            if (customName) nativeName = customName;

            nativeMap.set(fakeFunc, nativeString);
            originalDefineProperty(fakeFunc, 'name', { value: nativeName, writable: false, enumerable: false, configurable: true });
            originalDefineProperty(fakeFunc, 'length', { value: nativeLength, writable: false, enumerable: false, configurable: true });
            return fakeFunc;
        }

        function hookPrototype(proto, prop, newDesc) {
            const originalDesc = originalGetDescriptor(proto, prop);
            if (!originalDesc) return;
            const finalDesc = { ...originalDesc, ...newDesc };
            if (newDesc.get && originalDesc.get) finalDesc.get = makeNative(newDesc.get, originalDesc.get, `get ${prop}`);
            if (newDesc.value && typeof originalDesc.value === 'function') finalDesc.value = makeNative(newDesc.value, originalDesc.value, prop);
            originalDefineProperty(proto, prop, finalDesc);
        }

        return { makeNative, hookPrototype, originalDefineProperty };
    })();

    function installClockHooks(targetWindow) {
        try {
            const workerBlob = new Blob([`
                const timerIds = new Map();
                let rafInterval = null;

                self.onmessage = function(e) {
                    const { command, id, delay, type } = e.data;

                    if (command === 'start-timer') {
                        let tid;
                        if (type === 'interval') {
                            tid = setInterval(() => self.postMessage({ command: 'timer-tick', id }), delay);
                        } else {
                            tid = setTimeout(() => self.postMessage({ command: 'timer-tick', id }), delay);
                        }
                        timerIds.set(id, tid);
                    } else if (command === 'clear-timer') {
                        const tid = timerIds.get(id);
                        if (tid) {
                            clearInterval(tid);
                            clearTimeout(tid);
                            timerIds.delete(id);
                        }
                    }

                    else if (command === 'start-raf-loop') {
                        if (!rafInterval) {
                            rafInterval = setInterval(() => {
                                self.postMessage({ command: 'raf-tick' });
                            }, 16); // 16ms ≈ 60fps
                        }
                    } else if (command === 'stop-raf-loop') {
                        if (rafInterval) {
                            clearInterval(rafInterval);
                            rafInterval = null;
                        }
                    }
                };
            `], { type: 'application/javascript' });

            const workerUrl = URL.createObjectURL(workerBlob);
            const clockWorker = new Worker(workerUrl);
            const callbackMap = new Map();
            let globalIdCounter = 1;
            const rafQueue = new Map();
            let rafIdCounter = 1;
            let isRafLoopRunning = false;

            clockWorker.onmessage = function(e) {
                const { command, id } = e.data;

                if (command === 'timer-tick') {
                    const task = callbackMap.get(id);
                    if (task) {
                        try { task.callback.apply(targetWindow, task.args); } catch(err) { console.error(err); }
                        if (task.type === 'timeout') callbackMap.delete(id);
                    }
                }

                else if (command === 'raf-tick') {
                    const currentQueue = new Map(rafQueue);
                    rafQueue.clear();
                    const now = performance.now();
                    currentQueue.forEach((cb, rafId) => {
                        try { cb(now); } catch(err) { console.error(err); }
                    });
                }
            };

            const originalSetInterval = targetWindow.setInterval;
            const originalSetTimeout = targetWindow.setTimeout;
            const originalClearInterval = targetWindow.clearInterval;
            const originalClearTimeout = targetWindow.clearTimeout;

            const fakeSetInterval = function(cb, delay, ...args) {
                if (delay > 1000 || delay === undefined) return originalSetInterval.apply(this, arguments);
                const id = globalIdCounter++;
                callbackMap.set(id, { callback: cb, args, type: 'interval' });
                clockWorker.postMessage({ command: 'start-timer', id, delay, type: 'interval' });
                return id;
            };

            const fakeSetTimeout = function(cb, delay, ...args) {
                if (delay > 1000 || delay === undefined) return originalSetTimeout.apply(this, arguments);
                const id = globalIdCounter++;
                callbackMap.set(id, { callback: cb, args, type: 'timeout' });
                clockWorker.postMessage({ command: 'start-timer', id, delay, type: 'timeout' });
                return id;
            };

            const fakeClearTimer = function(id) {
                if (callbackMap.has(id)) {
                    callbackMap.delete(id);
                    clockWorker.postMessage({ command: 'clear-timer', id });
                } else {
                    originalClearInterval(id);
                    originalClearTimeout(id);
                }
            };

            const originalRAF = targetWindow.requestAnimationFrame;
            const originalCancelRAF = targetWindow.cancelAnimationFrame;

            const fakeRAF = function(callback) {
                if (!isRafLoopRunning) {
                    clockWorker.postMessage({ command: 'start-raf-loop' });
                    isRafLoopRunning = true;
                }
                const id = rafIdCounter++;
                rafQueue.set(id, callback);
                return id;
            };

            const fakeCancelRAF = function(id) {
                rafQueue.delete(id);
            };

            NativeUtils.makeNative(fakeSetInterval, originalSetInterval, 'setInterval');
            NativeUtils.makeNative(fakeSetTimeout, originalSetTimeout, 'setTimeout');
            NativeUtils.makeNative(fakeClearTimer, originalClearInterval, 'clearInterval');

            NativeUtils.originalDefineProperty(targetWindow, 'setInterval', { value: fakeSetInterval });
            NativeUtils.originalDefineProperty(targetWindow, 'setTimeout', { value: fakeSetTimeout });
            NativeUtils.originalDefineProperty(targetWindow, 'clearInterval', { value: fakeClearTimer });
            NativeUtils.originalDefineProperty(targetWindow, 'clearTimeout', { value: fakeClearTimer });

            NativeUtils.makeNative(fakeRAF, originalRAF, 'requestAnimationFrame');
            NativeUtils.makeNative(fakeCancelRAF, originalCancelRAF, 'cancelAnimationFrame');

            NativeUtils.originalDefineProperty(targetWindow, 'requestAnimationFrame', { value: fakeRAF });
            NativeUtils.originalDefineProperty(targetWindow, 'cancelAnimationFrame', { value: fakeCancelRAF });

            if (targetWindow.webkitRequestAnimationFrame) {
                 NativeUtils.originalDefineProperty(targetWindow, 'webkitRequestAnimationFrame', { value: fakeRAF });
            }

        } catch (e) {
            console.error(LOG_PREFIX, 'Clock hook failed', e);
        }
    }

    function applyPrototypeHacks(targetWindow) {
        if (!targetWindow || processedWindows.has(targetWindow)) return;

        const DocProto = targetWindow.Document.prototype;
        const ScreenProto = targetWindow.Screen.prototype;

        NativeUtils.hookPrototype(DocProto, 'hidden', { get: function() { return false; } });
        NativeUtils.hookPrototype(DocProto, 'visibilityState', { get: function() { return 'visible'; } });
        NativeUtils.hookPrototype(DocProto, 'hasFocus', { value: function() { return true; } });

        if (Object.getOwnPropertyDescriptor(ScreenProto, 'isExtended')) {
             NativeUtils.hookPrototype(ScreenProto, 'isExtended', { get: function() { return false; } });
        }
        processedWindows.add(targetWindow);
    }

    function injectEventStopper(targetWindow) {
        const blockEvents = ['blur', 'visibilitychange', 'webkitvisibilitychange', 'mouseleave', 'mouseout', 'pagehide', 'fullscreenchange', 'resize'];
        const eventFilter = (e) => { if (e.isTrusted) { e.stopImmediatePropagation(); e.stopPropagation(); } };

        blockEvents.forEach(evt => {
            try { targetWindow.addEventListener(evt, eventFilter, { capture: true, passive: false }); } catch (e) {}
        });
    }

    function installIframeHook() {
        const IframeProto = win.HTMLIFrameElement.prototype;
        const originalDesc = Object.getOwnPropertyDescriptor(IframeProto, 'contentWindow');
        if (!originalDesc) return;

        const fakeGet = function() {
            const realContentWindow = originalDesc.get.call(this);
            if (realContentWindow && !processedWindows.has(realContentWindow)) {
                try {
                    applyPrototypeHacks(realContentWindow);
                    injectEventStopper(realContentWindow);
                    installClockHooks(realContentWindow);
                } catch (e) {}
            }
            return realContentWindow;
        };

        const hookFunc = NativeUtils.makeNative(fakeGet, originalDesc.get, 'get contentWindow');
        Object.defineProperty(IframeProto, 'contentWindow', { ...originalDesc, get: hookFunc });
    }

    try {
        applyPrototypeHacks(win);
        injectEventStopper(win);
        installClockHooks(win);
        installIframeHook();
        console.log(LOG_PREFIX, 'Initialized (Holographic Mode).');
    } catch (e) {
        console.error(LOG_PREFIX, 'Init error:', e);
    }
})();