jQuery Hook

用于快速定位使用jQuery绑定到DOM元素上的事件的代码的真实位置,辅助逆向分析。

目前為 2023-08-17 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name jQuery Hook
  3. // @namespace https://github.com/CC11001100/jQuery-hook
  4. // @version 0.3
  5. // @description 用于快速定位使用jQuery绑定到DOM元素上的事件的代码的真实位置,辅助逆向分析。
  6. // @document https://github.com/CC11001100/jQuery-hook
  7. // @author CC11001100
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12. (() => {
  13.  
  14. // 尽量唯一有区分度即可
  15. const globalUniqPrefix = "cc11001100";
  16.  
  17. // 在第一次设置jquery的时候添加Hook,jQuery初始化的时候会添加一个名为$的全局变量
  18. Object.defineProperty(window, "$", {
  19. set: $ => {
  20.  
  21. // 为jquery的各种方法添加Hook
  22. try {
  23. addHook($);
  24. } catch (e) {
  25. console.error("为jQuery添加Hook时报错: " + e)
  26. }
  27.  
  28. // 删除set描述符拦截,恢复正常赋值
  29. delete window["$"];
  30. window["$"] = $;
  31. },
  32. configurable: true
  33. });
  34.  
  35. /**
  36. * 为jquery添加一些hook,等会儿使用jquery为dom元素绑定事件的话就会被捕获到
  37. * @param $
  38. */
  39. function addHook($) {
  40.  
  41. if (!$["fn"]) {
  42. console.log("当前页面虽然声明了$变量,但并不是jQuery,因此忽略。");
  43. return;
  44. }
  45.  
  46. // 一些比较通用的事件的拦截
  47. const eventNameList = [
  48. "click", "dblclick", "blur", "change", "contextmenu", "error", "focus",
  49. "focusin", "focusout", "hover", "holdReady", "proxy", "ready", "keydown", "keypress",
  50. "keyup", "live", "load", "mousedown", "mouseenter", "mouseleave", "mousemove", "mouseout",
  51. "mouseover", "mouseup"
  52. ];
  53. for (let eventName of eventNameList) {
  54. const old = $.fn[eventName];
  55. $.fn[eventName] = function () {
  56. try {
  57. setEventFunctionNameToDomObjectAttribute(this, eventName, arguments[0]);
  58. } catch (e) {
  59. console.error(`为jQuery添加${eventName}类型的事件的Hook时发生错误: ${e}`);
  60. }
  61. return old.apply(this, arguments);
  62. }
  63. }
  64.  
  65. // on,不仅是内置事件类型,还有可能有一些自定义的事件类型
  66. // https://api.jquery.com/on/
  67. const fnOnHolder = $.fn.on;
  68. $.fn.on = function () {
  69. try {
  70. const eventName = arguments[0];
  71. let eventFunction = undefined;
  72. for (let x of arguments) {
  73. if (x instanceof Function) {
  74. eventFunction = x;
  75. break;
  76. }
  77. }
  78. if (eventFunction instanceof Function) {
  79. setEventFunctionNameToDomObjectAttribute(this, eventName, eventFunction);
  80. }
  81. } catch (e) {
  82. console.error(`为jQuery添加on方法的Hook时发生错误: ${e}`);
  83. }
  84. return fnOnHolder.apply(this, arguments);
  85. }
  86.  
  87. // TODO 还有delegate之类的比较隐晦的绑定事件的方式
  88.  
  89. console.log(`当前页面使用了jQueryjQuery Hook已初始化完毕。`);
  90. }
  91.  
  92. const addressIdGeneratorMap = {};
  93.  
  94. /**
  95. * 生成一个全局唯一的标识
  96. * @param eventName
  97. */
  98. function globalUnique(eventName) {
  99. const id = (addressIdGeneratorMap[eventName] || 0) + 1;
  100. addressIdGeneratorMap[eventName] = id;
  101. return `${globalUniqPrefix}_${eventName}_${id}`;
  102. }
  103.  
  104. /**
  105. * 为绑定了jquery事件的dom元素添加元素,提示所绑定的事件与对应的函数代码的全局变量的名称,只需要复制粘贴跟进去即可
  106. * 注意,有可能会为同一个元素重复绑定相同的事件
  107. *
  108. * @param domObject
  109. * @param eventName
  110. * @param eventFunction
  111. */
  112. function setEventFunctionNameToDomObjectAttribute(domObject, eventName, eventFunction) {
  113. // TODO bug fix 注意,事件名可能会包含一些非法的字符
  114. // cc11001100-jquery-$destroy-event-function
  115. eventName = safeSymbol(eventName);
  116. const eventFunctionGlobalName = globalUnique(eventName);
  117. window[eventFunctionGlobalName] = eventFunction;
  118. const attrName = `${globalUniqPrefix}-jQuery-${eventName}-event-function`;
  119. if (domObject.attr(attrName)) {
  120. domObject.attr(attrName + "-" + new Date().getTime(), eventFunctionGlobalName);
  121. } else {
  122. domObject.attr(attrName, eventFunctionGlobalName);
  123. }
  124. }
  125.  
  126. /***
  127. *
  128. * @param name
  129. */
  130. function safeSymbol(name) {
  131. const replaceMap = {
  132. ".": "_dot_",
  133. "$": "_dollar_",
  134. "-": "_dash_"
  135. };
  136. for (let key of Object.getOwnPropertyNames(replaceMap)) {
  137. name = name.replace(key, replaceMap[key]);
  138. }
  139. return name;
  140. }
  141.  
  142. })();
  143.