二. hook插件
1.概念
在JavaScript中,hook是一种能够拦截和修改函数或方法行为的技术。通过使用hook,开发者可以在现有的函数执行前、执行后或者替换函数的实现逻辑。hook目的是找到函数入口以及一些参数变化,便于分析js逻辑。
2.hook的作用:
- 增强代码的可扩展性:Hook技术允许开发者在不修改原始代码的情况下,增加或修改功能,使得代码更加灵活和可扩展。
- 减少代码的侵入性:使用hook可以在不改变原始代码的前提下增加新功能,这减少了对原始代码的侵入,使得添加的功能更容易被管理和维护。
- 便于调试和问题定位:利用hook技术可以在函数执行前后插入调试信息,帮助开发者更好地理解程序执行流程和定位问题源头。
3. hook基本使用
3.1 函数的hook
- 定义函数
// 定义函数
function add(a,b){console.log("add方法正在执行");return a+b;
}
- 保存原函数,目的是为了不修改原函数内部的实现
_add = add;
- 对add函数进行hook(进行相关的日志输出)
- hook的位置必须是加载完需要hook的函数(原函数)后
add = function(a,b){console.log("原函数调用前, 参数:", a, b);let result = _add(a,b)console.log("原函数调用后, 结果:", result);return result;
}
- 调用函数
add(1,2)
3.2 对象属性的hook
//1、创建一个对象
let user = {"name": "波波",
};//2、保存原属性
_name = user.name;//3、对象属性的hook
//defineProperty函数用来重新定义对象的属性。
//参数1:对象。参数2:属性。参数3:属性描述符
Object.defineProperty(user, "name",{get(){ // 获取属性值的时候执行console.log("正在获取属性值");return _name;},set(value){ // 设置属性值的时候执行console.log("正在设置属性值:", value);_name = value;}
});//4、获取属性和设置属性操作
console.log(user.name)
user.name = 'Jay'
console.log(user.name)
如果对象没有/不存在的属性可以被hook吗?
//1、创建一个对象
let user = {"name": "波波",
};//2、保存原属性
_age = 18;//3、对象属性age的hook
Object.defineProperty(user, "age",{get(){ console.log("正在获取属性值");return _age;},set(value){ console.log("正在设置属性值:", value);_age = value;}
});//4、获取属性和设置属性操作
console.log(user.age)
user.age = 20
console.log(user.age)
3.3 浏览器环境下atob函数的hook
atob函数是浏览器环境自带的用来对base64数据进行解编码。接下来,使用对atob函数进行hook。
-
hook时机:在浏览器页面加载出来之前进行hook
-
1.在一个空白页面打开浏览器开发者工具
-
2.开启js的事件监听器
-
3.访问百度页面,会有断点停留
-
4.在Sources中的Snippets代码段中新增hook代码片段,打上断点,然后运行
-
编写hook操作:
_atob = atob;//保存原函数atob = function (str){console.log("正在执行atob方法, 参数:", str);let result = _atob(str);console.log("正在执行atob方法, 返回值:", result);return result; }

-
-
-
5.查看hook运行,监控atob函数的执行
-
取消事件监听器中的Script,因为此时已经成功对atob函数进行了hook(不可刷新页面)
-
3.4 浏览器环境下cookie的hook
- 操作步骤如步骤:3.3
_cookie = document.cookie;
Object.defineProperty(document,'cookie',{get(){console.log("正在获取cookie:", _cookie);return _cookie;},set(value){console.log("正在设置cookie:", value);_cookie = value;}
});
3.4 hook检测与破解检测
一些网站会严格检测该网站中的先关函数或者属性是否被一些别有用心的人进行hook。那么检测方式是什么呢?我们又该如何破解该种检测呢?
-
toString() 检测法
-
atob原函数的toString() 结果为:
-
atob被hook后的toString() 结果为:
-
结果:两个atob的toString返回的结果是不一样的。
什么是native?- 在js中,一些内置函数如toString或者atob等函数的函数实现会被显示为[native code],而不是显示实现的具体代码。这样的操作对于提高代码的安全性和封装性有一定的作用。
-
-
toString() 检测法的破解
- 在hook中重写atob函数的toStirng方法:
_atob = atob;//保存原函数atob = function (str){console.log("正在执行atob方法, 参数:", str);let result = _atob(str);console.log("正在执行atob方法, 返回值:", result);return result; } //重写atob函数的toString方法 atob.toString = function(){return 'function atob() { [native code] }' }
-
原型链上的toString()检测法
Function.prototype.toString.call(atob) //调用函数原型对象中的toString进行的检测,而不是atob实例对象的toString了。
-
原型链上的toString()检测法的破解
- 在hook中重写原型链上的toString()方法:
_atob = atob;//保存原函数atob = function (str){console.log("正在执行atob方法, 参数:", str);let result = _atob(str);console.log("正在执行atob方法, 返回值:", result);return result; } //重写原型链上的toString方法 Function.prototype.toString = function(){return `function ${this.name}() { [native code] }` }//this.name就是toString的调用者的名字,比如Location.toString,则this.name就是Location,如果将this.name直接换成atob的话,则以后任何调用者调用toString的话,则返回的function后面的名字就都是atob了。也就是如果Location.toString()返回的也是:function atob() { [native code] }
三. proxy代理机制
1. 概念
JavaScript中的Proxy是一种内置对象,它允许你在访问或操作对象之前拦截和自定义底层操作的行为。通过使用Proxy,你可以修改对象的默认行为,添加额外的逻辑或进行验证,以实现更高级的操作和控制。
Proxy对象包装了另一个对象(目标对象),并允许你定义一个处理程序(handler)来拦截对目标对象的操作。处理程序是一个带有特定方法的对象,这些方法被称为"捕获器"(traps),它们会在执行相应的操作时被调用。
2. 代理操作
- 给User对象设置代理,监控该对象 “已有/存在” 属性值的相关操作
//创建user对象
var User = {username: "bobo",age: 20
}
//创建代理对象,Proxy的参数1:被代理对象。参数2:处理器
User = new Proxy(User, {//target:被代理对象。p:属性。receiver:代理后的对象就是Userget(target, p, receiver) {console.log(`获取属性${p}操作`)//返回代理对象的p属性值return Reflect.get(target, p);},set(target, p, value, receiver) {console.log(`设置属性${p}操作`)Reflect.set(target, p, value);}
});console.log(User.username);
console.log(User.age);
User.username = "Jay"
User.age = 18
console.log(User.username);
console.log(User.age);
- 给User对象设置代理,监控该对象 “不存在” 属性值的相关操作
//创建user对象
var User = {username: "bobo",age: 20
}User = new Proxy(User, {get(target, p, receiver) {console.log(`获取属性${p}操作`)return Reflect.get(target, p);},set(target, p, value, receiver) {console.log(`设置属性${p}操作`)Reflect.set(target, p, value);}
});console.log(User.username);
console.log(User.age);
console.log(User.address);
输出结果为:说明该对象中不存在address这个属性值,则需要给该对象进行address属性的补充。同理可以作用在逆向的补环境中,例如:对window对象进行代理时,发现window的xxx属性返回的是空,则需要在逆向js代码中的window对象中补上xxx属性即可。
3. 属性描述符
- getOwnPropertyDescriptor对象属性的获取
有些时候,一些网站的js中是通过属性描述符的方式获取一个对象的属性值。如下所示:
var Stu = {"name":"小明"
};
//通过属性描述符获取Stu对象中的name属性值
console.log(Object.getOwnPropertyDescriptor(Stu, "name"));
那么,这个时候我们该如何通过代理去监控通过属性描述符对属性进行的操作行为呢?(拦截获取属性描述符)
//创建user对象
var User = {username: "bobo",age: 20,address:"Bj"
}User = new Proxy(User, {get(target, p, receiver) {console.log(`获取属性${p}操作`)return Reflect.get(target, p);},set(target, p, value, receiver) {console.log(`设置属性${p}操作`)Reflect.set(target, p, value);},getOwnPropertyDescriptor(target, p){let result;result = Reflect.getOwnPropertyDescriptor(target, p);console.log(`通过属性描述符获取属性${p}操作`)return result;}
});//测试
console.log(User.username);
User.age = 30;
console.log(Object.getOwnPropertyDescriptor(User, "address").value);
- defineProperty对象属性的定义
有些时候,一些网站的js中是通过属性描述符的方式对属性值的定义。如下所示:
var Person = {};// 通过属性描述符给Person对象定义一个name属性
Object.defineProperty(Person, 'name', {value: 'Bobo',writable: true, // 允许修改属性值enumerable: true, // 允许枚举属性configurable: true // 允许删除或修改属性描述符}
);
console.log(Person)
那么,这个时候我们该如何通过代理去监控通过属性描述符对属性进行的定义行为呢?(拦截属性定义)
var Person = {};Person = new Proxy(Person, {get(target, p, receiver) {console.log(`获取属性${p}操作`)return Reflect.get(target, p);},set(target, p, value, receiver) {console.log(`设置属性${p}操作`)Reflect.set(target, p, value);},getOwnPropertyDescriptor(target, p){let result;result = Reflect.getOwnPropertyDescriptor(target, p);console.log(`通过属性描述符获取属性${p}操作`)return result;},defineProperty: function (target, p, descriptor){console.log(`通过属性描述符设置属性${p}操作`)let result;result = Reflect.defineProperty(target, p, descriptor); return result;}});// 通过属性描述符给Person对象定义一个name属性
Object.defineProperty(Person, 'name', {value: 'Bobo',writable: true, // 允许修改属性值enumerable: true, // 允许枚举属性configurable: true // 允许删除或修改属性描述符}
);
console.log(Person)
4. 函数调用拦截监控
拦截监控指定函数的调用
add = function(a,b){console.log("add函数正在被调用");return a+b;
}
add = new Proxy(add, {apply:function (target, thisArg, argList){// target: 函数对象。thisArg: 调用函数的this指针。argList:函数参数数组let result;result = Reflect.apply(target, thisArg, argList);console.log(`${target.name}函数被调用,参数为:${argList}`);return result;}
});
add(1,2);
5. 对象构造方法拦截监控
拦截new关键字。基于new创建的对象就是在调用该对象的构造方法。
function Animal(){}
Animal = new Proxy(Animal, {construct:function (target, argArray, newTarget) {//target: 函数对象。argArray: 参数列表。newTarget: 代理后的对象let result = Reflect.construct(target, argArray, newTarget);console.log(`${target.name}对象被创建`);return result;}
});
animal = new Animal();