当前位置: 首页 > news >正文

补环境基础(四) Hook插件

1.Hook函数插件

Hook函数:

可以在不修改原函数代码的情况下,在函数执行前后插入自定义逻辑,同时还能修改函数的toString()行为和name属性,让钩子函数看起来更像原生函数。

主要功能

1.自定义toString

模拟原生函数的输出(如function xxx() { [native code] })。

!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] }");xu.setnative= function (func,funcname){set_native(func,symbol,`function ${funcname || func.name ||''}() { [native code] }`);}}();

2.函数重命名

用于修改函数的name属性

xu.rename = function(func, name){Object.defineProperty(func, "name", {writable: false, // 不可写enumerable: false, // 不可枚举configurable: true, // 可配置(未来可修改)value: name // 新的name值})
}

3.自定义hook逻辑

在函数执行前后插入自定义逻辑(如日志、参数修改、返回值处理等)。

hook = function (func, funcInfo, isDebug, onEnter, onLeave, isExc) {
/*1.func 原函数,需要hook的函数2.funcInfo 是一个对象,传入 objName,funName  方便查看一些日志信息3.isDebug 是否进行调试  boolean类型  用于关键点定位4.onEnter 函数,原函数执行前的操作,改原函数的入参5.onLeave 原函数执行后的操作  可以操作原函数的返回值6.isExc 是否执行原函数
*/if (typeof func !== "function") return func; // 如果不是函数,直接返回// 初始化funcInfo(默认全局对象下的函数)if (funcInfo === undefined) {funcInfo = { objName: "globalThis", 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 (isExc === undefined) isExc = true;// 定义钩子包装函数const hookFunc = function () {if (isDebug) debugger; // 调试模式下触发断点// 收集参数const obj = { args: [] };for (let i = 0; i < arguments.length; i++) {obj.args[i] = arguments[i];}// 执行进入回调(原函数执行前)onEnter.call(this, obj);// 执行原函数(根据isExc决定是否执行)let result;if (isExc) {result = func.apply(this, obj.args); // 用apply保证this指向正确}obj.result = result;// 执行离开回调(原函数执行后)onLeave.call(this, obj);return obj.result; // 返回结果}// 让钩子函数看起来像原函数:设置toString和namexu.setnative(hookFunc, funcInfo.funcName);xu.rename(hookFunc, funcInfo.funcName);return hookFunc; // 返回包装后的钩子函数
}

完整代码及测试

xu={};
!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] }");xu.setnative= function (func,funcname){set_native(func,symbol,`function ${funcname || func.name ||''}() { [native code] }`);}}();
xu.rename = function(func,name){Object.defineProperty(func,"name",{writable:false,enumerable:false,configurable:true,value:name})
}
hook = function (func,funcInfo,isDebug,onEnter,onLeave,isExc){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(isExc === undefined){isExc = 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)//原函数正在执行let result;func.apply(this,obj.args);if(isExc) {result = func.apply(this, obj.args)}obj.result = result//原函数执行后onLeave.call(this,obj)return obj.result}xu.setnative(hookFunc,funcInfo.funcName)xu.rename(hookFunc,funcInfo.funcName)return hookFunc
}function add(a,b){return  a+b;};xm = {"objName":"obj","funcName":"add"
};
onEnter = function (obj){console.log(`[${xm.funcName}]调用前的参数${JSON.stringify(obj.args)}`)
}
onleave = function(obj){console.log(`[${xm.funcName}]调用后返回值[${obj.result}]`);
}add = hook(add,xm,true,onEnter,onleave,true);
console.log(add(1, 5));
console.log(add.toString())
console.log(add.name)
console.log(Function.prototype.toString.call(add))

2.Hook对象及原型插件

Hook对象插件

Hook对象插件:

用于hook对象的属性,如果该属性是函数(或访问器方法 get/set),则用之前定义的hook函数插件对其进行包装,从而在该属性(函数)执行前后插入自定义逻辑(如日志、调试等),同时保持原属性的其他特性(可配置性、可枚举性等)。

常用于需要监控或增强对象属性(尤其是方法)的场景,例如:监控window.alert的调用、拦截对象属性的读写等。

xu.hookObj = function hookObj(obj,objName,proName,isDebugger){/*1.obj:需要被 hook 的目标对象(如window、自定义对象等)。2.objName:目标对象的名称(字符串,用于日志或标识,如"window"、"myObj")。3.proName:需要 hook 的对象属性名(字符串,如"add"、"name")。4.isDebugger:是否开启调试模式(布尔值,开启后会触发debugger断点)。*/let oldDescripor = Object.getOwnPropertyDescriptor(obj,proName);let newDescripor = {};//是否可配置if(!oldDescripor.configurable){return}newDescripor.configurable = oldDescripor.configurable;//是否枚举newDescripor.enumerable = oldDescripor.enumerable;//是否可写oldDescripor.writableif(oldDescripor.hasOwnProperty("writable")){newDescripor.writable = oldDescripor.writable};//配置属性描述符Valueif(oldDescripor.hasOwnProperty("value")){let value = oldDescripor.value;if(typeof value != "function"){return;};let funcInfo = {"objName": objName,"funcName": proName}newDescripor.value = xu.hook(value,funcInfo,isDebugger)};//配置属性描述符getif(oldDescripor.hasOwnProperty("get")){let get = oldDescripor.get;let funcInfo = {"objName": objName,"funcName": `get ${proName}`}newDescripor.get = xu.hook(get,funcInfo,isDebugger)};//配置属性描述符setif(oldDescripor.hasOwnProperty("set")){let set = oldDescripor.get;let funcInfo = {"objName": objName,"funcName": `set ${proName}`}newDescripor.set = xu.hook(set,funcInfo,isDebugger)};Object.defineProperty(obj,proName,newDescripor);
}

Hook原型对象插件

Hook原型对象插件:

用于批量 hook 某个构造函数原型对象上的所有自身属性(包括方法、getter、setter 等),本质是对hookObj的进一步扩展,实现 “一劳永逸” 地监控某个类型所有实例的方法调用。

xu.hookProto = function hookProto (proto,isDebug){let protoObj = proto.prototype;let name = proto.name;for(const prop in Object.getOwnPropertyDescriptors(protoObj)){xu.hookObj(protoObj,`${name}.prototype`,prop,isDebug);}console.log(`hook ${name}.prototype`);
};
http://www.lryc.cn/news/619992.html

相关文章:

  • Spring Boot项目调用第三方接口的三种方式比较
  • 当img占不满div时,图片居中显示,两侧加当前图片模糊效果
  • 如何记录日常笔记?
  • 【Linux学习|黑马笔记|Day3】root用户、查看权限控制信息、chmod、chown、快捷键、软件安装、systemctl、软连接、日期与时区
  • 语音交互像聊天:声网RTC技术给AI客服加温度
  • 基于 MybatisPlus 将百度天气数据存储至 PostgreSQL 数据库的实践
  • 开发避坑指南(25):MySQL不支持带有limit语句的子查询的解决方案
  • Java研学-RabbitMQ(六)
  • 算法题详细解析 + 代码 + 注释
  • 在 uniapp 里使用 unocss,vue3 + vite 项目
  • 数据结构初阶(12)排序算法—插入排序(插入、希尔)(动图演示)
  • 智驾系统架构解析
  • 常用机器学习公开数据集大全
  • [系统架构设计师]系统架构基础知识(一)
  • [系统架构设计师]信息安全技术基础知识(三)
  • DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
  • 最新去水印小程序系统 前端+后端全套源码 多套模版 免授权
  • TF-IDF实战——《红楼梦》文本分析
  • 商品分类拖拽排序设计
  • 用 Qt C++ 从零打通“前端界面 → 后端接口”的数据交互
  • Redis的基础命令
  • 图像分类-动手学计算机视觉10
  • RabbitMQ:Windows版本安装部署
  • 高防CDN和高防IP的各自优势
  • Vue项目生产环境性能优化实战指南
  • 【Java虚拟机】JVM内存模型
  • uniapp跨端性能优化方案
  • 中科米堆CASAIM蓝光三维扫描仪用于焊接件3D尺寸检测
  • GDB命令笔记
  • 【React】use-immer vs 原生 Hook:谁更胜一筹?