window对象下面不是所有对象下面的属性都能hook,我们只能hook
configurable可配置属性为true
下面是完整的hook 框架
直接上代码
ld={}
ld.reNameFunc=function reNameFunc (func,name){
Object.defineProperty(func,"name",{
configurable: true,
enumerable: false,
value: name,
writable: false
})
}
!(function (){
const $toString=Function.prototype.toString
const mysymbol=Symbol()
const toString=function (){
return typeof this==="function"&& this[mysymbol]|| $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",toString)
set_native(Function.prototype.toString,mysymbol,"function toString() { [native code] }")
ld.setNative=function (func,funcname){
set_native(func,mysymbol,`function ${funcname || func.name || ""}() { [native code] }`)
}
})()
//hook 实现hook插件的实现
ld.hook=function (func,funcInfo,isDebug,onEnter,onLeave,isExec){
//第一步先判断传进来的func的类型
if (!typeof func ==="function"){//如果传进来的不是一个函数我们就直接返回
return func
}
//funcInfo 关于函数的信息如果 是undefined 我们就自己给他有点补充
if(typeof funcInfo==="undefined"){
funcInfo={
}
funcInfo.Objectname="globalThis"//如果没有设置是哪个对象下面的函数 默认是全局下面
funcInfo.funcname=func.name || "" };
//是否进行断点
if(!isDebug){
isDebug=false//默认是false
}
//onEnter在进入函数执行前执行某些操作
if(!onEnter){
onEnter=function (arg){
console.log(`{hook|${funcInfo.Objectname}[${funcInfo.funcname}]} 正在被调用 调用的参数是 ${JSON.stringify(arg.args)}`);
}
}
//onLeave函数结束时执行默写操作
if(!onLeave){
onLeave=function (arg){
console.log(`{hook|${funcInfo.Objectname}[${funcInfo.funcname}]} 正在被调用 返回值是 ${JSON.stringify(arg.res)}`);
` `
}
}
//是否执行原函数
if(!isExec){
isExec=true
}
//要替换的函数
hookFunc=function (){
if(isDebug){
debugger
}
let arg={
}
arg.args=[]
for(let i=0;i<arguments.length;i++){
arg.args[i]=arguments[i]
}//把原函数执行的参数保存下来
//onEnter执行
onEnter.call(this,arg)
//原函数执行
if(isExec){
let res= func.apply(this,arg.args);
arg.res=res
}
onLeave.call(this,arg)
return arg.res
};
ld.setNative(hookFunc,funcInfo.funcname)
ld.reNameFunc(hookFunc,"name",funcInfo.funcname)
return hookFunc
}
//hook对象属性的本质就是替换属性描述符
ld.hookObj=function hookObj (obj,objName,propName,isDebug){
//obj 这个参数就是需要hook的对象
//objname 被hook对象的名字、
//propName 需要hook对象的的属性的名字
//isdebug 是否进行调试
//在进行hook之前我们就先把这个对象属性的描述符先保存一份
// 用上节讲过的这个方法获取一个对象属性描述符 Object.getOwnPropertyDescriptor
//第一个参数就是 对哪个对象进行属性获取 , 第二个参数就是要获取哪个属性的描述符
let initial_prop= Object.getOwnPropertyDescriptor(obj,propName);
//接下来声明一个我们要替换的属性描述符
let Replacement_prop={}//属性描述符本身就是一个 字典
//在属性描述符中有两个恒定的属性就是[[Configurable]][[Enumerable]]不知道啥意思的朋友去看上一级,有详细说明
//我们读取原来的属性描述符 ,添加到我们要替换的属性描述符中
//首先要看一下原先的属性描述符是不是可配置的 如果不可配置我们就无法进行替换
if(!initial_prop.configurable){
console.log("给属性不可以被配置")
return
};
Replacement_prop.configurable= initial_prop.configurable;
Replacement_prop.enumerable=initial_prop.enumerable;
if(initial_prop.hasOwnProperty("writable")){
Replacement_prop.writable= initial_prop.writable
}
if(initial_prop.hasOwnProperty("value")){
let value=initial_prop.value
if(!typeof value==="function"){
console.log("value不是一个函数")
return;
}
let funcInfo={
"objname":obj,
"name":propName,
}
Replacement_prop.value=ld.hook(value,funcInfo,isDebug)
}
if(initial_prop.hasOwnProperty("get")){
let get=initial_prop.get
let funcInfo={
"objname":obj,
"name":`get ${propName}`,
}
Replacement_prop.get=ld.hook(get,funcInfo,isDebug)
}
if(initial_prop.hasOwnProperty("set")){
let set=initial_prop.set
let funcInfo={
"objname":obj,
"name":`set ${propName}`,
}
Replacement_prop.set=ld.hook(set,funcInfo,isDebug)
}
Object.defineProperty(obj,propName,Replacement_prop)
}
ld.hookproto=function hookProto (protp,isdebug) {
let protoObj = protp.prototype
let name = protp.name
for (let i in Object.getOwnPropertyDescriptors(protoObj)) {
ld.hookObj(protoObj, `${name}.prototype`, i, isdebug)
}
console.log(`{hook} |${name}.prototype `);
};
//hook 全局对象lds
ld.hookWindow=function hookWindow(isdebug){
for (const key in Object.getOwnPropertyDescriptors(window)){
if(typeof window[key] ==="function"){
if(typeof window[key].prototype==='undefined'){
funcInfo={
};
funcInfo.objname="globalThis";
funcInfo.name=key;
//如果是普通函数 我们就走普通函数的流程
ld.hook(window[key],funcInfo,isdebug)
}else if(typeof window[key].prototype==='object'){
//如果是一个对象我们 就用hook对象属性的方式
ld.hookproto(window[key],isdebug)
}
}
}
}
复制粘贴将代码 直接复制粘贴到 浏览器的代码片段 然后运行
执行我们的 hook函数 ld.hookWindow()
下面是效果