Greasy Fork

来自缓存

Greasy Fork is available in English.

Anti Anti-Debugger Enhanced v1.3

阻止网页通过 Function/eval/debugger 等常见方式来无限断点反调试

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Anti Anti-Debugger Enhanced v1.3
// @namespace    https://example.com/
// @version      1.3.0
// @description  阻止网页通过 Function/eval/debugger 等常见方式来无限断点反调试
// @author       IsKongKongYa
// @match        *://*/*
// @run-at       document-start
// @inject-into  page
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ==================================================================
    // 用户可配置项(按需开关)
    // ==================================================================
    const CONFIG = {
        DEBUG_LOG: false,                    // 控制台调试日志
        AGGRESSIVE_IMPORT_SCRIPTS: true,     // Worker 内 importScripts 使用 XHR+eval 激进拦截
        BLOCK_CONSOLE_CLEAR: true,           // 阻止 console.clear
        PATCH_INNER_HTML: true,              // 拦截 innerHTML 中的反调试代码
        PATCH_REQUEST_ANIMATION_FRAME: true, // 拦截 requestAnimationFrame 中的可疑回调
        EXPOSE_DEBUG_API: true,              // 暴露 __ANTI_DEBUGGER__ 调试 API
    };

    const win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
    const doc = win.document;

    function log(...args) {
        if (CONFIG.DEBUG_LOG) console.log('[Anti-Debugger v3.1]', ...args);
    }

    const NativeStore = {
        Function: win.Function,
        eval: win.eval,
        Blob: win.Blob,
        Worker: win.Worker,
        URL_createObjectURL: win.URL && win.URL.createObjectURL,
        URL_revokeObjectURL: win.URL && win.URL.revokeObjectURL,
        FnToString: Function.prototype.toString,
        setTimeout: win.setTimeout,
        setInterval: win.setInterval,
        appendChild: Element.prototype.appendChild,
        insertBefore: Element.prototype.insertBefore,
        replaceChild: Element.prototype.replaceChild,
        createElement: Document.prototype.createElement,
        write: Document.prototype.write,
        writeln: Document.prototype.writeln,
    };

    // ==================================================================
    // 可序列化纯函数(通过 fn.toString() 注入 Worker,禁止引用闭包变量)
    // ==================================================================

    // [FIX #1] 循环折叠 + 同类引号 + 混合引号("a"+'b')支持
    function tryFoldSimpleConcat(code) {
        if (typeof code !== 'string') return code;

        var dqRe = /"((?:[^"\\]|\\.)*)"\s*\+\s*"((?:[^"\\]|\\.)*)"/g;
        var sqRe = /'((?:[^'\\]|\\.)*)'\s*\+\s*'((?:[^'\\]|\\.)*)'/g;
        var btRe = /`((?:[^`\\]|\\.)*)`\s*\+\s*`((?:[^`\\]|\\.)*)`/g;
        var mxRe = /(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')\s*\+\s*(?:"((?:[^"\\]|\\.)*)"|'((?:[^'\\]|\\.)*)')/g;

        var out = code;
        var prev;
        var rounds = 0;
        do {
            prev = out;
            // 同类引号折叠(保留原始引号风格)
            dqRe.lastIndex = 0;
            out = out.replace(dqRe, function (_, a, b) { return '"' + a + b + '"'; });
            sqRe.lastIndex = 0;
            out = out.replace(sqRe, function (_, a, b) { return "'" + a + b + "'"; });
            btRe.lastIndex = 0;
            out = out.replace(btRe, function (_, a, b) { return '`' + a + b + '`'; });
            // 混合引号折叠(输出双引号,跳过含双引号内容防语法错误)
            mxRe.lastIndex = 0;
            out = out.replace(mxRe, function (m, dq1, sq1, dq2, sq2) {
                // 两侧引号类型相同 → 跳过(已被同类正则处理)
                var leftIsDouble = dq1 != null;
                var rightIsDouble = dq2 != null;
                if (leftIsDouble === rightIsDouble) return m;
                var a = leftIsDouble ? dq1 : (sq1 || '');
                var b = rightIsDouble ? dq2 : (sq2 || '');
                if (a.indexOf('"') >= 0 || b.indexOf('"') >= 0) return m;
                return '"' + a + b + '"';
            });
        } while (out !== prev && ++rounds < 5);
        return out;
    }

    // [FIX #2 prep] 改用 atob(非 win.atob)以便序列化到 Worker
    function tryDecodeBase64Literal(code) {
        if (typeof code !== 'string') return code;
        return code.replace(
            /\batob\s*\(\s*(['"`])([A-Za-z0-9+/=]{8,})\1\s*\)/g,
            function (m, q, b64) {
                try { return JSON.stringify(atob(b64)); }
                catch (e) { return m; }
            }
        );
    }

    // [FIX #2] 只解码可见 ASCII 范围(0x20-0x7E),避免展开控制字符破坏代码
    function tryDecodeEscapes(code) {
        if (typeof code !== 'string') return code;
        return code
            .replace(/\\x([2-7][0-9a-fA-F])/g, function (m, h) {
                var c = parseInt(h, 16);
                return (c >= 0x20 && c <= 0x7e) ? String.fromCharCode(c) : m;
            })
            .replace(/\\u00([2-7][0-9a-fA-F])/g, function (m, h) {
                var c = parseInt(h, 16);
                return (c >= 0x20 && c <= 0x7e) ? String.fromCharCode(c) : m;
            });
    }

    function normalizeCode(code) {
        if (typeof code !== 'string') return code;
        var out = code;
        out = tryFoldSimpleConcat(out);
        out = tryDecodeBase64Literal(out);
        out = tryDecodeEscapes(out);
        return out;
    }

    function stripDebugger(code) {
        if (typeof code !== 'string') return code;
        var out = normalizeCode(code);
        out = out.replace(/\bdebugger\b\s*;?/g, '');
        out = out.replace(/;\s*;/g, ';');
        out = out.replace(/Function\s*\(\s*(['"`])\s*debugger\s*;?\s*\1\s*\)/g, 'Function("")');
        out = out.replace(/new\s+Function\s*\(\s*(['"`])\s*debugger\s*;?\s*\1\s*\)/g, 'new Function("")');
        out = out.replace(/eval\s*\(\s*(['"`])\s*debugger\s*;?\s*\1\s*\)/g, 'eval("")');
        return out;
    }

    // ==================================================================
    // [FIX #4] looksSuspicious 评分制,降低误报率
    // ==================================================================
    function looksSuspicious(code) {
        if (typeof code !== 'string') return false;
        var normalized = normalizeCode(code);

        // 直接命中
        if (/\bdebugger\b/.test(normalized)) return true;

        // 高可疑:Function/eval 参数直接包含 debugger
        if (/Function\s*\(\s*['"`][^)]{0,500}debugger[^)]{0,500}['"`]\s*\)/.test(normalized)) return true;
        if (/eval\s*\(\s*['"`][^)]{0,500}debugger[^)]{0,500}['"`]\s*\)/.test(normalized)) return true;

        // 评分制:至少命中 2 项才标记
        var score = 0;
        if (/new\s+Function\s*\(/.test(normalized)) score++;
        if (/\.constructor\s*\(\s*['"`]/.test(normalized)) score++;
        if (/\beval\s*\(\s*atob\s*\(/.test(normalized)) score++;
        if (/\batob\s*\([\s\S]*(?:debugger|Function|eval)/.test(code)) score++;
        if (/setInterval\s*\(\s*['"`]/.test(normalized)) score++;
        if (/setTimeout\s*\(\s*['"`]/.test(normalized)) score++;

        return score >= 2;
    }

    // ==================================================================
    // 工具函数
    // ==================================================================
    function safeToString(name) {
        return function () {
            return 'function ' + name + '() { [native code] }';
        };
    }

    function patchFunctionLike(targetObj, key, wrapperFactory) {
        var original = targetObj[key];
        if (typeof original !== 'function') return;
        var wrapped = wrapperFactory(original);
        try {
            Object.defineProperty(wrapped, 'toString', {
                configurable: true,
                value: safeToString(key)
            });
        } catch (e) {}
        try {
            Object.defineProperty(targetObj, key, {
                configurable: true,
                writable: true,
                value: wrapped
            });
            log('patched', key);
        } catch (e) {
            log('patch failed:', key, e);
        }
    }

    function patchCodeStringArg(args, indexList) {
        var newArgs = Array.from(args);
        for (var i = 0; i < indexList.length; i++) {
            if (typeof newArgs[indexList[i]] === 'string') {
                newArgs[indexList[i]] = stripDebugger(newArgs[indexList[i]]);
            }
        }
        return newArgs;
    }

    // ==================================================================
    // 主线程基础补丁
    // ==================================================================
    patchFunctionLike(win, 'eval', function (original) {
        return new Proxy(original, {
            apply: function (target, thisArg, args) {
                return Reflect.apply(target, thisArg, patchCodeStringArg(args, [0]));
            }
        });
    });

    (function patchFunctionConstructor() {
        var RawFunction = NativeStore.Function;

        function SafeFunction() {
            var patchedArgs = Array.from(arguments).map(function (arg) {
                return typeof arg === 'string' ? stripDebugger(arg) : arg;
            });
            return RawFunction.apply(this, patchedArgs);
        }
        SafeFunction.prototype = RawFunction.prototype;

        var FunctionProxy = new Proxy(SafeFunction, {
            apply: function (target, thisArg, args) {
                var patchedArgs = args.map(function (arg) {
                    return typeof arg === 'string' ? stripDebugger(arg) : arg;
                });
                return Reflect.apply(RawFunction, thisArg, patchedArgs);
            },
            construct: function (target, args, newTarget) {
                var patchedArgs = args.map(function (arg) {
                    return typeof arg === 'string' ? stripDebugger(arg) : arg;
                });
                return Reflect.construct(RawFunction, patchedArgs, newTarget || RawFunction);
            }
        });

        try {
            Object.defineProperty(FunctionProxy, 'toString', {
                configurable: true,
                value: safeToString('Function')
            });
        } catch (e) {}
        try {
            Object.defineProperty(win, 'Function', {
                configurable: true, writable: true, value: FunctionProxy
            });
        } catch (e) {}
        try {
            Object.defineProperty(RawFunction.prototype, 'constructor', {
                configurable: true, writable: true, value: FunctionProxy
            });
        } catch (e) {}
    })();

    ['setTimeout', 'setInterval'].forEach(function (name) {
        patchFunctionLike(win, name, function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    return Reflect.apply(target, thisArg, patchCodeStringArg(args, [0]));
                }
            });
        });
    });

    if (CONFIG.PATCH_REQUEST_ANIMATION_FRAME) {
        patchFunctionLike(win, 'requestAnimationFrame', function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    var cb = args[0];
                    if (typeof cb === 'function') {
                        try {
                            var src = NativeStore.FnToString.call(cb);
                            if (looksSuspicious(src)) {
                                args[0] = function () {};
                            }
                        } catch (e) {}
                    }
                    return Reflect.apply(target, thisArg, args);
                }
            });
        });
    }

    // 注意:MutationObserver 触发时脚本可能已开始执行,修改 textContent 无法阻止已启动的执行。
    // 但对通过 innerHTML 批量插入的后续 script 节点仍有拦截价值。
    function patchScriptNode(node) {
        if (!node || node.nodeType !== 1 || node.tagName !== 'SCRIPT') return node;
        try {
            if (typeof node.textContent === 'string' && node.textContent) {
                node.textContent = stripDebugger(node.textContent);
            }
        } catch (e) {}
        try {
            if (node.src && typeof node.src === 'string' && node.src.startsWith('javascript:')) {
                node.src = stripDebugger(node.src);
            }
        } catch (e) {}
        return node;
    }

    ['appendChild', 'insertBefore', 'replaceChild'].forEach(function (name) {
        patchFunctionLike(Element.prototype, name, function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    if (args[0]) patchScriptNode(args[0]);
                    return Reflect.apply(target, thisArg, args);
                }
            });
        });
    });

    patchFunctionLike(Document.prototype, 'createElement', function (original) {
        return new Proxy(original, {
            apply: function (target, thisArg, args) {
                var node = Reflect.apply(target, thisArg, args);
                try {
                    if (String(args[0]).toLowerCase() === 'script') {
                        // 注意:此处用实例属性劫持 text/textContent,会脱离 DOM 原型链行为。
                        // 可能与 React/Vue 等框架操作 script 节点时产生细微不一致。
                        var _text = '';
                        var _textContent = '';
                        Object.defineProperty(node, 'text', {
                            configurable: true,
                            get: function () { return _text; },
                            set: function (v) { _text = typeof v === 'string' ? stripDebugger(v) : v; }
                        });
                        Object.defineProperty(node, 'textContent', {
                            configurable: true,
                            get: function () { return _textContent; },
                            set: function (v) { _textContent = typeof v === 'string' ? stripDebugger(v) : v; }
                        });
                    }
                } catch (e) {}
                return node;
            }
        });
    });

    ['write', 'writeln'].forEach(function (name) {
        patchFunctionLike(Document.prototype, name, function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    var newArgs = args.map(function (arg) {
                        return typeof arg === 'string' ? stripDebugger(arg) : arg;
                    });
                    return Reflect.apply(target, thisArg, newArgs);
                }
            });
        });
    });

    if (CONFIG.PATCH_INNER_HTML) {
        try {
            var rawInnerHTMLDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
            if (rawInnerHTMLDescriptor && rawInnerHTMLDescriptor.set) {
                var rawSetter = rawInnerHTMLDescriptor.set;
                var rawGetter = rawInnerHTMLDescriptor.get;
                Object.defineProperty(Element.prototype, 'innerHTML', {
                    configurable: true,
                    get: function () { return rawGetter.call(this); },
                    set: function (v) {
                        if (typeof v === 'string' && looksSuspicious(v)) {
                            v = v.replace(
                                /(<script[^>]*>)([\s\S]*?)(<\/script>)/gi,
                                function (m, open, body, close) {
                                    return open + stripDebugger(body) + close;
                                }
                            );
                        }
                        return rawSetter.call(this, v);
                    }
                });
            }
        } catch (e) {}
    }

    // ==================================================================
    // Blob / URL / Worker 主链增强
    // ==================================================================
    var blobToMeta = new WeakMap();
    var urlToMeta = new Map();
    var wrappedWorkerUrlSet = new Set();

    function detectJsMime(type) {
        return /javascript|ecmascript|json|text\/plain|application\/octet-stream/i.test(type || '');
    }

    // [FIX #6] Worker bootstrap 使用 fn.toString() 序列化,消除代码重复
    // workerPatchBody 是一个永远不在主线程调用的函数,仅取 toString() 注入到 Worker
    // 它假定 stripDebugger 和 __config 变量已在 Worker 作用域中定义
    function workerPatchBody() {
        /* eslint-disable no-undef */
        var __strip = stripDebugger;

        function __patchCodeArgs(args, idxs) {
            var out = Array.prototype.slice.call(args);
            for (var i = 0; i < idxs.length; i++) {
                if (typeof out[idxs[i]] === 'string') out[idxs[i]] = __strip(out[idxs[i]]);
            }
            return out;
        }

        try {
            var __rawEval = self.eval;
            self.eval = new Proxy(__rawEval, {
                apply: function (target, thisArg, args) {
                    return Reflect.apply(target, thisArg, __patchCodeArgs(args, [0]));
                }
            });
        } catch (e) {}

        try {
            var __RawFunction = self.Function;
            function __SafeFunction() {
                var a = Array.prototype.slice.call(arguments).map(function (x) {
                    return typeof x === 'string' ? __strip(x) : x;
                });
                return __RawFunction.apply(this, a);
            }
            __SafeFunction.prototype = __RawFunction.prototype;
            var __FP = new Proxy(__SafeFunction, {
                apply: function (target, thisArg, args) {
                    args = args.map(function (x) { return typeof x === 'string' ? __strip(x) : x; });
                    return Reflect.apply(__RawFunction, thisArg, args);
                },
                construct: function (target, args, newTarget) {
                    args = args.map(function (x) { return typeof x === 'string' ? __strip(x) : x; });
                    return Reflect.construct(__RawFunction, args, newTarget || __RawFunction);
                }
            });
            self.Function = __FP;
            __RawFunction.prototype.constructor = __FP;
        } catch (e) {}

        // [FIX #3] importScripts 激进拦截:XHR 同步读取 + __strip + eval
        // Worker 中同步 XHR 不受主线程限制,比主线程可靠得多
        try {
            var __rawImportScripts = self.importScripts;
            if (__config.AGGRESSIVE_IMPORT_SCRIPTS) {
                self.importScripts = function () {
                    var urls = Array.prototype.slice.call(arguments);
                    for (var i = 0; i < urls.length; i++) {
                        try {
                            var xhr = new XMLHttpRequest();
                            xhr.open('GET', urls[i], false);
                            xhr.send();
                            if (xhr.status >= 200 && xhr.status < 300) {
                                var code = __strip(xhr.responseText || '');
                                (0, eval)(code);
                                continue;
                            }
                        } catch (e) {}
                        // XHR 失败(跨域等)回退原生 importScripts
                        __rawImportScripts.call(self, urls[i]);
                    }
                };
            }
            // 非激进模式不做任何处理,保持原生 importScripts
        } catch (e) {}

        try {
            var __rawSetTimeout = self.setTimeout;
            self.setTimeout = new Proxy(__rawSetTimeout, {
                apply: function (target, thisArg, args) {
                    return Reflect.apply(target, thisArg, __patchCodeArgs(args, [0]));
                }
            });
        } catch (e) {}

        try {
            var __rawSetInterval = self.setInterval;
            self.setInterval = new Proxy(__rawSetInterval, {
                apply: function (target, thisArg, args) {
                    return Reflect.apply(target, thisArg, __patchCodeArgs(args, [0]));
                }
            });
        } catch (e) {}
        /* eslint-enable no-undef */
    }

    function buildWorkerBootstrap(userCode) {
        // 序列化纯函数到 Worker
        var serializableFns = [
            tryFoldSimpleConcat,
            tryDecodeBase64Literal,
            tryDecodeEscapes,
            normalizeCode,
            stripDebugger
        ];
        var parts = [];
        parts.push('(function(){');
        parts.push('"use strict";');
        parts.push('var __config = ' + JSON.stringify({
            AGGRESSIVE_IMPORT_SCRIPTS: CONFIG.AGGRESSIVE_IMPORT_SCRIPTS
        }) + ';');
        for (var i = 0; i < serializableFns.length; i++) {
            parts.push(serializableFns[i].toString() + ';');
        }
        // 提取 workerPatchBody 的函数体(去掉外层 function 声明)
        var patchSrc = workerPatchBody.toString();
        var bodyMatch = patchSrc.match(/^[^{]*\{([\s\S]*)\}\s*$/);
        parts.push(bodyMatch ? bodyMatch[1] : patchSrc);
        parts.push('})();');

        return parts.join('\n') + '\n\n' + userCode;
    }

    function makeWrappedWorkerUrlFromCode(code, mimeType) {
        var cleanUserCode = stripDebugger(code);
        var wrappedCode = buildWorkerBootstrap(cleanUserCode);
        var blob = new NativeStore.Blob([wrappedCode], {
            type: mimeType || 'application/javascript'
        });
        var url = NativeStore.URL_createObjectURL.call(win.URL, blob);
        wrappedWorkerUrlSet.add(url);
        return url;
    }

    // [FIX #7] SafeBlob 去除双重清理:只对各 part 清理一次,不再 join 后再清理
    if (typeof NativeStore.Blob === 'function') {
        var RawBlob = NativeStore.Blob;

        function SafeBlob(parts, options) {
            var originalCode = null;
            var cleanedCode = null;
            var type = options && options.type ? options.type : '';

            try {
                if (Array.isArray(parts)) {
                    var rawTextParts = [];
                    var cleanTextParts = [];
                    var sanitizedParts = parts.map(function (part) {
                        if (typeof part === 'string') {
                            rawTextParts.push(part);
                            var cleaned = stripDebugger(part);
                            cleanTextParts.push(cleaned);
                            return cleaned;
                        }
                        return part;
                    });
                    var joinedRaw = rawTextParts.join('\n');
                    if (joinedRaw) {
                        originalCode = joinedRaw;
                        cleanedCode = cleanTextParts.join('\n');
                        parts = sanitizedParts;
                    }
                }
            } catch (e) {
                log('SafeBlob sanitize failed:', e);
            }

            var blob = new RawBlob(parts, options);

            try {
                if (originalCode !== null && (looksSuspicious(originalCode) || detectJsMime(type))) {
                    blobToMeta.set(blob, {
                        originalCode: originalCode,
                        cleanCode: cleanedCode,
                        type: type
                    });
                }
            } catch (e) {}

            return blob;
        }

        SafeBlob.prototype = RawBlob.prototype;
        try {
            Object.defineProperty(SafeBlob, 'toString', {
                configurable: true, value: safeToString('Blob')
            });
        } catch (e) {}
        try {
            Object.defineProperty(win, 'Blob', {
                configurable: true, writable: true, value: SafeBlob
            });
        } catch (e) {}
    }

    if (win.URL && typeof NativeStore.URL_createObjectURL === 'function') {
        patchFunctionLike(win.URL, 'createObjectURL', function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    var blob = args[0];
                    var url = Reflect.apply(target, thisArg, args);
                    try {
                        var meta = blobToMeta.get(blob);
                        if (meta) {
                            urlToMeta.set(url, meta);
                            // 防止长时间运行页面内存泄漏
                            if (urlToMeta.size > 1000) {
                                var oldest = urlToMeta.keys().next().value;
                                urlToMeta.delete(oldest);
                            }
                            log('tracked blob url', url);
                        }
                    } catch (e) {}
                    return url;
                }
            });
        });
    }

    if (win.URL && typeof NativeStore.URL_revokeObjectURL === 'function') {
        patchFunctionLike(win.URL, 'revokeObjectURL', function (original) {
            return new Proxy(original, {
                apply: function (target, thisArg, args) {
                    var url = args[0];
                    try {
                        if (typeof url === 'string') {
                            urlToMeta.delete(url);
                            wrappedWorkerUrlSet.delete(url);
                        }
                    } catch (e) {}
                    return Reflect.apply(target, thisArg, args);
                }
            });
        });
    }

    function tryReadSameOriginScript(url) {
        try {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, false);
            xhr.send();
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
                return xhr.responseText || '';
            }
        } catch (e) {}
        return null;
    }

    if (typeof NativeStore.Worker === 'function') {
        patchFunctionLike(win, 'Worker', function (original) {
            return new Proxy(original, {
                construct: function (target, args, newTarget) {
                    var url = args[0];
                    try {
                        if (typeof url === 'string' && url.startsWith('javascript:')) {
                            args[0] = stripDebugger(url);
                            return Reflect.construct(target, args, newTarget);
                        }

                        // 1) blob: 且来自已追踪的 createObjectURL
                        if (typeof url === 'string' && url.startsWith('blob:')) {
                            var meta = urlToMeta.get(url);
                            if (meta && meta.cleanCode != null) {
                                args[0] = makeWrappedWorkerUrlFromCode(meta.cleanCode, meta.type);
                                log('worker wrapped from tracked blob url');
                                return Reflect.construct(target, args, newTarget);
                            }
                            // 2) 兜底:尽量读回 blob: 内容
                            var blobCode = tryReadSameOriginScript(url);
                            if (typeof blobCode === 'string' && blobCode) {
                                args[0] = makeWrappedWorkerUrlFromCode(blobCode, 'application/javascript');
                                log('worker wrapped from fallback-read blob url');
                                return Reflect.construct(target, args, newTarget);
                            }
                        }

                        // [FIX #5] 3) 普通同源 js 文件,附加日志说明跨域失败情况
                        if (typeof url === 'string' && /^(https?:|\/|\.\/|\.\.\/)/.test(url)) {
                            var srcCode = tryReadSameOriginScript(url);
                            if (typeof srcCode === 'string' && srcCode) {
                                args[0] = makeWrappedWorkerUrlFromCode(srcCode, 'application/javascript');
                                log('worker wrapped from same-origin script');
                                return Reflect.construct(target, args, newTarget);
                            }
                            // 跨域或读取失败:直接放行,不阻塞不报错
                            log('worker script not interceptable (likely cross-origin):', url);
                        }
                    } catch (e) {
                        log('Worker wrap failed:', e);
                    }

                    return Reflect.construct(target, args, newTarget);
                }
            });
        });
    }

    // ==================================================================
    // MutationObserver
    // ==================================================================
    try {
        var mo = new MutationObserver(function (mutations) {
            for (var i = 0; i < mutations.length; i++) {
                var addedNodes = mutations[i].addedNodes;
                for (var j = 0; j < addedNodes.length; j++) {
                    try {
                        patchScriptNode(addedNodes[j]);
                        if (addedNodes[j] && addedNodes[j].querySelectorAll) {
                            var scripts = addedNodes[j].querySelectorAll('script');
                            for (var k = 0; k < scripts.length; k++) patchScriptNode(scripts[k]);
                        }
                    } catch (e) {}
                }
            }
        });

        var startObserve = function () {
            try {
                mo.observe(doc.documentElement || doc, { childList: true, subtree: true });
            } catch (e) {}
        };

        if (doc.documentElement) startObserve();
        else doc.addEventListener('DOMContentLoaded', startObserve, { once: true });
    } catch (e) {}

    // [FIX #8] console.clear:修复未使用的 rawClear,增加 CONFIG 开关
    if (CONFIG.BLOCK_CONSOLE_CLEAR) {
        try {
            var rawClear = console.clear;
            console.clear = function () {
                log('console.clear blocked');
                // 如需恢复原始行为,取消下面注释:
                // rawClear.apply(console, arguments);
            };
            try { console.clear.toString = safeToString('clear'); } catch (e) {}
        } catch (e) {}
    }

    // 调试 API
    if (CONFIG.EXPOSE_DEBUG_API) {
        try {
            win.__ANTI_DEBUGGER__ = {
                version: '1.3.0',
                config: CONFIG,
                stripDebugger: stripDebugger,
                looksSuspicious: looksSuspicious,
                dumpTrackedBlobUrls: function () {
                    return Array.from(urlToMeta.keys());
                },
                dumpWrappedWorkerUrls: function () {
                    return Array.from(wrappedWorkerUrlSet);
                },
                test: function () {
                    console.log('Anti-Debugger v1.3 active');
                    return true;
                }
            };
        } catch (e) {}
    }

    log('Anti Anti-Debugger Enhanced v1.3 loaded');
})();