HOOK 构造器

1、asyncFunction   通过闭包钩子拦截和替换 JavaScript 中的 AsyncFunction 构造器

// --- 拦截 Async Function ---

// 1. 获取异步函数的原型构造器(即 `AsyncFunction` 构造器)
// `async function() {}` 是一个异步函数表达式,通过获取其原型的构造器属性,
// 可以获得系统内置的 `AsyncFunction` 构造器。
var oldAsyncFunctionConstructor = Object.getPrototypeOf(async function() {}).constructor;

// 2. 使用 `Closure` 函数包装原始的 `AsyncFunction` 构造器。
// 这样创建的新构造器在执行时,会首先替换掉代码中的 `debugger` 字符串。
var newAsyncFunctionConstructor = Closure(oldAsyncFunctionConstructor);

// 3. 将原始的 `toString` 方法绑定到新的构造器。
// 这样可以确保调用 `newAsyncFunctionConstructor.toString()` 时,
// 返回的是原始构造器的内容,而不是包装后的版本。
// 这个步骤是为了保证新构造器在输出时表现得与原始构造器一致。
newAsyncFunctionConstructor.toString = oldAsyncFunctionConstructor.toString.bind(oldAsyncFunctionConstructor);

// 4. 将拦截后的新构造器赋值为异步函数的构造器。
// 使用 `Object.defineProperty` 可以直接设置 `AsyncFunction` 构造器,
// 将新构造器绑定到原来的 `AsyncFunction` 原型上。
// 设置 `writable: false` 禁止重写,但 `configurable: true` 保留重新配置的可能性。
Object.defineProperty(oldAsyncFunctionConstructor.prototype, "constructor", {
    value: newAsyncFunctionConstructor, // 设置新的构造器为新包装后的构造器
    writable: false,                    // 设置 `writable: false`,不允许外部修改构造器
    configurable: true                  // 允许重新配置
});

代码解释

  • 获取 AsyncFunction 构造器AsyncFunction 是异步函数的构造器,但它在 JavaScript 中并没有暴露为直接调用的全局对象。因此,代码通过 async function() {} 获取其构造器。
  • 使用 Closure 函数包装:使用 Closure 函数来包装原始构造器,使得每次使用 AsyncFunction 创建异步函数时,debugger 关键字会被自动移除。
  • 绑定 toString 方法:绑定原始 toString 方法,以确保输出的代码内容一致,避免暴露包装后的构造器。
  • 更新构造器定义:将 newAsyncFunctionConstructor 设置为异步函数的构造器,保证所有异步函数的创建行为都使用新的构造器。

 

2、这段代码分为两个部分:一部分是拦截 setAttribute 方法以移除 debugger,另一部分是拦截 iframe 元素的 contentWindow 属性,以便在 iframe 中注入代码。以下是详细的注释和解释:

  第一部分:拦截 setAttribute 方法

  

// --- 拦截 setAttribute 方法 ---

// 1. 备份原始的 `setAttribute` 方法
// `setAttribute` 是 DOM 元素中用于设置属性的方法。
var oldSetAttribute = window.Element.prototype.setAttribute;

// 2. 重写 `setAttribute` 方法
// 这里定义一个新函数覆盖 `setAttribute`,以便拦截属性设置的值。
window.Element.prototype.setAttribute = function(name, value) {
    // 3. 检查属性值是否为字符串,并移除其中的 `debugger` 关键字
    // 如果 `value` 是字符串,则使用正则表达式去除 `debugger` 关键字。
    if (typeof value == "string")
        value = value.replace(/debugger/g, "");

    // 4. 调用原始的 `setAttribute` 方法,传递修改后的值
    // 通过 `.call(this, name, value)` 保证 `setAttribute` 方法的上下文和参数一致。
    oldSetAttribute.call(this, name, value);
};

 

  

 

  

代码解释

  • 备份原始方法:代码将 setAttribute 方法的原始版本保存为 oldSetAttribute
  • 拦截属性设置:当用户调用 setAttribute 设置 DOM 元素属性时,拦截输入值。
  • 移除 debugger:如果属性值是字符串类型,删除其中的 debugger 关键字。
  • 调用原始方法:调用原始 setAttribute 方法,将修改后的值设置到元素属性中。

第二部分:拦截 iframecontentWindow 属性

// --- 拦截 iframe 的 contentWindow 属性 ---

// 1. 获取 iframe 元素的 `contentWindow` 属性描述符中的 `get` 方法
// 这里通过 `Object.getOwnPropertyDescriptor` 方法获取 `contentWindow` 属性的 getter 方法。
var oldContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "contentWindow").get;

// 2. 重写 `contentWindow` 的 getter
// 使用 `Object.defineProperty` 重新定义 `contentWindow` 属性,添加自定义逻辑。
Object.defineProperty(window.HTMLIFrameElement.prototype, "contentWindow", {
    get() {
        // 3. 调用原始的 `contentWindow` getter 方法,获取 iframe 的 `contentWindow` 对象
        var newV = oldContentWindow.call(this);

        // 4. 检查 `contentWindow` 对象上是否存在 `inject` 属性
        // 如果 `inject` 属性不存在,则添加它并调用 `core` 函数进行代码注入。
        if (!newV.inject) {
            newV.inject = true; // 设置 `inject` 属性为 `true`
            core.call(newV, globalConfig, newV); // 执行 `core` 函数,将配置注入到 iframe 的 `contentWindow`
        }

        // 5. 返回 `contentWindow` 对象
        return newV;
    }
});

代码解释

  • 获取 contentWindow 的原始 getter:通过 Object.getOwnPropertyDescriptor 获取 iframe 的 contentWindow 属性的原始 getter 方法,以便后续调用。
  • 拦截 contentWindow 的访问:重新定义 contentWindow 的 getter 方法,添加代码注入逻辑。
  • 注入代码:每次访问 iframe 的 contentWindow 时,检查是否已经设置 inject 属性。如果未设置,则调用 core 函数将 globalConfignewV 注入到 iframe 的上下文中,以实现代码注入。
  • 返回 contentWindow:返回 iframe 的 contentWindow 对象,确保代码逻辑不会影响对 contentWindow 的正常访问。

总结

这段代码实现了两个主要功能:

  1. 拦截 setAttribute 方法:自动删除所有通过 setAttribute 设置的字符串属性值中的 debugger 关键字。
  2. 拦截 iframe 的 contentWindow 属性:每次访问 iframe 的 contentWindow 时,在其上下文中注入指定的代码。这种方式常用于防止 iframe 内部的 JavaScript 执行带有 debugger 的代码,并在加载 iframe 时注入特定的功能或配置。

 

 

3、通过重写constructor 来过debugger 

// --- Function.prototype.constructor 构造器反调试 ---

// 1. 保存原始的构造器函数,备份到 `_constructor` 变量中
var _constructor = Function.prototype.constructor;

// 2. 重写 `Function.prototype.constructor`,用于拦截 `Function` 构造的代码
Function.prototype.constructor = function(s) {
    // 3. 检查传入的字符串 `s` 是否为 "debugger"。
    // 如果 `s` 等于 "debugger",则输出到控制台,并返回 `null`,阻止执行。
    if (s == "debugger") {
        console.log(s);
        return null; // 返回 `null`,防止构造 `debugger` 语句
    }

    // 4. 否则,调用原始构造器 `_constructor` 来处理其他代码
    return _constructor(s);
};

4、通过重写eval来过debugger反调试

// --- eval 构造器的反调试 ---

(function() {
    'use strict';
    
    // 1. 备份原始的 `eval` 函数,保存到 `eval_` 变量中
    var eval_ = window.eval;

    // 2. 重写 `window.eval` 函数
    window.eval = function(x) {
        // 3. 调用原始的 `eval`,在执行之前,将代码中的 "debugger;" 替换为空白字符
        eval_(x.replace("debugger;", "  ; "));
    };

    // 4. 将新的 `eval` 的 `toString` 方法指向原始的 `eval.toString`,
    // 以便在外部查看时,新 `eval` 的输出保持一致。
    window.eval.toString = eval_.toString;
})();

 

posted @ 2024-11-04 14:29  *感悟人生*  阅读(10)  评论(0编辑  收藏  举报