1.第一个知识点: 原型链
先拿平时大家最经常hook document.cookie来举个例子。
Object.defineProperty(document,”cookie”,{ get:function(){}, set:function(){} });
然后我们需要自己去实现里面的代码逻辑
而这个代码又有一个问题。事实上cookie是不在document对象下的
Object.getOwnPropertyDescriptors(document);
用这个api我们可以查看document下的所有对象属性。
就很怪,没有cookie对象,那document是怎么拿到cookie的值的?
这就涉及到原型链的知识点了。document获取cookie的时候,发现自身没有这个对象时,
会从document.__proto__ 去寻找有没有cookie这个对象。(document.__proto__ == HTMLDocument.prototype),还是没有,然后就继续这个操作寻找,直到原型链的最后一层。
然后的话,在Document.prototype 下发现了这个cookie对象,并获取它的值。
但是不等于Document.prototype.cookie 来获取值,这样调用会报错的,因为它不知道调用者是谁。
Object.getOwnPropertyDescriptors(Document.prototype);
打印看看
那么事实上hook代码应该是
Object.defineProperty(Document.prototype,”cookie”,{ get:function(){}, set:function(){} });
这样的话,只要是对象继承Document.prototype,并获取cookie这个值的时候都会被我们hook住,而不只是仅仅hook document来调用的时候。
所以我们只需要hook所有原型链上的方法,或者原型链上所有对象的get set方法,就能实现像v神插件那样一键hook的功能。
2.第二个知识点:apply的妙用
但是我们hook 那么多的方法,总不能一个个去手动实现方法内容吧?能不能一键给所有方法添加一个拦截器,调用的时候先走到拦截器,先执行我们的逻辑,再走到原生方法里
这就要用到apply这个实用的api了。
举个例子
我们hook document.createElement
document.createElement_ = document.createElement; document.createElement = function createElement (参数1){ //写hook逻辑;return document.createElement_(参数1); }
这还要自己手动传参,那么多方法,我怎么知道他到底有几个参数,要如何传参给原来的方法?
用apply的话就很简单了
document.createElement_ = document.createElement; document.createElement = function createElement() {//写hook逻辑; return document.createElement_.apply(undefined,arguments); }
arguments是一个数组,存放的就是当前方法里传进来的参数
我们原先调用方法是 document.createElement(“a”);
用apply,document.createElement.apply(undefined, [“a”,]);
它会帮我们去分配参数,和方法体定义的传参一一对应上
再来说说apply方法传进去的第一个参数
document.createElement.apply(undefined, [“a”,]) == document.createElement.apply(document, [“a”,])
这样是等同的。 如果你传入undefined,他就默认调用者是document.你可以通过第一个参数来修改调用者。
document.createElement.apply(window, [“a”,])
这样就报错了。以上就是hook的核心原理。
3.核心api就三个
1.apply
2.Object.getOwnPropertyDescriptors
- Tips:可以用Object.getOwnPropertyDescriptors(Document.prototype).cookie.get
- 来得到get/set cookie的原生方法。等同于
- Object.getOwnPropertyDescriptor(Document.prototype,‘cookie’).get
3. Object.defineProperty
- 对方法/对象的get set重新定义,来为我们添加一层拦截器。
4.hook.js
globalMy = {}; globalMy.console_log = console.log; globalMy.is_log = true; globalMy.is_debug = false; globalMy.not_log = []; globalMy.want_debug = []; globalMy.set_func_prop = function(func_descriptors){ let prop_track = []; for (let name in func_descriptors){ if(name == "caller" || name == "arguments" || name == "prototype"){ continue; } let descriptor = func_descriptors[name]; let val = descriptor["value"]; let attr = { configurable : descriptor["configurable"], enumerable : descriptor["enumerable"], } if(descriptor["writable"]){ attr["writable"] = descriptor["writable"]; } if(val == undefined){ debugger; } else{ attr["value"] = val; } prop_track.push({name:name,attr:attr}); } return prop_track; }; //保护函数,保护toString (() => { 'use strict'; const $toString = Function.toString; const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random() + '').toString(36))); //key const myToString = function () { return typeof this == 'function' && this[myFunction_toString_symbol] || $toString.call(this); }; function set_native(func, key, value) { try { Object.defineProperty(func, key, { "enumerable": false, "configurable": true, "writable": true, "value": value }) } catch (e) { globalMy.console_log("保护函数出错 => ", e) debugger } } set_native(Function.prototype, "toString", myToString); //自己定义一个getter方法 set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }"); globalMy.functionprotect = (func, func_name, type) => { set_native(func, myFunction_toString_symbol, `function ${ func_name || ''}() { [native code] }`); } ; } ).call(globalMy); globalMy.check_debug = function check_debug(key) { if (globalMy.is_debug || globalMy.want_debug.indexOf(key + '') !== -1) { debugger; } } globalMy.check_log = function check_log(obj, param_func_name, args, result) { if (globalMy.is_log && globalMy.not_log.indexOf(param_func_name) === -1) { let arg; if (args.length === 1) { arg = args[0] } else { arg = [] for (let i = 0; i < args.length; i++) { arg.push(args[i]); } } let property; try { property = JSON.stringify(obj) } catch (e) { property = obj } globalMy.console_log("[*] ", " 调用者 =>", obj, "属性值 => ", property, " 函数名 => ", param_func_name, " 传参 => ", arg, " 结果 => ", result) } } // hook HTMLXxxxElement 下的所有属性的get set方法,以及方法的hook globalMy.hook_HTML = function fuck_HTML(html) { let html_name = html.name; globalMy[html_name] = {}; let descriptors = Object.getOwnPropertyDescriptors(html.prototype); for (let key in descriptors) { if (key !== 'constructor') { let param_value = descriptors[key].value; if (typeof param_value === 'function') { let attr = { configurable: descriptors[key]['configurable'], enumerable: descriptors[key]['enumerable'], } if(descriptors[key]["writable"]){ attr["writable"] = descriptors[key]["writable"]; } let param_func_name = param_value.name; globalMy[html_name][param_func_name] = function () { let result = param_value.apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } globalMy.functionprotect(globalMy[html_name][param_func_name], param_func_name) attr['value'] = globalMy[html_name][param_func_name]; Object.defineProperty(html.prototype, key, attr); } else if (typeof param_value === 'undefined') { let attr = { configurable: descriptors[key]['configurable'], enumerable: descriptors[key]['enumerable'], } if (descriptors[key]['writable']) { attr['writable'] = descriptors[key]['writable'] } // globalMy.console_log(descriptors[key]) if (descriptors[key]['get']) { let param_func_name = descriptors[key]['get'].name; globalMy[html_name][key + '_get'] = function () { let result = descriptors[key]['get'].apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } let func_attr_track = globalMy.set_func_prop(Object.getOwnPropertyDescriptors(descriptors[key]['get'])); var i = 0; while (i < func_attr_track.length){ Object.defineProperty(globalMy[html_name][key + '_get'],func_attr_track[i].name,func_attr_track[i].attr); i++; } globalMy.functionprotect(globalMy[html_name][key + '_get'], param_func_name, 'get'); attr['get'] = globalMy[html_name][key + '_get']; } else { attr['get'] = undefined; } if (descriptors[key]['set']) { let param_func_name = descriptors[key]['set'].name; globalMy[html_name][key + '_set'] = function () { let result = descriptors[key]['set'].apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } let func_attr_track = globalMy.set_func_prop(Object.getOwnPropertyDescriptors(descriptors[key]['set'])); var i = 0; while (i < func_attr_track.length){ Object.defineProperty(globalMy[html_name][key + '_set'],func_attr_track[i].name,func_attr_track[i].attr); i++; } globalMy.functionprotect(globalMy[html_name][key + '_set'], param_func_name, 'set') attr['set'] = globalMy[html_name][key + '_set']; } else { attr['set'] = undefined; } try { Object.defineProperty(html.prototype, key, attr); } catch (e) { debugger } } } } } // hook Object, hook 像window location 这样的属性,不得行 globalMy.hook_obj = function hook_obj(html) { let html_name = html.name; globalMy[html_name] = {}; let descriptors = Object.getOwnPropertyDescriptors(html); for (let key in descriptors) { if (key !== 'constructor') { let param_value = descriptors[key].value; if (typeof param_value === 'function') { let attr = { configurable: descriptors[key]['configurable'], enumerable: descriptors[key]['enumerable'], } if(descriptors[key]["writable"]){ attr["writable"] = descriptors[key]["writable"]; } let param_func_name = param_value.name; globalMy[html_name][param_func_name] = function () { let result = param_value.apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } globalMy.functionprotect(globalMy[html_name][param_func_name], param_func_name) attr['value'] = globalMy[html_name][param_func_name]; Object.defineProperty(html, key, attr); } else if (typeof param_value === 'undefined') { let attr = { configurable: descriptors[key]['configurable'], enumerable: descriptors[key]['enumerable'], } if (descriptors[key]['writable']) { attr['writable'] = descriptors[key]['writable'] } // globalMy.console_log(descriptors[key]) if (descriptors[key]['get']) { let param_func_name = descriptors[key]['get'].name; globalMy[html_name][key + '_get'] = function () { let result = descriptors[key]['get'].apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } let func_attr_track = globalMy.set_func_prop(Object.getOwnPropertyDescriptors(descriptors[key]['get'])); var i = 0; while (i < func_attr_track.length){ Object.defineProperty(globalMy[html_name][key + '_get'],func_attr_track[i].name,func_attr_track[i].attr); i++; } globalMy.functionprotect(globalMy[html_name][key + '_get'], param_func_name, 'get') attr['get'] = globalMy[html_name][key + '_get'] } else { attr['get'] = undefined; } if (descriptors[key]['set']) { let param_func_name = descriptors[key]['set'].name; globalMy[html_name][key + '_set'] = function () { let result = descriptors[key]['set'].apply(this, arguments); globalMy.check_debug(key); globalMy.check_log(this, param_func_name, arguments, result); return result; } let func_attr_track = globalMy.set_func_prop(Object.getOwnPropertyDescriptors(descriptors[key]['set'])); var i = 0; while (i < func_attr_track.length){ Object.defineProperty(globalMy[html_name][key + '_set'],func_attr_track[i].name,func_attr_track[i].attr); i++; } globalMy.functionprotect(globalMy[html_name][key + '_set'], param_func_name, 'set') attr['set'] = globalMy[html_name][key + '_set'] } else { attr['set'] = undefined; } try { Object.defineProperty(html, key, attr); } catch (e) { debugger } } } } } globalMy.init = function() { console.clear = function () { }; globalMy.functionprotect(console.clear, "clear"); globalMy.dom = [MimeTypeArray, PluginArray, MimeType, Plugin, Request, Path2D, NodeList, MediaEncryptedEvent, MediaQueryList, InputDeviceCapabilities, IDBRequest, IDBOpenDBRequest, IDBFactory, CSSStyleDeclaration, Image, WebSocket, XMLHttpRequestEventTarget, XMLHttpRequest, EventTarget, Node, Element, HTMLElement, WebGLRenderingContext, CanvasRenderingContext2D, HTMLAnchorElement, HTMLImageElement, HTMLFontElement, HTMLOutputElement, HTMLAreaElement, HTMLInputElement, HTMLFormElement, HTMLParagraphElement, HTMLAudioElement, HTMLLabelElement, HTMLFrameElement, HTMLParamElement, HTMLBaseElement, HTMLLegendElement, HTMLFrameSetElement, HTMLPictureElement, HTMLBodyElement, HTMLLIElement, HTMLHeadingElement, HTMLPreElement, HTMLBRElement, HTMLLinkElement, HTMLHeadElement, HTMLProgressElement, HTMLButtonElement, HTMLMapElement, HTMLHRElement, HTMLQuoteElement, HTMLCanvasElement, HTMLMarqueeElement, HTMLHtmlElement, HTMLScriptElement, HTMLDataElement, HTMLMediaElement, HTMLIFrameElement, HTMLTimeElement, HTMLDataListElement, HTMLMenuElement, HTMLSelectElement, HTMLTitleElement, HTMLDetailsElement, HTMLMetaElement, HTMLSlotElement, HTMLTableRowElement, HTMLDialogElement, HTMLMeterElement, HTMLSourceElement, HTMLTableSectionElement, HTMLDirectoryElement, HTMLModElement, HTMLSpanElement, HTMLTemplateElement, HTMLDivElement, HTMLObjectElement, HTMLStyleElement, HTMLTextAreaElement, HTMLDListElement, HTMLOListElement, HTMLTableCaptionElement, HTMLTrackElement, HTMLEmbedElement, HTMLOptGroupElement, HTMLTableCellElement, HTMLUListElement, HTMLFieldSetElement, HTMLOptionElement, HTMLTableColElement, HTMLUnknownElement, HTMLTableElement, HTMLVideoElement] for (var i = 0; i < globalMy.dom.length - 1; i++) { globalMy.hook_HTML(globalMy.dom[i]) } //不想全局debug,可以关闭全局degbug,单独将你想要debug的方法名或者属性名传入进去 globalMy.want_debug = ['cookie'] // globalMy.want_debug = ['appendChild', 'createElement', 'target'] globalMy.not_log = ['get hidden', 'get visibilityState'] // 打印 鼠标事件 globalMy.hook_HTML(MouseEvent) globalMy.hook_HTML(Document) //hook Object.prototype 会把toString也hook了,会打印很多不必要的数据。可以hook,但是不要hook toString //globalMy.hook_HTML(Object) // hook Object 可以打印对方是否使用了Object里的函数做检测 globalMy.hook_obj(Object) } globalMy.init()
标签:脚本,func,name,descriptors,一键,globalMy,hook,key,attr From: https://www.cnblogs.com/yoyo1216/p/17585670.html