您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
2023/11/15 14:17:13
// ==UserScript== // @name qchook // @namespace qchook // @match *://*/* // @grant none // @version 1.2 // @author cthousand // @description 2023/11/15 14:17:13 // @license MIT // ==/UserScript== window.yqvm = { "toolsFunc": {},//功能函数相关,插件 "memory": {}, // 内存 'hook': {}, 'proxy': {} } yqvm.debug_array = [] //用于debugger yqvm.memory.symbolProxy = Symbol("proxy");// 独一无二的属性, 标记是否已代理 yqvm.memory.symbolData = Symbol("data");// 用来保存当前对象上的原型属性 yqvm.memory.symbolToString = Symbol("toString");// 用来保存当前对象上的原型属性 yqvm.memory.symbolRawFatherObj = Symbol('fatherObj')//用于更改apply行为内部this指向到原生对象,而非Proxy对象 yqvm.memory.symbolRawSelfObj = Symbol('selfObj')//用于更改construct行为内部this指向到原生对象,而非Proxy对象 yqvm.memory.symbolObjName = Symbol('name')//用于表示被创建的元素名称,方便在内存中通过此属性精准定位到 yqvm.memory.symbolRawName = Symbol('rawName')//用于表示被创建的元素名称,方便在内存中通过此属性精准定位到 // 函数native化 !function () { const $toString = Function.prototype.toString; const myToString = function () { return typeof this === 'function' && this[yqvm.memory.symbolToString] || $toString.call(this); } function set_native(func, key, value) { Object.defineProperty(func, key, { enumerable: false, configurable: true, writable: true, value: value }); } delete Function.prototype.toString; set_native(Function.prototype, "toString", myToString); set_native(Function.prototype.toString, yqvm.memory.symbolToString, "function toString() { [native code] }"); yqvm.toolsFunc.setNative = function (func, funcname) { set_native(func, yqvm.memory.symbolToString, `function ${funcname || func.name || ''}() { [native code] }`); } }(); // 函数重命名 yqvm.toolsFunc.reNameFunc = function reNameFunc(func, name) { Object.defineProperty(func, "name", { configurable: true, enumerable: false, writable: true, value: name }); } // hook 插件 yqvm.toolsFunc.hook = function hook(func, funcInfo, isDebug, onEnter, onLeave, isExec) { // func : 原函数,需要hook的函数 // funcInfo: 是一个对象,objName,funcName属性 // isDebug: 布尔类型, 是否进行调试,关键点定位,回溯调用栈 // onEnter:函数, 原函数执行前执行的函数,改原函数入参,或者输出入参 // onLeave: 函数,原函数执行完之后执行的函数,改原函数的返回值,或者输出原函数的返回值 // isExec : 布尔, 是否执行原函数,比如无限debuuger函数 if (typeof func !== 'function') { return func; } if (funcInfo === undefined) { funcInfo = {}; funcInfo.objName = "globalThis"; funcInfo.funcName = func.name || ''; } if (isDebug === undefined) { isDebug = false; } if (!onEnter) { onEnter = function (obj) { yqvm.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); } } if (!onLeave) { onLeave = function (obj) { yqvm.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,返回值是[${obj.result}}]`); } } if (isExec === undefined) { isExec = true; } // 替换的函数 let hookFunc = function () { if (isDebug) { debugger; } let obj = {}; obj.funcInfo = funcInfo obj.args = []; for (let i = 0; i < arguments.length; i++) { obj.args[i] = arguments[i]; } // 原函数执行前 onEnter.call(this, obj); // onEnter(obj); // 原函数正在执行 let result; if (isExec) { result = func.apply(this, obj.args); } obj.result = result; // 原函数执行后 onLeave.call(this, obj); // onLeave(obj); // 返回结果 return obj.result; } // hook 后的函数进行native yqvm.toolsFunc.setNative(hookFunc, funcInfo.funcName); yqvm.toolsFunc.reNameFunc(hookFunc, funcInfo.funcName); return hookFunc; } //自定义log yqvm.log = yqvm.toolsFunc.hook( console.log, undefined, false, function () { }, function () { }, true ); // hook 对象的属性,本质是替换属性描述符 yqvm.toolsFunc.hookObj = function hookObj(obj, objName, propName, isDebug, onEnter, onLeave, isExec) { // obj :需要hook的对象 // objName: hook对象的名字 // propName: 需要hook的对象属性名 // isDubug: 是否需要debugger let oldDescriptor = Object.getOwnPropertyDescriptor(obj, propName); let newDescriptor = {}; if (!oldDescriptor.configurable) { // 如果是不可配置的,直接返回 return; } // 必须有的属性描述 newDescriptor.configurable = true; newDescriptor.enumerable = oldDescriptor.enumerable; if (oldDescriptor.hasOwnProperty("writable")) { newDescriptor.writable = oldDescriptor.writable; } if (oldDescriptor.hasOwnProperty("value")) { let value = oldDescriptor.value; if (typeof value !== "function") { return; } let funcInfo = { "objName": objName, "funcName": propName } newDescriptor.value = yqvm.toolsFunc.hook(value, funcInfo, isDebug, onEnter, onLeave, isExec); } if (oldDescriptor.hasOwnProperty("get")) { let get = oldDescriptor.get; let funcInfo = { "objName": objName, "funcName": `get ${propName}` } newDescriptor.get = yqvm.toolsFunc.hook(get, funcInfo, isDebug, onEnter, onLeave, isExec); } if (oldDescriptor.hasOwnProperty("set")) { let set = oldDescriptor.set; let funcInfo = { "objName": objName, "funcName": `set ${propName}` } newDescriptor.set = yqvm.toolsFunc.hook(set, funcInfo, isDebug, onEnter, onLeave, isExec); } Object.defineProperty(obj, propName, newDescriptor); } // Proxy yqvm.toolsFunc.proxy = function proxy(obj, objName) { // obj: 原始对象 // objName: 原始对象的名字 // 代理前需要注意,一些configuable为false的对象不可被代理,需要滤过 let RawFatherObj if (Object.hasOwnProperty.call(obj, yqvm.memory.symbolRawFatherObj)) { RawFatherObj = obj[yqvm.memory.symbolRawFatherObj] } else { RawFatherObj = void 0 } let RawName = Object.hasOwnProperty.call(obj, yqvm.memory.symbolRawName) && obj[yqvm.memory.symbolRawName] let propertyDes = RawFatherObj && Object.getOwnPropertyDescriptor(RawFatherObj, RawName) let configurable = propertyDes && propertyDes['configurable'] if (configurable === false && !Object.hasOwnProperty.call(RawFatherObj, yqvm.memory.symbolToString)) { //构造函数默认prototype是不可配置的,这里用双层确认以过滤出内置对象 return obj } if (!yqvm.config.proxy) { return obj; } if (Object.hasOwnProperty.call(obj, yqvm.memory.symbolProxy)) { return obj[yqvm.memory.symbolProxy] } let handler = { get: function (target, prop, receiver) {// 三个参数 let result; // if(prop==='href'){debugger} try {//防止报错 result = Reflect.get(target, prop, receiver); //拦截 if (objName === 'document' && prop === 'all') { yqvm.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],该对象为防typeof检测不设代理}`); // debugger return result } if (yqvm.toolsFunc.filterProxyProp(prop)) { return result; //Proxy对象白名单 } let type = yqvm.toolsFunc.getType(result); if (result instanceof Object) { yqvm.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); //将原生父对象(相对于result)的地址存到result的symbol属性中,方便在apply行为中更改this指向 Object.defineProperty(result, yqvm.memory.symbolRawFatherObj, { configurable: true, enumerable: false, writable: true, value: target }) //将原生对象地址存到result的symbol属性中,方便在construct行为中更改this指向 Object.defineProperty(result, yqvm.memory.symbolRawSelfObj, { configurable: true, enumerable: false, writable: true, value: result }) //将对象名字存到result的symbol属性中,在过滤不可配置属性时(Object.hasOwnProperty)作为prop字符串 Object.defineProperty(result, yqvm.memory.symbolRawName, { configurable: true, enumerable: false, writable: true, value: prop }) // 递归代理 result = yqvm.toolsFunc.proxy(result, `${objName}.${prop.toString()}`); } else if (typeof result === "symbol") { yqvm.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result.toString()}]}`); } else { yqvm.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result}]}`); } } catch (e) { yqvm.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, set: function (target, prop, value, receiver) { let result; try { result = Reflect.set(target, prop, value, receiver); if (yqvm.toolsFunc.filterProxyProp(prop)) { return result; //Proxy对象白名单 } let type = yqvm.toolsFunc.getType(value); if (value instanceof Object) { yqvm.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); } else if (typeof value === "symbol") { yqvm.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value.toString()}]}`); } else { yqvm.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value}]}`); } } catch (e) { yqvm.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, // getOwnPropertyDescriptor: function (target, prop) { // let result;// undefined, 描述符对象 // try { // result = Reflect.getOwnPropertyDescriptor(target, prop); // let type = yqvm.toolsFunc.getType(result); // //过滤一些不希望输出的日志,如SymbolProxy,SymbolDate // if (!yqvm.toolsFunc.filterLogProxyProp(prop)) { // yqvm.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); // } // // if(typeof result !== "undefined"){ // // result = yqvm.toolsFunc.proxy(result, `${objName}.${prop.toString()}.PropertyDescriptor`); // // } // } catch (e) { // yqvm.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); // } // return result; // }, // defineProperty: function (target, prop, descriptor) { // let result; // try { // //将this执行原生对象 // //target=target[yqvm.memory.symbolRawFatherObj] // result = Reflect.defineProperty(target, prop, descriptor); // //过滤一些不希望输出的日志,如SymbolProxy,SymbolDate // if (!yqvm.toolsFunc.filterLogProxyProp(prop)) { // yqvm.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}]}`); // } // } catch (e) { // debugger // yqvm.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); // } // return result; // }, apply: function (target, thisArg, argumentsList) { // target: 函数对象 // thisArg: 调用函数的this指针 // argumentsList: 数组, 函数的入参组成的一个列表 // debug let result; try { // if(argumentsList.indexOf('iframe')!==-1){debugger} //特殊函数过滤(不可被执行) // if(['URL'].indexOf(objName)!==-1){ // return yqvm.toolsFunc.throwError("TypeError", "Failed to construct 'URL': 1 argument required, but only 0 present.") // } result = Reflect.apply(target, thisArg, argumentsList); let type = yqvm.toolsFunc.getType(result); if (result instanceof Object) { let result_objName = `${objName}_obj${yqvm.memory.globalVar.id++}` result = yqvm.toolsFunc.proxy(result, result_objName) //让function返回的对象也自动套上代理 yqvm.log(`{apply|function:[${objName}], args:[${argumentsList}], type:[${type}]}`); } else if (typeof result === "symbol") { yqvm.log(`{apply|function:[${objName}], args:[${argumentsList}], result:[${result.toString()}]}`); } else { yqvm.log(`{apply|function:[${objName}], args:[${argumentsList}], result:[${result}]}`); } } catch (e) { yqvm.log(`{apply|function:[${objName}],error:[${e.message}]}`); if (e[yqvm.memory.symbolError]) { yqvm.toolsFunc.throwError(e['name'], e['message']) } } return result; }, construct: function (target, argArray, newTarget) { // target: 函数对象 // argArray: 参数列表 // newTarget:代理对象 let result; try { result = Reflect.construct(target, argArray, newTarget); let type = yqvm.toolsFunc.getType(result); yqvm.log(`{construct|function:[${objName}], argArray:[${argArray}] , type:[${type}]}`); } catch (e) { yqvm.log(`{construct|function:[${objName}],error:[${e.message}]}`); } return result; }, deleteProperty: function (target, propKey) { let result = Reflect.deleteProperty(target, propKey); yqvm.log(`{deleteProperty|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`); return result; }, // has: function (target, propKey) { // in 操作符 // let result = Reflect.has(target, propKey); // if (!yqvm.toolsFunc.filterLogProxyProp(propKey)) { // yqvm.log(`{has|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`); // } // return result; // }, // ownKeys: function (target) { // let result = Reflect.ownKeys(target); // yqvm.log(`{ownKeys|obj:[${objName}]}`); // return result // }, // getPrototypeOf: function (target) { // let result = Reflect.getPrototypeOf(target); // yqvm.log(`{getPrototypeOf|obj:[${objName}]}`); // return result; // }, // setPrototypeOf: function (target, proto) { // let result = Reflect.setPrototypeOf(target, proto); // yqvm.log(`{setPrototypeOf|obj:[${objName}]}`); // return result; // }, // preventExtensions: function (target) { // let result = Reflect.preventExtensions(target, proto); // yqvm.log(`{preventExtensions|obj:[${objName}]}`); // return result; // }, // isExtensible: function (target) { // let result = Reflect.isExtensible(target, proto); // yqvm.log(`{isExtensible|obj:[${objName}]}`); // return result; // } }; let proxyObj = new Proxy(obj, handler); //防止重复代理,导致代理被用嵌套对比的方式检测出来 Object.defineProperty(obj, yqvm.memory.symbolProxy, { configurable: true, enumerable: false, writable: false, value: proxyObj }) return proxyObj } //hook方法写在下面 //hook cookie yqvm.hook.Document_cookie = function (isDebug, args) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); if (obj.funcInfo.funcName === 'set cookie' && obj.args[0].indexOf(args) !== -1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(Document.prototype, 'Document.prototype', 'cookie', false, onEnter, undefined, true) } //hook xhr_open yqvm.hook.XMLHttpRequest_open = function (isDebug, args) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); if (obj.args[1].indexOf(args) !== -1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(XMLHttpRequest.prototype, 'XMLHttpRequest.prototype', 'open', false, onEnter, undefined, true) } //hook xhr_send yqvm.hook.XMLHttpRequest_send = function (isDebug, data) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); if (obj.args[0] && obj.args[0].indexOf(data) !== -1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(XMLHttpRequest.prototype, 'XMLHttpRequest.prototype', 'send', false, onEnter, undefined, true) } //hook XMLHttpRequest.prototype.onreadystatechange yqvm.hook.XMLHttpRequest_onreadystatechange = function (isDebug) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用`); if (this.responseURL.indexOf("batchexecute?rpcids=M0CRd") != -1 && this.readyState === 4 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(XMLHttpRequest.prototype, 'XMLHttpRequest.prototype', 'onreadystatechange', false, onEnter, undefined, true) } //hook XMLHttpRequest.prototype.responseText yqvm.hook.XMLHttpRequest_responseText = function (isDebug) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用`); if (this.responseURL.indexOf("batchexecute?rpcids=M0CRd") != -1 && this.readyState === 4 && this.response.indexOf("g04aAb") != -1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(XMLHttpRequest.prototype, 'XMLHttpRequest.prototype', 'responseText', false, onEnter, undefined, true) } //hook HTMLCanvasElement.prototype.toDataURL yqvm.hook.HTMLCanvasElement_toDataURL = function (isDebug) { let onLeave = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用|result:[${obj.result}]`); if (isDebug) { debugger } } yqvm.toolsFunc.hookObj(HTMLCanvasElement.prototype, 'HTMLCanvasElement.prototype', 'toDataURL', false, undefined, onLeave, true) } //hook window_open yqvm.hook.window_open = function (isDebug,args) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); if (obj.args[0].indexOf(args) !== -1 && isDebug) { yqvm.log(obj.args[0]); debugger } } yqvm.toolsFunc.hookObj(window, 'window', 'open', false, onEnter, undefined, true) } //hook window_close yqvm.hook.window_close = function (isDebug) { yqvm.toolsFunc.hookObj(window, 'window', 'open', isDebug, undefined, undefined, true) } //hook window_close yqvm.hook.window_close = function (isDebug) { yqvm.toolsFunc.hookObj(window, 'window', 'open', isDebug, undefined, undefined, true) } //hook window_btoa yqvm.hook.window_btoa = function (isDebug,args) { let onLeave = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用|result:[${obj.result}]`); if (obj.result.indesOf(args)!==-1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(window, 'window', 'btoa', false, undefined, undefined, true) } //hook window_atob yqvm.hook.window_atob = function (isDebug,args) { let onEnter = function (obj) { yqvm.log(`{hook|${obj.funcInfo.objName}[${obj.funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`); if (obj.args[0].indexOf(args) !== -1 && isDebug) { debugger } } yqvm.toolsFunc.hookObj(window, 'window', 'atob', false, undefined, undefined, true) } // yqvm.hook.XMLHttpRequest_send(true,'ua') yqvm.hook.Document_cookie(true,"EqLJ0qhYnviOGCTm")