qchook

2023/11/15 14:17:13

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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