MonkeyDebugger

Debug js using monkey patch

  1. // ==UserScript==
  2. // @name MonkeyDebugger
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-07-18
  5. // @description Debug js using monkey patch
  6. // @author Jiyu Shao <jiyu.shao@gmail.com>
  7. // @license MIT
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant unsafeWindow
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_unregisterMenuCommand
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_addValueChangeListener
  16. // @require https://update.gf.qytechs.cn/scripts/496315/1392531/QuickMenu.js
  17. // ==/UserScript==
  18.  
  19. /* eslint-disable no-eval */
  20. (function () {
  21. 'use strict';
  22.  
  23. // 定义通用的补丁代码给 new Function 与 eval 使用
  24. const patchCode = `
  25. debugger;
  26. `;
  27.  
  28. // 保存原始的 Function 构造器
  29. const originalFunction = unsafeWindow.Function.prototype.constructor;
  30. const addFnPatchCode = function (fn) {
  31. const finalFn = function (...args) {
  32. if (!args.length) return fn.apply(this, args);
  33. args[args.length - 1] = `${patchCode}; ${args[args.length - 1]}`;
  34. return fn.apply(this, args);
  35. };
  36. finalFn.prototype = originalFunction.prototype;
  37. Object.defineProperty(finalFn.prototype, 'constructor', {
  38. value: finalFn,
  39. writable: true,
  40. configurable: true,
  41. });
  42. return finalFn;
  43. };
  44. const removeFnDebugger = function () {
  45. const finalFn = function (...args) {
  46. if (!args.length) return originalFunction.apply(this, args);
  47. args[args.length - 1] = args[args.length - 1].replace(/debugger/g, '');
  48. return originalFunction.apply(this, args);
  49. };
  50. finalFn.prototype = originalFunction.prototype;
  51. Object.defineProperty(finalFn.prototype, 'constructor', {
  52. value: finalFn,
  53. writable: true,
  54. configurable: true,
  55. });
  56. return finalFn;
  57. };
  58. QuickMenu.add({
  59. name: '开启 Function 调试',
  60. type: 'toggle',
  61. shouldInitRun: true,
  62. shouldAddMenu: () => {
  63. return unsafeWindow === unsafeWindow.top;
  64. },
  65. callback: (value) => {
  66. if (value === 'on') {
  67. // 替换全局的 Function constructor
  68. unsafeWindow.Function = addFnPatchCode();
  69. } else if (value === 'off') {
  70. // 替换全局的 Function constructor
  71. unsafeWindow.Function = removeFnDebugger();
  72. }
  73. },
  74. });
  75.  
  76. // 保存原始的 eval 函数
  77. const originalEval = unsafeWindow.eval;
  78. const addEvalPatchCode = function () {
  79. return function (...args) {
  80. if (!args.length) return originalEval.apply(this, args);
  81. args[0] = `${patchCode}; ${args[0]}`;
  82. return originalEval.apply(this, args);
  83. };
  84. };
  85. const removeEvalDebugger = function () {
  86. return function (...args) {
  87. if (!args.length) return originalEval.apply(this, args);
  88. args[0] = args[0].replace(/debugger/g, '');
  89. return originalEval.apply(this, args);
  90. };
  91. };
  92. QuickMenu.add({
  93. name: '开启 eval 调试',
  94. type: 'toggle',
  95. shouldInitRun: true,
  96. shouldAddMenu: () => {
  97. return unsafeWindow === unsafeWindow.top;
  98. },
  99. callback: (value) => {
  100. if (value === 'on') {
  101. // 替换全局的 eval 函数
  102. unsafeWindow.eval = addEvalPatchCode();
  103. } else if (value === 'off') {
  104. unsafeWindow.eval = removeEvalDebugger();
  105. }
  106. },
  107. });
  108.  
  109. const originalDefineProperty = unsafeWindow.Object.defineProperty;
  110. // 覆盖 `Error` 对象的 `message` 属性的 getter
  111. const removeErrorMessageGetter = () => {
  112. return function (obj, prop, descriptor) {
  113. if (obj instanceof Error && prop === 'message') {
  114. delete descriptor.get;
  115. delete descriptor.set;
  116. }
  117. return originalDefineProperty.call(Object, obj, prop, descriptor);
  118. };
  119. };
  120. // https://github.com/fz6m/console-ban/tree/master
  121. QuickMenu.add({
  122. name: '解除 console-ban 限制',
  123. type: 'toggle',
  124. shouldInitRun: true,
  125. shouldAddMenu: () => {
  126. return unsafeWindow === unsafeWindow.top;
  127. },
  128. callback: (value) => {
  129. if (value === 'on') {
  130. unsafeWindow.Object.defineProperty = removeErrorMessageGetter();
  131. } else if (value === 'off') {
  132. unsafeWindow.Object.defineProperty = originalDefineProperty;
  133. }
  134. },
  135. });
  136.  
  137. const originalFetch = unsafeWindow.fetch;
  138. QuickMenu.add({
  139. name: 'Fetch with credentials',
  140. type: 'toggle',
  141. shouldInitRun: true,
  142. shouldAddMenu: () => {
  143. return true;
  144. },
  145. callback: (value) => {
  146. if (value === 'on') {
  147. unsafeWindow.fetch = function (input, init) {
  148. init = init || {};
  149. init.credentials = 'include';
  150. return originalFetch(input, init);
  151. };
  152. } else if (value === 'off') {
  153. unsafeWindow.fetch = originalFetch;
  154. }
  155. },
  156. });
  157.  
  158. QuickMenu.add({
  159. name: '清空菜单缓存',
  160. type: 'button',
  161. shouldInitRun: false,
  162. shouldAddMenu: () => {
  163. return unsafeWindow === unsafeWindow.top;
  164. },
  165. callback: () => {
  166. QuickMenu.clearStore();
  167. },
  168. });
  169. })();

QingJ © 2025

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