Greasy Fork

Greasy Fork is available in English.

开发工具限制绕过

绕过网站对开发工具的限制,具有增强的保护功能

当前为 2025-06-10 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         DevTools Bypass
// @name:vi      Bỏ Qua Chặn DevTools
// @name:zh-CN   开发工具限制绕过
// @name:ru      Разблокировка DevTools
// @namespace    http://greasyfork.icu/vi/users/1195312-renji-yuusei
// @version      2025.06.10.1
// @description  Bypass for website restrictions on DevTools with enhanced protection
// @description:vi Bỏ qua các hạn chế của trang web về DevTools với bảo vệ nâng cao
// @description:zh-CN 绕过网站对开发工具的限制,具有增强的保护功能
// @description:ru   Разблокировка DevTools с усиленной защитой
// @author       Yuusei
// @match        *://*/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      GPL-3.0-only
// ==/UserScript==

(() => {
  'use strict';

  // Constants
  const CONSTANTS = {
    PREFIX: '[DevTools Bypass]',
    LOG_LEVELS: {
      INFO: 'info',
      WARN: 'warn', 
      ERROR: 'error',
      DEBUG: 'debug'
    },
    TIME_THRESHOLDS: {
      DEBUGGER: 80,
      CACHE: 30000
    },
    CACHE_TTL: {
        DEBUGGER_CHECK: 500
    }
  };

  // Configuration
  const config = {
    antiDebugRegex: new RegExp([
        // debugger, debug(), etc.
        /[;\s]*(?:debugger|debug(?:ger)?|breakpoint)[\s;]*/,
        // new Function('debugger')(), etc.
        /(?:eval|Function|setTimeout|setInterval)\s*\(\s*['"`].*?debugger.*?['"`]\s*\)/,
        // devtools checks
        /(?:isDevTools?|devtools?|debugMode|debug_enabled)\s*[=:]\s*(?:true|1|!0|yes)/,
        // console checks
        /console\.(?:log|warn|error|info|debug|trace|dir|table)/,
        // source map urls
        /\/\/[#@]\s*source(?:Mapping)?URL\s*=.*/,
        // Known anti-debug library patterns
        /FuckDevTools|devtools-detector/
    ].map(r => r.source).join('|'), 'gi'),

    consoleProps: ['log', 'warn', 'error', 'info', 'debug', 'trace', 'dir', 'dirxml', 'table', 'profile', 'group', 'groupEnd', 'time', 'timeEnd'],
    cutoffs: {
      debugger: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE },
      debuggerThrow: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE }
    },
    bypassTriggers: {
      timeThreshold: CONSTANTS.TIME_THRESHOLDS.DEBUGGER,
      stackDepth: 30,
      recursionLimit: 50
    },
    debuggerDetector: {
        cacheTTL: CONSTANTS.CACHE_TTL.DEBUGGER_CHECK,
        historyCleanupInterval: 60000 // 1 minute
    },
    logging: {
      enabled: true,
      prefix: CONSTANTS.PREFIX,
      levels: Object.values(CONSTANTS.LOG_LEVELS),
      detailedErrors: true,
      monitorAPI: false,
      monitorDOM: false
    },
    protection: {
      preventDevToolsKeys: true,
      hideStackTraces: true,
      sanitizeErrors: true,
      obfuscateTimers: true,
      preventRightClick: true,
      preventViewSource: true,
      preventCopy: true,
      preventPaste: true,
      preventPrint: true,
      preventSave: true
    }
  };

  // Logger class
  class Logger {
    static #instance;
    #lastLog = 0;
    #logCount = 0;
    #logBuffer = [];

    constructor() {
      if (Logger.#instance) {
        return Logger.#instance;
      }
      Logger.#instance = this;
      this.#setupBufferFlush();
    }

    #setupBufferFlush() {
      setInterval(() => {
        if (this.#logBuffer.length) {
          this.#flushBuffer();
        }
      }, 1000);
    }

    #flushBuffer() {
      this.#logBuffer.forEach(({level, args}) => {
        console[level](config.logging.prefix, ...args);
      });
      this.#logBuffer = [];
    }

    #shouldLog() {
      const now = Date.now();
      if (now - this.#lastLog > 1000) {
        this.#logCount = 0;
      }
      this.#lastLog = now;
      return ++this.#logCount <= 10;
    }

    #log(level, ...args) {
      if (!config.logging.enabled || !this.#shouldLog()) return;
      this.#logBuffer.push({ level, args });
    }

    info(...args) { this.#log(CONSTANTS.LOG_LEVELS.INFO, ...args); }
    warn(...args) { this.#log(CONSTANTS.LOG_LEVELS.WARN, ...args); }
    error(...args) { this.#log(CONSTANTS.LOG_LEVELS.ERROR, ...args); }
    debug(...args) { this.#log(CONSTANTS.LOG_LEVELS.DEBUG, ...args); }
  }

  // Original functions store
  const OriginalFunctions = {
    defineProperty: Object.defineProperty,
    getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor,
    setTimeout: window.setTimeout,
    setInterval: window.setInterval,
    Date: window.Date,
    now: Date.now,
    performance: window.performance,
    Function: window.Function,
    eval: window.eval,
    console: {},
    toString: Function.prototype.toString,
    preventDefault: Event.prototype.preventDefault,
    getComputedStyle: window.getComputedStyle,
    addEventListener: window.addEventListener,
    removeEventListener: window.removeEventListener,
    fetch: window.fetch,
    XMLHttpRequest: window.XMLHttpRequest,

    initConsole() {
      config.consoleProps.forEach(prop => {
        if (console[prop]) {
          this.console[prop] = console[prop].bind(console);
        }
      });
    }
  };

  OriginalFunctions.initConsole();

  // Debugger detector
  class DebuggerDetector {
    static #detectionCache = new Map();
    static #detectionHistory = [];
    static #historyCleanupTimer = null;

    static isPresent() {
      try {
        const cacheKey = 'debugger_check';
        const cached = this.#detectionCache.get(cacheKey);
        if (cached && Date.now() - cached.timestamp < config.debuggerDetector.cacheTTL) {
          return cached.result;
        }

        const startTime = OriginalFunctions.now.call(Date);
        new Function('debugger;')();
        const timeDiff = OriginalFunctions.now.call(Date) - startTime;

        const result = timeDiff > config.bypassTriggers.timeThreshold;
        this.#detectionCache.set(cacheKey, {
          result,
          timestamp: Date.now()
        });

        this.#detectionHistory.push({
          timestamp: Date.now(),
          result,
          timeDiff
        });

        // Keep history for 5 minutes
        const fiveMinutesAgo = Date.now() - 300000;
        this.#detectionHistory = this.#detectionHistory.filter(entry => entry.timestamp > fiveMinutesAgo);

        return result;
      } catch {
        return false;
      }
    }

    static analyzeStack() {
      try {
        const stack = new Error().stack;
        if (!stack) return { depth: 0, hasDebugKeywords: false, isRecursive: false, suspiciousPatterns: [], stackHash: '' };
        
        const frames = stack.split('\n');
        const uniqueFrames = new Set(frames);

        return {
          depth: frames.length,
          hasDebugKeywords: config.antiDebugRegex.test(stack),
          isRecursive: uniqueFrames.size < frames.length,
          suspiciousPatterns: this.#detectSuspiciousPatterns(stack),
          stackHash: this.#generateStackHash(stack)
        };
      } catch {
        return {
          depth: 0,
          hasDebugKeywords: false,
          isRecursive: false,
          suspiciousPatterns: [],
          stackHash: ''
        };
      }
    }

    static #detectSuspiciousPatterns(stack) {
      const patterns = [
        /eval.*?\(/g,
        /Function.*?\(/g,
        /debugger/g,
        /debug/g,
        /DevTools/g,
        /console\./g,
        /chrome-extension/g
      ];
      return patterns.filter(pattern => pattern.test(stack));
    }

    static #generateStackHash(stack) {
      return Array.from(stack).reduce((hash, char) => {
        hash = ((hash << 5) - hash) + char.charCodeAt(0);
        return hash & hash;
      }, 0).toString(36);
    }

    static getDetectionStats() {
      const now = Date.now();
      const recentDetections = this.#detectionHistory.filter(entry => 
        entry.timestamp > now - 60000
      );

      return {
        total: recentDetections.length,
        positive: recentDetections.filter(entry => entry.result).length,
        averageTime: recentDetections.reduce((acc, curr) => 
          acc + curr.timeDiff, 0) / (recentDetections.length || 1)
      };
    }

    static startHistoryCleanup() {
        if (this.#historyCleanupTimer) return;
        this.#historyCleanupTimer = setInterval(() => {
            const fiveMinutesAgo = Date.now() - 300000;
            this.#detectionHistory = this.#detectionHistory.filter(entry => entry.timestamp > fiveMinutesAgo);
        }, config.debuggerDetector.historyCleanupInterval);
    }
  }

  // Protection class
  class Protection {
    static #combinedPattern = null;
    
    static applyAll() {
      this.#patchGlobalDebugVariables();
      this.#protectTimers();
      this.#protectTiming();
      this.#protectFunction();
      this.#protectStack();
      this.#protectEval();
      this.#protectConsole();
      this.#setupMutationObserver();
      this.#protectDeveloperKeys();
      this.#protectRightClick();
      this.#protectNetwork();
      this.#protectStorage();
      this.#protectClipboard();
      this.#protectPrinting();
      this.#protectWebWorkers();
      this.#applyDeveloperHelpers();
    }

    static #protectTimers() {
      const wrapTimer = original => {
        return function(handler, timeout, ...args) {
          if (typeof handler !== 'function') {
            return original.apply(this, arguments);
          }

          const wrappedHandler = function() {
            try {
              if (DebuggerDetector.isPresent()) return;
              return handler.apply(this, arguments);
            } catch (e) {
              if (e.message?.includes('debugger')) return;
              throw e;
            }
          };

          if (config.protection.obfuscateTimers) {
            timeout = Math.max(1, timeout + (Math.random() * 20 - 10));
          }

          return original.call(this, wrappedHandler, timeout, ...args);
        };
      };

      window.setTimeout = wrapTimer(OriginalFunctions.setTimeout);
      window.setInterval = wrapTimer(OriginalFunctions.setInterval);
    }

    static #protectTiming() {
      const timeOffset = Math.random() * 25;
      const safeNow = () => OriginalFunctions.now.call(Date) + timeOffset;

      Object.defineProperty(Date, 'now', {
        value: safeNow,
        configurable: false,
        writable: false
      });

      if (window.performance?.now) {
        Object.defineProperty(window.performance, 'now', {
          value: safeNow,
          configurable: false,
          writable: false
        });
      }
    }

    static #protectFunction() {
      const handler = {
        apply(target, thisArg, args) {
          if (typeof args[0] === 'string') {
            args[0] = Protection.#cleanCode(args[0]);
          }
          return Reflect.apply(target, thisArg, args);
        },
        construct(target, args) {
          if (typeof args[0] === 'string') {
            args[0] = Protection.#cleanCode(args[0]);
          }
          return Reflect.construct(target, args);
        }
      };

      window.Function = new Proxy(OriginalFunctions.Function, handler);
      if (typeof unsafeWindow !== 'undefined') {
        unsafeWindow.Function = window.Function;
      }
    }

    static #protectStack() {
      if (!config.protection.hideStackTraces) return;

      // V8-specific API for stack trace customization
      if ('prepareStackTrace' in Error) {
        Error.prepareStackTrace = (error, stack) => {
          const stackString = [error.toString(), ...stack.map(frame => `    at ${frame}`)].join('\n');
          return Protection.#cleanCode(stackString);
        };
        return;
      }
      
      // Standard-based approach
      try {
        const originalStackDescriptor = Object.getOwnPropertyDescriptor(Error.prototype, 'stack');
        if (originalStackDescriptor?.get) {
          Object.defineProperty(Error.prototype, 'stack', {
            get() {
              const originalStack = originalStackDescriptor.get.call(this);
              return Protection.#cleanCode(originalStack);
            },
            configurable: true,
          });
        }
      } catch (e) {
        logger.error('Failed to protect stack traces:', e);
      }
    }

    static #protectEval() {
      const safeEval = function(code) {
        if (typeof code === 'string') {
          if (DebuggerDetector.isPresent()) return;
          return OriginalFunctions.eval.call(this, Protection.#cleanCode(code));
        }
        return OriginalFunctions.eval.apply(this, arguments);
      };

      Object.defineProperty(window, 'eval', {
        value: safeEval,
        configurable: false,
        writable: false
      });

      if (typeof unsafeWindow !== 'undefined') {
        unsafeWindow.eval = safeEval;
      }
    }

    static #protectConsole() {
      const consoleHandler = {
        get(target, prop) {
          if (!config.consoleProps.includes(prop)) return target[prop];

          return function(...args) {
            if (DebuggerDetector.isPresent()) return;
            return OriginalFunctions.console[prop]?.apply(console, args);
          };
        },
        set(target, prop, value) {
          if (config.consoleProps.includes(prop)) return true;
          target[prop] = value;
          return true;
        }
      };

      window.console = new Proxy(console, consoleHandler);
    }

    static #setupMutationObserver() {
      new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          mutation.addedNodes.forEach(node => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              // Clean script content
              if (node.tagName === 'SCRIPT') {
                const originalContent = node.textContent;
                const cleanedContent = Protection.#cleanCode(originalContent);
                if (originalContent !== cleanedContent) {
                  node.textContent = cleanedContent;
                }
              }
              // Clean attributes
              for (const attr of node.attributes) {
                if (attr.name.startsWith('on') && Protection.#cleanCode(attr.value) !== attr.value) {
                    node.removeAttribute(attr.name);
                }
              }
            }
          });
        });
      }).observe(document.documentElement, {
        childList: true,
        subtree: true,
      });
    }

    static #protectDeveloperKeys() {
      if (!config.protection.preventDevToolsKeys && !config.protection.preventViewSource) return;

      const handler = e => {
        const key = e.key.toUpperCase();
        const ctrl = e.ctrlKey;
        const shift = e.shiftKey;
        const alt = e.altKey;

        const isDevToolsKey =
          key === 'F12' ||
          (ctrl && shift && (key === 'I' || key === 'J' || key === 'C')) ||
          (ctrl && key === 'U');
        
        if (isDevToolsKey) {
          e.preventDefault();
          e.stopPropagation();
        }
      };

      window.addEventListener('keydown', handler, true);
    }

    static #protectRightClick() {
      if (!config.protection.preventRightClick) return;

      window.addEventListener('contextmenu', e => {
        e.preventDefault();
        e.stopPropagation();
        return false;
      }, true);
    }

    static #protectNetwork() {
      window.fetch = async function(...args) {
        if (DebuggerDetector.isPresent()) {
          throw new Error('Network request blocked');
        }
        return OriginalFunctions.fetch.apply(this, args);
      };

      window.XMLHttpRequest = function() {
        const xhr = new OriginalFunctions.XMLHttpRequest();
        const originalOpen = xhr.open;
        xhr.open = function(...args) {
          if (DebuggerDetector.isPresent()) {
            throw new Error('Network request blocked');
          }
          return originalOpen.apply(xhr, args);
        };
        return xhr;
      };
    }

    static #protectStorage() {
      const storageHandler = {
        get(target, prop) {
          if (DebuggerDetector.isPresent()) return null;
          return target[prop];
        },
        set(target, prop, value) {
          if (DebuggerDetector.isPresent()) return true;
          target[prop] = value;
          return true;
        }
      };

      window.localStorage = new Proxy(window.localStorage, storageHandler);
      window.sessionStorage = new Proxy(window.sessionStorage, storageHandler);
    }

    static #protectClipboard() {
      const events = [];
      if (config.protection.preventCopy) events.push('copy', 'cut');
      if (config.protection.preventPaste) events.push('paste');

      if (events.length) {
        events.forEach(eventName => {
          document.addEventListener(eventName, e => {
            e.preventDefault();
            e.stopPropagation();
          }, true);
        });
      }
    }

    static #protectPrinting() {
      if (!config.protection.preventPrint) return;

      window.addEventListener('beforeprint', e => {
        e.preventDefault();
      }, true);

      window.addEventListener('afterprint', e => {
        e.preventDefault();
      }, true);
    }

    static #protectWebWorkers() {
      window.Worker = function(scriptURL, options) {
        console.log('[Worker Created]', scriptURL);
        return new OriginalFunctions.Worker(scriptURL, options);
      };
    }

    static #applyDeveloperHelpers() {
      if (config.logging.monitorAPI) {
        this.#monitorAPICalls();
      }
      if (config.logging.monitorDOM) {
        this.#monitorDOMEvents();
      }
    }
    
    static #patchGlobalDebugVariables() {
        const noop = () => {};
        Object.defineProperties(window, {
            'debug': { value: noop, configurable: false, writable: false },
            'debugger': { value: noop, configurable: false, writable: false },
            'isDebuggerEnabled': { value: false, configurable: false, writable: false }
        });
    }
    
    static #monitorAPICalls() {
      const originalFetch = window.fetch;
      window.fetch = async (...args) => {
        logger.debug('[API Call]', ...args);
        return originalFetch.apply(this, args);
      };
    }

    static #monitorDOMEvents() {
      new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          logger.debug('[DOM Change]', mutation);
        });
      }).observe(document.documentElement, {
        childList: true,
        subtree: true,
        attributes: true,
        characterData: true
      });
    }

    static #cleanCode(code) {
      if (typeof code !== 'string') return code;
      return code.replace(config.antiDebugRegex, '');
    }
  }
  // Main class
  class DevToolsBypass {
    static init() {
      try {
        DebuggerDetector.startHistoryCleanup();
        Protection.applyAll();
        logger.info('DevTools Bypass initialized successfully');
      } catch (e) {
        logger.error('Failed to initialize DevTools Bypass:', e);
      }
    }
  }

  // Initialize
  DevToolsBypass.init();
})();