hook

在JavaScript中,hook是一种能够拦截和修改函数或方法行为的技术。通过使用hook,开发者可以在现有的函数执行前、执行后或者替换函数的实现逻辑。hook目的是找到函数入口以及一些参数变化,便于分析js逻辑。

作用

  1. 增强代码的可扩展性:Hook技术允许开发者在不修改原始代码的情况下,增加或修改功能,使得代码更加灵活和可扩展。

  2. 减少代码的侵入性:使用hook可以在不改变原始代码的前提下增加新功能,这减少了对原始代码的侵入,使得添加的功能更容易被管理和维护。

  3. 便于调试和问题定位:利用hook技术可以在函数执行前后插入调试信息,帮助开发者更好地理解程序执行流程和定位问题源头。

基本使用

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)

2. 对象属性的 hook

// 1、创建一个对象
let user = {
    "name": "Bob",
};

// 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": "Bob",
};

// 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)

案例 - 浏览器环境下atob函数的hook

atob函数是浏览器环境自带的用来对base64数据进行解编码。接下来,使用对atob函数进行hook。

编写hook代码:

_atob = atob; //保存原函数

atob = function (str) {
    console.log("正在执行atob方法, 参数:", str);
    let result = _atob(str);
    console.log("正在执行atob方法, 返回值:", result);
    return result;
}

hook时机:在浏览器页面加载出来之前进行hook

  1. 在一个空白页面打开浏览器开发者工具

  2. 开启js的事件监听器

  3. 访问百度页面,会有断点停留

  4. 在Sources中的Snippets代码段中新增hook代码片段,打上断点,然后运行

  5. 查看hook运行,监控atob函数的执行

  6. 取消事件监听器中的Script,因为此时已经成功对atob函数进行了hook(不可刷新页面)

编写hook代码:

_cookie = document.cookie;

Object.defineProperty(document, 'cookie', {
    get() {
        console.log("正在获取cookie:", _cookie);
        return _cookie;
    },
    set(value) {
        console.log("正在设置cookie:", value);
        _cookie = value;
    }
});

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] }
*/

Last updated