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);
}
}
}
}