hook钩子机制

299 阅读2分钟

定义

Hook 技术又叫做钩子函数,在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递 简单来说,就是把系统的程序拉出来变成我们自己执行代码片段

函数中常用方法,统一管理

    // 使用前
    function oldFun (errCode) {
      if (errCode) {
        if (errCode === '001') {
          console.log('错误一');
        } else if (errCode === '002') {
          console.log('错误二');
        } else if (errCode === '003') {
          console.log('错误三');
        }
      }
    }
    // 使用后
    const code = {
      '001': '错误一',
      '002': '错误二',
      '003': '错误三'
    };
    function newFun (errCode) {
      if (errCode) {
        console.log(code[errCode]);
      }
    }
    // 使用
    newFun('002');// 输出错误二

函数hook公式

Function的函数
    const old_alert = alert;
    console.log('旧alert方法toString打印:', old_alert.toString()); // 输出结果:旧alert方法toString打印: function alert() { [native code] }
    // 重写函数
    alert = function () {
      console.log('开始弹窗', JSON.stringify(arguments));
      return old_alert.apply(this, arguments);
    }
    // 函数上做alert防hook检测
    alert.toString = function () {
      return 'function alert() { [native code] }'
    }
    // 原型链上做alert防hook检测
    const old_prototype_alert = Function.prototype.toString.call;
    Function.prototype.toString.call = function (arg) {
      if (arg === alert) {
        return 'function alert() { [native code] }'
      }
      return old_prototype_alert(arg);
    }
    alert('abc');
    console.log('函数上做hook做防检测:', alert.toString()); // 输出结果:hook做防检测: function alert() { [native code] }
    console.log('原型链上做hook做防检测:', Function.prototype.toString.call(alert)); // 输出结果:hook做防检测: function alert() { [native code] }
自定义函数
    function func (a, b) {
      return a + b;
    }
    const old_func = func;
    func = function () {
      console.log('执行func方法,参数为:', JSON.stringify(arguments));
      return old_func.apply(this, arguments);
    }
    func(1, 2); // 输出 3

原型链hook

    String.prototype.split_old = String.prototype.split;
    String.prototype.split = function (value) {
      str = this.toString();
      console.log('开始执行字符串截取');
      // debugger;
      return str.split_old(value);
    };

JSON hook

    //将 JavaScript 值转换为 JSON 字符串
    (function () {
      var stringify = JSON.stringify;
      JSON.stringify = function (params) {
        console.log("Hook JSON.stringify ——> ", params);
        debugger;
        return stringify(params);
      }
    })();

    // 将字符串解析成对象,多用于返回加密数据
    (function () {
      var parse = JSON.parse;
      JSON.parse = function (params) {
        console.log("Hook JSON.parse ——> ", params);
        debugger;
        return parse(params);
      }
    })();

对象中属性hook

    const lsData = {
      name: 'abc'
    }
    let oldName = lsData.name;
    Object.defineProperty(lsData, 'name', {
      get: function () {
        console.log('开始【获取】lsData的name属性:', oldName);
        return oldName;
      },
      set: function (val) {
        console.log('开始【赋值】lsData的name属性:', val);
        oldName = val
        return val;
      }
    })

一键快速hook框架

    (() => {
      const $toString = Function.toString
      const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random()) + '').toString(36))
      const myToString = function () {
        return typeof this === 'function' && this[myFunction_toString_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, myFunction_toString_symbol, "function toString() { [native code] }")
      globalThis.func_set_native = (func) => {
        set_native(func, myFunction_toString_symbol, `function ${func.name || ''}() { [native code] }`)
      }
    }).call(this);

    window.dta = {}
    Function.prototype.hook = function (onEnter, onLeave, context, Funcname) {
      if (!onEnter) {
        onEnter = function (warpper) {
          var args = warpper.args;
          console.log(args)
        }
      }
      if (!onLeave) {
        onLeave = function (retval) {
          console.log(retval)
        }
      }

      // btoa.hook()
      var _context = context || window;
      var FuncName = this.name || Funcname;
      if (!FuncName) {
        console.error("hook function name is empty!")
        return false
      }
      window.dta[FuncName] = this;


      _context[FuncName] = function () {
        var args = Array.prototype.slice.call(arguments, 0)
        var _this = this
        var warpper = {
          args
        }

        onEnter.call(_this, warpper)
        // this -> window
        var retval = window.dta[FuncName].apply(this, warpper.args)

        var hook_retval = onLeave.call(_this, retval)
        if (hook_retval) {
          return hook_retval
        }
        return retval
      }
      Object.defineProperty(_context[FuncName], "name", {
        get: function () {
          return FuncName
        }
      })
      func_set_native(_context[FuncName])
    }

    console.log("quick hook start")
    ```