Hook插件

1、hook原理作用

原理:重新定义函数,使得调用位置走我们重新定义的函数,改变执行流程
作用: 输出分析日志,定位关键点, 返回结果


// 定义函数
function add(a, b){
    return a + b;
}
// 保留原函数
_add = add;
// 函数加载完成 hook位置
add = function (a, b){
    return a - b;
}

// 调用函数
console.log(add(1, 2))

2、hook对象属性

let user= {
    "name": "Xiao"
}
// hook
_name = user.name;
Object.defineProperty(user, "name", {
    get(){// 获取属性值的时候执行
        console.log("获取属性值的时候执行")
        return _name;
    },
    set(value) {// 设置属性值的时候执行
        console.log("设置属性值的时候执行")
        _name = value;
    }
})
// 获取属性值操作
console.log(user.name)
// 设置属性值的操作
user.name = "Big"
// 重新获取值
console.log(user.name)

3、hook浏览器环境

_atob = atob;
atob = function (str){
    console.log("正在执行atob方法:", str)
    let result = _atob(str);
    console.log("正在执行atob方法, 返回值:", result)
    return result
}

因为atob为浏览器自带函数, 所以选择Hook时机应该在最前面
选择source面板, 选择Event Listener Breakpoints下的 Script进行断点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、hook检测与保护

通过toSiring()的方式来进行检测有没有进行hook
在这里插入图片描述
重写toString方式使得结果保持一致
在这里插入图片描述
使用原型链的结果进行比较
在这里插入图片描述
修改原型链返回值
在这里插入图片描述
Location.toString()进行比较
在这里插入图片描述
使用this.name输出原对象名称

Function.prototype.toString = function(){
    return `function ${this.name}() { [native code] }`
}

在这里插入图片描述

5、函数native化

!function (){
    const $toString = Function.prototype.toString;
    const symbol = Symbol(); // 独一无二的属性
    const myToString = function (){
        return typeof this === 'function' && this[symbol] || $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, symbol, "function toString() { [native code] }");
    globalThis.setNative = function (func, funcname) {
        set_native(func, symbol, `function ${funcname || func.name || ''}() { [native code] }`);
    }
}();

// 自执行函数

add = function add(a, b){
    return a + b;
}
console.log(globalThis === global); // ture
setNative(add, "add"); // 添加了一个Symbol类型的属性,值是function add() { [native code] }
console.log(add.toString());
console.log(Function.prototype.toString.call(add));
console.log(Function.prototype.toString.toString());

6、函数重命名

查看属性描述符
在这里插入图片描述
修改

// 函数重命名
reNameFunc = function reNameFunc(func, name){
    Object.defineProperty(func, "name", {
        configurable: true,
        enumerable: false,
        writable: false,
        value: name
    })
}

add = function get(){
    return 1
}

reNameFunc(add, "add")

console.log(add.name)

7、实现hook插件

ld = {}; // 全局对象

// 函数native化
!function (){
    const $toString = Function.prototype.toString;
    const symbol = Symbol(); // 无一无二的属性
    const myToString = function (){
        return typeof this === 'function' && this[symbol] || $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, symbol, "function toString() { [native code] }");
    ld.setNative = function (func, funcname) {
        set_native(func, symbol, `function ${funcname || func.name || ''}() { [native code] }`);
    }
}();

// 函数重命名
ld.reNameFunc = function reNameFunc(func, name){
    Object.defineProperty(func, "name", {
        configurable:true,
        enumerable:false,
        writable:false,
        value:name
    });
}

// hook 插件
hook = function (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){
            console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`);
        }
    }
    if(!onLeave){
        onLeave = function (obj){
            console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,返回值是[${obj.result}}]`);
        }
    }
    if(isExec === undefined){
        isExec = true;
    }
    // 替换的函数
    hookFunc = function (){
        if(isDebug){
            debugger;
        }
        let obj = {};
        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
    ld.setNative(hookFunc, funcInfo.funcName);
    ld.reNameFunc(hookFunc, funcInfo.funcName);
    return hookFunc;
}


function add(a,b){
    // 输出信息
    console.log("正在执行原函数add方法");
    return a+b;
}

let funcInfo = {
    "objName":"Obj",
    "funcName": "add"
}
onEnter = function (obj) {
    console.log("正在执行原函数调用前的操作", obj.args);
    obj.args[0] = 10;
}
onLeave = function (obj) {
    console.log("正在执行原函数调用后的操作", obj.result);
    obj.result = 16;
}

add = hook(add, funcInfo, true, onEnter, onLeave, true); // hook 了add函数
add(1, 5)
console.log(add.toString());
console.log(Function.prototype.toString.call(add));
console.log(add.name);

8、hook原型和window对象所有属性

// hook全局window下的函数与原型


ld = {}; // 全局对象

// 函数native化
!function (){
    const $toString = Function.prototype.toString;
    const symbol = Symbol(); // 无一无二的属性
    const myToString = function (){
        return typeof this === 'function' && this[symbol] || $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, symbol, "function toString() { [native code] }");
    ld.setNative = function (func, funcname) {
        set_native(func, symbol, `function ${funcname || func.name || ''}() { [native code] }`);
    }
}();

// 函数重命名
ld.reNameFunc = function reNameFunc(func, name){
    Object.defineProperty(func, "name", {
        configurable:true,
        enumerable:false,
        writable:false,
        value:name
    });
}

// hook 插件
ld.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){
            console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`);
        }
    }
    if(!onLeave){
        onLeave = function (obj){
            console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,返回值是[${obj.result}}]`);
        }
    }
    if(isExec === undefined){
        isExec = true;
    }
    // 替换的函数
    hookFunc = function (){
        if(isDebug){
            debugger;
        }
        let obj = {};
        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
    ld.setNative(hookFunc, funcInfo.funcName);
    ld.reNameFunc(hookFunc, funcInfo.funcName);
    return hookFunc;
}

// hook 对象的属性,本质是替换属性描述符
ld.hookObj = function hookObj(obj, objName, propName, isDebug){
    // 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 = ld.hook(value,funcInfo ,isDebug);
    }
    if(oldDescriptor.hasOwnProperty("get")){
        let get = oldDescriptor.get;
        let funcInfo = {
            "objName": objName,
            "funcName": `get ${propName}`
        }
        newDescriptor.get = ld.hook(get,funcInfo ,isDebug);
    }
    if(oldDescriptor.hasOwnProperty("set")){
        let set = oldDescriptor.set;
        let funcInfo = {
            "objName": objName,
            "funcName": `set ${propName}`
        }
        newDescriptor.set = ld.hook(set,funcInfo ,isDebug);
    }
    Object.defineProperty(obj, propName, newDescriptor);
}

// hook 原型对象的所有属性
ld.hookProto = function hookProto(proto, isDebug){
    // proto :函数原型
    // isDebug: 是否debugger
    let protoObj = proto.prototype;
    let name = proto.name;
    for(const prop in Object.getOwnPropertyDescriptors(protoObj)){
        ld.hookObj(protoObj, `${name}.prototype`, prop, isDebug);
    }
    console.log(`hook ${name}.prototype`);
}

// 普通函数: atob,btoa
// 原型函数: Document, Element
// hook 全局对象
ld.hookGlobal = function hookGlobal(isDubeg){
    for(const key in Object.getOwnPropertyDescriptors(window)){
        if(typeof window[key] === "function"){
            if(typeof window[key].prototype === "object"){
                // 函数原型
                ld.hookProto(window[key], isDubeg);
            }else if(typeof window[key].prototype === 'undefined'){
                // 普通函数
                let funcInfo = {
                    "objName": "globalThis",
                    "funcName": key
                }
                ld.hook(window[key], funcInfo, isDubeg);
            }
        }
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值