// ==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")