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