开发工具限制绕过

绕过网站的反开发者工具限制,启用完整的开发者访问权限

  1. // ==UserScript==
  2. // @name DevTools Bypass
  3. // @name:vi Bỏ Qua Chặn DevTools
  4. // @name:zh-CN 开发工具限制绕过
  5. // @name:en DevTools Bypass
  6. // @namespace https://gf.qytechs.cn/vi/users/1195312-renji-yuusei
  7. // @version 4.0.0
  8. // @description Bypass website anti-DevTools restrictions and enable full developer access
  9. // @description:vi Vô hiệu hóa các biện pháp chặn DevTools của website và cho phép truy cập đầy đủ
  10. // @description:zh-CN 绕过网站的反开发者工具限制,启用完整的开发者访问权限
  11. // @description:en Bypass website anti-DevTools restrictions and enable full developer access
  12. // @author Yuusei
  13. // @match *://*/*
  14. // @grant unsafeWindow
  15. // @run-at document-start
  16. // @license GPL-3.0-only
  17. // ==/UserScript==
  18.  
  19. (() => {
  20. "use strict";
  21.  
  22. // Configuration
  23. const CONFIG = {
  24. // Enhanced regex to detect and neutralize anti-DevTools code
  25. antiDevToolsRegex: new RegExp(
  26. [
  27. // Debugger statements - remove completely
  28. /(?:^|[;\s{(,])\s*debugger\s*(?:[;\s}),]|$)/.source,
  29.  
  30. // Function constructor with debugger
  31. /(?:new\s+)?Function\s*\(\s*['"`][^'"`]*debugger[^'"`]*['"`]\s*\)/
  32. .source,
  33.  
  34. // Timer-based debugger injections
  35. /(?:setTimeout|setInterval)\s*\(\s*(?:function[^{]*\{[^}]*debugger[^}]*\}|['"`][^'"`]*debugger[^'"`]*['"`])\s*[,)]/
  36. .source,
  37.  
  38. // eval with debugger
  39. /eval\s*\(\s*['"`][^'"`]*debugger[^'"`]*['"`]\s*\)/.source,
  40.  
  41. // Console detection tricks
  42. /console\s*\[\s*['"`](?:log|warn|error|info|debug|clear|table|dir|group|time)['"`]\s*\]\s*\.\s*toString/
  43. .source,
  44. /console\.(?:log|warn|error|info|debug|trace|clear|table|dir|group|time)\s*\.\s*toString\s*\(\s*\)/
  45. .source,
  46.  
  47. // DevTools size detection
  48. /(?:window\.(?:outer|inner)(?:Width|Height)|screen\.(?:width|height))\s*[-+*\/]\s*(?:window\.(?:outer|inner)(?:Width|Height)|screen\.(?:width|height))\s*[<>=!]+\s*\d+/
  49. .source,
  50.  
  51. // Performance timing detection
  52. /(?:performance\.now|Date\.now)\s*\(\s*\)\s*[-+]\s*(?:performance\.now|Date\.now)\s*\(\s*\)\s*[><=!]+\s*\d+/
  53. .source,
  54.  
  55. // Known anti-DevTools libraries
  56. /(?:FuckDevTools|devtools-detector|disable-devtool|console-ban|anti-debug|devtools-detect|fuck-debugger)/
  57. .source,
  58.  
  59. // DevTools event listeners
  60. /(?:addEventListener|on)\s*\(\s*['"`](?:keydown|keyup|keypress|contextmenu|selectstart|copy|cut|paste|dragstart)['"`][^)]*(?:F12|preventDefault|stopPropagation)/
  61. .source,
  62.  
  63. // Console override attempts
  64. /console\s*=\s*(?:\{\}|null|undefined|false)/.source,
  65. /window\.console\s*=/.source,
  66.  
  67. // DevTools detection via exceptions
  68. /try\s*\{[^}]*(?:debugger|console)[^}]*\}\s*catch/.source,
  69.  
  70. // Stack trace analysis for DevTools detection
  71. /(?:Error|TypeError|ReferenceError)\(\)\.stack\.(?:split|match|replace|indexOf|includes|search)/
  72. .source,
  73.  
  74. // Arguments.callee detection (used in some anti-debug)
  75. /arguments\.callee/.source,
  76.  
  77. // toString override for detection
  78. /toString\s*=\s*function[^{]*\{[^}]*(?:devtools|debug|console)/.source,
  79. ].join("|"),
  80. "gim"
  81. ),
  82.  
  83. protection: {
  84. neutralizeDebugger: true,
  85. enableDevToolsKeys: true,
  86. enableRightClick: true,
  87. enableTextSelection: true,
  88. enableCopyPaste: true,
  89. preventAntiDebugTimers: true,
  90. restoreConsole: true,
  91. preventKeyBlocking: true,
  92. },
  93.  
  94. logging: {
  95. enabled: true,
  96. prefix: "[DevTools Bypass]",
  97. verbose: false,
  98. },
  99. };
  100.  
  101. // Logger
  102. class Logger {
  103. static #logHistory = new Map();
  104. static #maxLogsPerType = 5;
  105.  
  106. static #canLog(type, message) {
  107. const key = `${type}:${message}`;
  108. const count = this.#logHistory.get(key) || 0;
  109. if (count >= this.#maxLogsPerType) return false;
  110.  
  111. this.#logHistory.set(key, count + 1);
  112. return true;
  113. }
  114.  
  115. static info(message, ...args) {
  116. if (CONFIG.logging.enabled && this.#canLog("info", message)) {
  117. console.info(CONFIG.logging.prefix, message, ...args);
  118. }
  119. }
  120.  
  121. static warn(message, ...args) {
  122. if (CONFIG.logging.enabled && this.#canLog("warn", message)) {
  123. console.warn(CONFIG.logging.prefix, message, ...args);
  124. }
  125. }
  126.  
  127. static debug(message, ...args) {
  128. if (
  129. CONFIG.logging.enabled &&
  130. CONFIG.logging.verbose &&
  131. this.#canLog("debug", message)
  132. ) {
  133. console.debug(CONFIG.logging.prefix, message, ...args);
  134. }
  135. }
  136. }
  137.  
  138. // Store original functions before they get overridden
  139. const ORIGINAL = {
  140. // Core functions
  141. Function: window.Function,
  142. eval: window.eval,
  143. setTimeout: window.setTimeout,
  144. setInterval: window.setInterval,
  145. clearTimeout: window.clearTimeout,
  146. clearInterval: window.clearInterval,
  147.  
  148. // Timing
  149. Date: window.Date,
  150. now: Date.now,
  151. performance: window.performance?.now?.bind(window.performance),
  152.  
  153. // DOM
  154. addEventListener: window.addEventListener,
  155. removeEventListener: window.removeEventListener,
  156. createElement: document.createElement,
  157.  
  158. // Object methods
  159. defineProperty: Object.defineProperty,
  160. getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor,
  161. keys: Object.keys,
  162.  
  163. // Console (store before potential override)
  164. console: {},
  165. };
  166.  
  167. // Backup console methods
  168. [
  169. "log",
  170. "warn",
  171. "error",
  172. "info",
  173. "debug",
  174. "trace",
  175. "dir",
  176. "table",
  177. "group",
  178. "groupEnd",
  179. "clear",
  180. "time",
  181. "timeEnd",
  182. ].forEach((method) => {
  183. if (console[method]) {
  184. ORIGINAL.console[method] = console[method].bind(console);
  185. }
  186. });
  187.  
  188. // Code Neutralizer - Cleans anti-DevTools code
  189. class CodeNeutralizer {
  190. static neutralize(code) {
  191. if (typeof code !== "string" || !code.trim()) {
  192. return code;
  193. }
  194.  
  195. try {
  196. let neutralized = code;
  197.  
  198. // Replace anti-DevTools patterns
  199. neutralized = neutralized.replace(
  200. CONFIG.antiDevToolsRegex,
  201. (match, ...args) => {
  202. const replacement = this.#getReplacement(match);
  203. Logger.debug(
  204. "Neutralized anti-DevTools code:",
  205. match.substring(0, 100)
  206. );
  207. return replacement;
  208. }
  209. );
  210.  
  211. // Handle encoded debugger statements
  212. neutralized = neutralized.replace(
  213. /\\u0064\\u0065\\u0062\\u0075\\u0067\\u0067\\u0065\\u0072/g,
  214. ""
  215. );
  216. neutralized = neutralized.replace(
  217. /\u0064\u0065\u0062\u0075\u0067\u0067\u0065\u0072/g,
  218. ""
  219. );
  220.  
  221. // Remove obfuscated debugger
  222. neutralized = neutralized.replace(/['"`]debugger['"`]/g, '""');
  223. neutralized = neutralized.replace(/\bdebugger\b/g, "");
  224.  
  225. // Neutralize console blocking
  226. neutralized = neutralized.replace(
  227. /console\s*=\s*(?:\{\}|null|undefined|false)/g,
  228. "console = console"
  229. );
  230. neutralized = neutralized.replace(/window\.console\s*=\s*[^;]+/g, "");
  231.  
  232. return neutralized;
  233. } catch (e) {
  234. Logger.warn("Code neutralization failed:", e.message);
  235. return code;
  236. }
  237. }
  238.  
  239. static #getReplacement(match) {
  240. if (match.includes("debugger")) {
  241. return "/* debugger statement removed */";
  242. }
  243. if (match.includes("console")) {
  244. return "/* console detection removed */";
  245. }
  246. if (match.includes("addEventListener") || match.includes("keydown")) {
  247. return "/* key blocking removed */";
  248. }
  249. if (match.includes("performance") || match.includes("Date.now")) {
  250. return "/* timing detection removed */";
  251. }
  252. return "/* anti-DevTools code removed */";
  253. }
  254. }
  255.  
  256. // DevTools Protection Bypass
  257. class DevToolsProtectionBypass {
  258. static apply() {
  259. this.#neutralizeFunctionConstructor();
  260. this.#neutralizeEval();
  261. this.#neutralizeTimers();
  262. this.#preventKeyBlocking();
  263. this.#restoreRightClick();
  264. this.#restoreTextSelection();
  265. this.#restoreConsole();
  266. this.#preventTimingDetection();
  267. this.#neutralizeDebuggerTricks();
  268. this.#patchMutationObserver();
  269. this.#restoreClipboard();
  270. this.#preventErrorOverrides();
  271. }
  272.  
  273. // Neutralize Function constructor to prevent debugger injection
  274. static #neutralizeFunctionConstructor() {
  275. const handler = {
  276. construct(target, args) {
  277. if (args[0] && typeof args[0] === "string") {
  278. args[0] = CodeNeutralizer.neutralize(args[0]);
  279. }
  280. return Reflect.construct(target, args);
  281. },
  282. apply(target, thisArg, args) {
  283. if (args[0] && typeof args[0] === "string") {
  284. args[0] = CodeNeutralizer.neutralize(args[0]);
  285. }
  286. return Reflect.apply(target, thisArg, args);
  287. },
  288. };
  289.  
  290. try {
  291. window.Function = new Proxy(ORIGINAL.Function, handler);
  292. if (typeof unsafeWindow !== "undefined") {
  293. unsafeWindow.Function = window.Function;
  294. }
  295. Logger.info("Function constructor protected");
  296. } catch (e) {
  297. Logger.warn("Function protection failed:", e.message);
  298. }
  299. }
  300.  
  301. // Neutralize eval to prevent debugger injection
  302. static #neutralizeEval() {
  303. const safeEval = function (code) {
  304. if (typeof code === "string") {
  305. code = CodeNeutralizer.neutralize(code);
  306. }
  307. return ORIGINAL.eval.call(this, code);
  308. };
  309.  
  310. try {
  311. Object.defineProperty(window, "eval", {
  312. value: safeEval,
  313. writable: false,
  314. configurable: false,
  315. });
  316.  
  317. if (typeof unsafeWindow !== "undefined") {
  318. unsafeWindow.eval = safeEval;
  319. }
  320. Logger.info("eval function protected");
  321. } catch (e) {
  322. Logger.warn("eval protection failed:", e.message);
  323. }
  324. }
  325.  
  326. // Neutralize timers that might inject debugger
  327. static #neutralizeTimers() {
  328. const wrapTimer = (original, name) => {
  329. return function (handler, delay, ...args) {
  330. if (typeof handler === "string") {
  331. handler = CodeNeutralizer.neutralize(handler);
  332. }
  333. return original.call(this, handler, delay, ...args);
  334. };
  335. };
  336.  
  337. window.setTimeout = wrapTimer(ORIGINAL.setTimeout, "setTimeout");
  338. window.setInterval = wrapTimer(ORIGINAL.setInterval, "setInterval");
  339.  
  340. Logger.info("Timer functions protected");
  341. }
  342.  
  343. // Prevent key blocking (F12, Ctrl+Shift+I, etc.)
  344. static #preventKeyBlocking() {
  345. const events = ["keydown", "keypress", "keyup"];
  346.  
  347. events.forEach((eventType) => {
  348. // Override addEventListener to prevent key blocking
  349. const originalAddListener = ORIGINAL.addEventListener;
  350. document.addEventListener = function (type, listener, options) {
  351. if (type === eventType && typeof listener === "function") {
  352. const originalListener = listener;
  353. listener = function (event) {
  354. const key = event.key?.toLowerCase();
  355. const code = event.code?.toLowerCase();
  356.  
  357. // Allow DevTools keys
  358. const isDevToolsKey =
  359. key === "f12" ||
  360. (event.ctrlKey &&
  361. event.shiftKey &&
  362. ["i", "j", "c"].includes(key)) ||
  363. (event.ctrlKey && key === "u");
  364.  
  365. if (isDevToolsKey) {
  366. Logger.debug("Allowing DevTools key:", key);
  367. return; // Don't call the original listener
  368. }
  369.  
  370. return originalListener.call(this, event);
  371. };
  372. }
  373. return originalAddListener.call(this, type, listener, options);
  374. };
  375.  
  376. // Also handle window events
  377. window.addEventListener = document.addEventListener;
  378. });
  379.  
  380. // Block existing key event listeners by overriding preventDefault
  381. const originalPreventDefault = Event.prototype.preventDefault;
  382. Event.prototype.preventDefault = function () {
  383. const key = this.key?.toLowerCase();
  384. const isDevToolsKey =
  385. key === "f12" ||
  386. (this.ctrlKey && this.shiftKey && ["i", "j", "c"].includes(key)) ||
  387. (this.ctrlKey && key === "u");
  388.  
  389. if (
  390. isDevToolsKey &&
  391. (this.type === "keydown" ||
  392. this.type === "keypress" ||
  393. this.type === "keyup")
  394. ) {
  395. Logger.debug("Prevented preventDefault on DevTools key:", key);
  396. return; // Don't prevent default for DevTools keys
  397. }
  398.  
  399. return originalPreventDefault.call(this);
  400. };
  401.  
  402. Logger.info("Key blocking prevention enabled");
  403. }
  404.  
  405. // Restore right-click context menu
  406. static #restoreRightClick() {
  407. // Override contextmenu event blocking
  408. const originalAddListener = document.addEventListener;
  409. document.addEventListener = function (type, listener, options) {
  410. if (type === "contextmenu") {
  411. // Replace with a dummy function that doesn't block
  412. listener = function (e) {
  413. Logger.debug("Context menu allowed");
  414. return true;
  415. };
  416. }
  417. return originalAddListener.call(this, type, listener, options);
  418. };
  419.  
  420. // Also handle oncontextmenu attribute
  421. const observer = new MutationObserver((mutations) => {
  422. mutations.forEach((mutation) => {
  423. if (
  424. mutation.type === "attributes" &&
  425. mutation.attributeName === "oncontextmenu"
  426. ) {
  427. mutation.target.removeAttribute("oncontextmenu");
  428. Logger.debug("Removed oncontextmenu attribute");
  429. }
  430. });
  431. });
  432.  
  433. observer.observe(document.documentElement, {
  434. attributes: true,
  435. subtree: true,
  436. attributeFilter: ["oncontextmenu"],
  437. });
  438.  
  439. Logger.info("Right-click restored");
  440. }
  441.  
  442. // Restore text selection
  443. static #restoreTextSelection() {
  444. // Override selectstart event blocking
  445. const originalAddListener = document.addEventListener;
  446. const blockedEvents = ["selectstart", "dragstart", "copy", "cut"];
  447.  
  448. document.addEventListener = function (type, listener, options) {
  449. if (blockedEvents.includes(type)) {
  450. listener = function (e) {
  451. Logger.debug("Selection/copy event allowed:", type);
  452. return true;
  453. };
  454. }
  455. return originalAddListener.call(this, type, listener, options);
  456. };
  457.  
  458. // Remove CSS that prevents text selection
  459. const style = document.createElement("style");
  460. style.textContent = `
  461. *, *::before, *::after {
  462. -webkit-user-select: text !important;
  463. -moz-user-select: text !important;
  464. -ms-user-select: text !important;
  465. user-select: text !important;
  466. }
  467. `;
  468. document.head.appendChild(style);
  469.  
  470. Logger.info("Text selection restored");
  471. }
  472.  
  473. // Restore console if it was overridden
  474. static #restoreConsole() {
  475. try {
  476. // Check if console was disabled/overridden
  477. if (!window.console || typeof window.console.log !== "function") {
  478. window.console = ORIGINAL.console;
  479. Logger.info("Console restored");
  480. }
  481.  
  482. // Ensure console methods are working
  483. Object.keys(ORIGINAL.console).forEach((method) => {
  484. if (!console[method] || typeof console[method] !== "function") {
  485. console[method] = ORIGINAL.console[method];
  486. }
  487. });
  488.  
  489. // Prevent future console overrides
  490. Object.defineProperty(window, "console", {
  491. value: window.console,
  492. writable: false,
  493. configurable: false,
  494. });
  495. } catch (e) {
  496. Logger.warn("Console restoration failed:", e.message);
  497. }
  498. }
  499.  
  500. // Prevent timing-based DevTools detection
  501. static #preventTimingDetection() {
  502. // Add small random delays to timing functions to break detection
  503. const addNoise = () => Math.random() * 2;
  504.  
  505. try {
  506. Object.defineProperty(Date, "now", {
  507. value: () => ORIGINAL.now() + addNoise(),
  508. writable: false,
  509. configurable: false,
  510. });
  511.  
  512. if (window.performance?.now) {
  513. Object.defineProperty(window.performance, "now", {
  514. value: () => ORIGINAL.performance() + addNoise(),
  515. writable: false,
  516. configurable: false,
  517. });
  518. }
  519.  
  520. Logger.info("Timing detection prevented");
  521. } catch (e) {
  522. Logger.warn("Timing protection failed:", e.message);
  523. }
  524. }
  525.  
  526. // Neutralize debugger tricks
  527. static #neutralizeDebuggerTricks() {
  528. // Override toString methods that might be used for detection
  529. const safeToString = function () {
  530. return this.name
  531. ? `function ${this.name}() { [native code] }`
  532. : "function() { [native code] }";
  533. };
  534.  
  535. try {
  536. // Protect critical functions from toString inspection
  537. window.Function.prototype.toString = safeToString;
  538. window.eval.toString = () => "function eval() { [native code] }";
  539.  
  540. Logger.info("Debugger tricks neutralized");
  541. } catch (e) {
  542. Logger.warn("Debugger trick neutralization failed:", e.message);
  543. }
  544. }
  545.  
  546. // Monitor and clean injected scripts
  547. static #patchMutationObserver() {
  548. if (!window.MutationObserver) return;
  549.  
  550. try {
  551. new MutationObserver((mutations) => {
  552. mutations.forEach((mutation) => {
  553. mutation.addedNodes.forEach((node) => {
  554. if (node.nodeType === Node.ELEMENT_NODE) {
  555. // Clean script content
  556. if (node.tagName === "SCRIPT" && node.textContent) {
  557. const originalContent = node.textContent;
  558. const cleanedContent =
  559. CodeNeutralizer.neutralize(originalContent);
  560.  
  561. if (originalContent !== cleanedContent) {
  562. node.textContent = cleanedContent;
  563. Logger.debug("Cleaned injected script");
  564. }
  565. }
  566.  
  567. // Clean event attributes
  568. if (node.attributes) {
  569. Array.from(node.attributes).forEach((attr) => {
  570. if (
  571. attr.name.startsWith("on") ||
  572. attr.name === "oncontextmenu"
  573. ) {
  574. const cleaned = CodeNeutralizer.neutralize(attr.value);
  575. if (
  576. cleaned !== attr.value ||
  577. attr.name === "oncontextmenu"
  578. ) {
  579. node.removeAttribute(attr.name);
  580. Logger.debug(
  581. "Removed/cleaned event attribute:",
  582. attr.name
  583. );
  584. }
  585. }
  586. });
  587. }
  588. }
  589. });
  590. });
  591. }).observe(document.documentElement, {
  592. childList: true,
  593. subtree: true,
  594. attributes: true,
  595. attributeFilter: [
  596. "onload",
  597. "onerror",
  598. "onclick",
  599. "oncontextmenu",
  600. "onkeydown",
  601. "onkeyup",
  602. ],
  603. });
  604.  
  605. Logger.info("DOM monitoring active");
  606. } catch (e) {
  607. Logger.warn("DOM monitoring failed:", e.message);
  608. }
  609. }
  610.  
  611. // Restore clipboard functionality
  612. static #restoreClipboard() {
  613. const clipboardEvents = ["copy", "cut", "paste"];
  614.  
  615. clipboardEvents.forEach((eventType) => {
  616. document.addEventListener(
  617. eventType,
  618. (e) => {
  619. // Ensure clipboard events are not blocked
  620. e.stopImmediatePropagation();
  621. },
  622. true
  623. );
  624. });
  625.  
  626. Logger.info("Clipboard functionality restored");
  627. }
  628.  
  629. // Prevent error handling overrides that might block DevTools
  630. static #preventErrorOverrides() {
  631. const originalErrorHandler = window.onerror;
  632.  
  633. window.addEventListener(
  634. "error",
  635. (e) => {
  636. // Don't let websites block error reporting
  637. e.stopImmediatePropagation();
  638. },
  639. true
  640. );
  641.  
  642. // Prevent overriding of error handlers
  643. Object.defineProperty(window, "onerror", {
  644. set: function (handler) {
  645. Logger.debug("Prevented error handler override");
  646. },
  647. get: function () {
  648. return originalErrorHandler;
  649. },
  650. });
  651.  
  652. Logger.info("Error override prevention active");
  653. }
  654. }
  655.  
  656. // Main bypass controller
  657. class DevToolsBypass {
  658. static init() {
  659. try {
  660. Logger.info("Starting DevTools protection bypass...");
  661.  
  662. // Apply all bypasses
  663. DevToolsProtectionBypass.apply();
  664.  
  665. // Clean existing page content
  666. this.#cleanExistingContent();
  667.  
  668. Logger.info("DevTools bypass activated successfully");
  669.  
  670. // Show success indicator
  671. this.#showSuccessIndicator();
  672. } catch (e) {
  673. Logger.warn("Bypass initialization failed:", e.message);
  674. }
  675. }
  676.  
  677. static #cleanExistingContent() {
  678. try {
  679. // Clean all existing script tags
  680. const scripts = document.querySelectorAll("script");
  681. scripts.forEach((script) => {
  682. if (script.textContent) {
  683. const cleaned = CodeNeutralizer.neutralize(script.textContent);
  684. if (cleaned !== script.textContent) {
  685. script.textContent = cleaned;
  686. Logger.debug("Cleaned existing script");
  687. }
  688. }
  689. });
  690.  
  691. // Remove problematic event attributes
  692. const elementsWithEvents = document.querySelectorAll(
  693. "[oncontextmenu], [onkeydown], [onkeyup], [onselectstart], [ondragstart]"
  694. );
  695. elementsWithEvents.forEach((element) => {
  696. [
  697. "oncontextmenu",
  698. "onkeydown",
  699. "onkeyup",
  700. "onselectstart",
  701. "ondragstart",
  702. ].forEach((attr) => {
  703. if (element.hasAttribute(attr)) {
  704. element.removeAttribute(attr);
  705. Logger.debug("Removed event attribute:", attr);
  706. }
  707. });
  708. });
  709. } catch (e) {
  710. Logger.warn("Content cleaning failed:", e.message);
  711. }
  712. }
  713.  
  714. static #showSuccessIndicator() {
  715. if (!CONFIG.logging.enabled) return;
  716.  
  717. // Create a temporary success indicator
  718. const indicator = document.createElement("div");
  719. indicator.style.cssText = `
  720. position: fixed;
  721. top: 20px;
  722. right: 20px;
  723. background: #4CAF50;
  724. color: white;
  725. padding: 10px 20px;
  726. border-radius: 5px;
  727. z-index: 999999;
  728. font-family: Arial, sans-serif;
  729. font-size: 14px;
  730. box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  731. `;
  732. indicator.textContent = "✓ DevTools Bypass Active";
  733.  
  734. document.body?.appendChild(indicator);
  735.  
  736. // Remove after 3 seconds
  737. setTimeout(() => {
  738. indicator.remove();
  739. }, 3000);
  740. }
  741. }
  742.  
  743. // Initialize immediately and on various events
  744. DevToolsBypass.init();
  745.  
  746. // Also initialize when DOM is ready (fallback)
  747. if (document.readyState === "loading") {
  748. document.addEventListener("DOMContentLoaded", DevToolsBypass.init);
  749. }
  750.  
  751. // Initialize on window load (another fallback)
  752. window.addEventListener("load", DevToolsBypass.init);
  753. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址