通用失焦反检测

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);
    }
})();