DevTools Bypass

Bypass for website restrictions on DevTools with enhanced protection

目前為 2024-12-23 提交的版本,檢視 最新版本

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         DevTools Bypass
// @name:vi      Bỏ Qua Chặn DevTools
// @name:zh-CN   开发工具限制绕过
// @name:ru      Разблокировка DevTools
// @namespace    https://greasyfork.org/vi/users/1195312-renji-yuusei
// @version      2024.12.23.2
// @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
    }
  };

  // Configuration
  const config = {
    debugPatterns: {
      basic: /[;\s]*(?:debugger|debug(?:ger)?|breakpoint|console\.[a-z]+)[\s;]*/gi,
      advanced: /(?:debugger|debug(?:ger)?|breakpoint|devtools?)(?:\s*\{[\s\S]*?\}|\s*\([^)]*\)|\s*;|\s*$)/gi,
      timing: /(?:performance(?:\.timing)?|Date)\.(?:now|getTime)\(\)|new\s+Date(?:\s*\(\s*\))?\s*\.getTime\(\)/gi,
      eval: /(?:eval|Function|setTimeout|setInterval)\s*\(\s*(?:[`'"].*?(?:debugger|debug|breakpoint).*?[`'"]|\{[\s\S]*?(?:debugger|debug|breakpoint)[\s\S]*?\})\s*\)/gi,
      devtools: /(?:isDevTools?|devtools?|debugMode|debug_mode|debugEnabled)\s*[=:]\s*(?:true|1|!0|yes)/gi,
      consoleCheck: /console\.(?:log|warn|error|info|debug|trace|dir|table)\s*\([^)]*\)/gi,
      functionDebug: /function\s*[^(]*\([^)]*\)\s*\{(?:\s|.)*?(?:debugger|debug|breakpoint)(?:\s|.)*?\}/gi,
      sourceMap: /\/\/[#@]\s*source(?:Mapping)?URL\s*=\s*(?:data:|https?:)?\/\/.*?$/gm,
      debugStrings: /(['"`])(?:(?!\1).)*?(?:debug|debugger|breakpoint|devtools?)(?:(?!\1).)*?\1/gi,
      debugComments: /\/\*[\s\S]*?(?:debug|debugger|breakpoint|devtools?)[\s\S]*?\*\/|\/\/.*(?:debug|debugger|breakpoint|devtools?).*$/gim
    },
    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
    },
    logging: {
      enabled: true,
      prefix: CONSTANTS.PREFIX,
      levels: Object.values(CONSTANTS.LOG_LEVELS),
      detailedErrors: true
    },
    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 isPresent() {
      try {
        const cacheKey = 'debugger_check';
        const cached = this.#detectionCache.get(cacheKey);
        if (cached && Date.now() - cached.timestamp < 500) {
          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;
        const frames = stack.split('\n');
        const uniqueFrames = new Set(frames);

        return {
          depth: frames.length,
          hasDebugKeywords: frames.some(frame => 
            Object.values(config.debugPatterns).some(pattern => pattern.test(frame))
          ),
          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 || 0
      };
    }
  }

  // Helper functions
  const BypassHelpers = {
    disableAntiDebugging: {
      patchTimingChecks() {
        const originalNow = Date.now;
        Date.now = function() {
          return originalNow.call(this) - Math.random() * 100;
        };
      },

      patchStackTraces() {
        Error.prepareStackTrace = (_, stack) => 
          stack.filter(frame => !frame.toString().includes('debugger'));
      },

      patchDebugChecks() {
        const noop = () => {};
        window.debug = noop;
        window.debugger = noop;
        window.isDebuggerEnabled = false;
      }
    },

    debugTools: {
      monitorAPICalls() {
        const originalFetch = window.fetch;
        window.fetch = async (...args) => {
          console.log('[API Call]', ...args);
          return originalFetch.apply(this, args);
        };
      },

      monitorDOMEvents() {
        new MutationObserver(mutations => {
          mutations.forEach(mutation => {
            console.log('[DOM Change]', mutation);
          });
        }).observe(document.body, {
          childList: true,
          subtree: true
        });
      }
    }
  };

  // Protection class
  class Protection {
    static applyAll() {
      this.#protectTimers();
      this.#protectTiming();
      this.#protectFunction();
      this.#protectStack();
      this.#protectEval();
      this.#protectConsole();
      this.#setupMutationObserver();
      this.#protectDevToolsKeys();
      this.#protectRightClick();
      this.#protectViewSource();
      this.#protectNetwork();
      this.#protectStorage();
      this.#protectClipboard();
      this.#protectPrinting();
      this.#protectWebWorkers();
      this.#enableDebuggingHelpers();
    }

    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;

      const errorHandler = {
        get(target, prop) {
          if (prop === 'stack') {
            return Protection.#cleanCode(target.stack);
          }
          return target[prop];
        }
      };

      const originalErrorPrototype = Error.prototype;
      const proxyErrorPrototype = Object.create(originalErrorPrototype);

      Object.defineProperty(proxyErrorPrototype, 'stack', {
        get() {
          return Protection.#cleanCode(new Error().stack);
        },
        configurable: true
      });

      try {
        Error.prototype = proxyErrorPrototype;
      } 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 => {
          if (mutation.type === 'childList') {
            mutation.addedNodes.forEach(node => {
              if (node.tagName === 'SCRIPT') {
                const originalContent = node.textContent;
                const cleanedContent = Protection.#cleanCode(originalContent);
                if (originalContent !== cleanedContent) {
                  node.textContent = cleanedContent;
                }
              }
            });
          }
        });
      }).observe(document, {
        childList: true,
        subtree: true
      });
    }

    static #protectDevToolsKeys() {
      if (!config.protection.preventDevToolsKeys) return;

      const handler = e => {
        const { keyCode, ctrlKey, shiftKey, altKey } = e;
        if (
          keyCode === 123 || // F12
          (ctrlKey && shiftKey && keyCode === 73) || // Ctrl+Shift+I
          (ctrlKey && shiftKey && keyCode === 74) || // Ctrl+Shift+J
          (ctrlKey && keyCode === 85) || // Ctrl+U
          (altKey && keyCode === 68) // Alt+D
        ) {
          e.preventDefault();
          e.stopPropagation();
          return false;
        }
      };

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

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

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

    static #protectViewSource() {
      if (!config.protection.preventViewSource) return;

      const handler = e => {
        if (
          (e.ctrlKey && (e.key === 'u' || e.key === 's')) || // Ctrl+U, Ctrl+S
          (e.ctrlKey && e.shiftKey && e.key === 'i') || // Ctrl+Shift+I
          (e.ctrlKey && e.shiftKey && e.key === 'j') || // Ctrl+Shift+J
          (e.ctrlKey && e.shiftKey && e.key === 'c') || // Ctrl+Shift+C
          e.key === 'F12'
        ) {
          e.preventDefault();
          e.stopPropagation();
          return false;
        }
      };

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

      window.addEventListener('beforeunload', e => {
        if (window.location.protocol === 'view-source:') {
          e.preventDefault();
          return false;
        }
      });
    }

    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() {
      if (!config.protection.preventCopy) return;

      document.addEventListener('copy', e => {
        e.preventDefault();
      }, true);

      document.addEventListener('cut', e => {
        e.preventDefault();
      }, true);

      if (config.protection.preventPaste) {
        document.addEventListener('paste', e => {
          e.preventDefault();
        }, 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 #enableDebuggingHelpers() {
      BypassHelpers.disableAntiDebugging.patchTimingChecks();
      BypassHelpers.disableAntiDebugging.patchStackTraces();
      BypassHelpers.debugTools.monitorAPICalls();
      BypassHelpers.debugTools.monitorDOMEvents();
    }

    static #cleanCode(code) {
      if (typeof code !== 'string') return code;

      let cleanCode = code
        .replace(/\/\/[#@]\s*source(?:Mapping)?URL\s*=.*$/gm, '')
        .replace(/(['"`])(?:(?!\1).)*?(?:debug|debugger|devtools?)(?:(?!\1).)*?\1/gi, match => `'${btoa(match)}'`)
        .replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '')
        .replace(/debugger|debug\s*\(|console\.[a-z]+/gi, '');

      Object.values(config.debugPatterns).forEach(pattern => {
        cleanCode = cleanCode.replace(pattern, '');
      });

      return cleanCode
        .replace(/function\s*\(/g, 'function /*debug*/(')
        .replace(/return\s+/g, 'return /*debug*/ ');
    }
  }

  // Main class
  class DevToolsBypass {
    static init() {
      try {
        Protection.applyAll();
        logger.info('DevTools Bypass initialized successfully');
      } catch (e) {
        logger.error('Failed to initialize DevTools Bypass:', e);
      }
    }
  }

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