JavaScript Hook Technical Practical Guide

Front-end analysis, crawler development, and security testing often require "tampering" with web pages during runtime - intercepting function calls, inserting monitoring code, and even modifying execution logic. JavaScript Hook is the core technique to achieve these goals. This article starts from the principle and uses the Tampermonkey script to practice, allowing you to quickly master this technology.


1. What is Hook technology

Hook refers to the technology of replacing or wrapping the original function while the program is running: you can insert your own code before, during, and after the function is executed, while (optionally) retaining all or part of the functionality of the original function.

Its common application scenarios

  1. Analysis of front-end encryption: intercept the plain text of login token and password before encryption
  2. Debugging front-end logic: Automatically add breakpoints to function calls that meet specific conditions
  3. Modify page behavior: block ads and unlock paid content (only for learning and testing)
  4. Monitor API calls: Track fetch / XHR request and response parameters

To do JS Hook in a browser environment, Tampermonkey is the well-deserved first choice:

-Supports Chrome, Edge, Firefox, Safari and other mainstream browsers

  • Zero threshold for script installation and management
  • Built-in rich GM_* API (cross-domain requests, storage, DOM operations, etc.)
  • You can specify the running time and effective domain name of the script

Quick installation

  1. Chrome/Edge users can directly search for "Tampermonkey Beta" in the app store (the Beta version has more complete functions)
  2. Firefox users search for "Tampermonkey" in AMO
  3. Official website backup: https://www.tampermonkey.net/

3. Tampermonkey basic script development

The Grease Monkey script is essentially an ordinary JS code wrapped in a self-executing function, plus a metadata header to tell the browser the basic information about the script.

Complete basic template

// ==UserScript==
// @name         Hook入门测试脚本
// @namespace    https://github.com/yourname/
// @version      1.0.0
// @description  一个演示 console.log Hook 的油猴脚本
// @author       你的名字
// @match        https://*.example.com/*
// @match        https://login1.scrape.center/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict'; // 强制开启严格模式,避免变量污染

    // 这里写你的 Hook 逻辑
    console.log('✅ Hook 入门测试脚本已加载');
})();

High frequency metadata command cheat sheet

CommandFunctionCommon examples
@matchURL pattern for the script to take effect (supports wildcards)@match https://*.jd.com/*
@run-atExecution timing of scriptdocument-start(in front of DOM tree)/document-end(behind DOM tree)
@grantRequested GM_* API permissionsGM_xmlhttpRequest(Cross-domain request)/none(no permission)
@requireIntroduce external JS library@require https://code.jquery.com/jquery-3.7.1.min.js

4. JS Hook practical basis: three common modes

4.1 Simple function Hook (replacement/wrapping)

This is the most basic Hook method: first save the reference to the original function, then replace the original position with a custom function, call the original function in the custom function and insert the code.

/**
 * 通用简单 Hook 函数
 * @param {object} obj - 原始函数所在的对象(全局函数传 window)
 * @param {string} methodName - 要 Hook 的函数名
 */
function simpleHook(obj, methodName) {
    const originalFn = obj[methodName];
    // 替换原函数
    obj[methodName] = function(...args) {
        // ✅ 执行前逻辑
        console.group(`🔍 Hook 捕获到 ${methodName}`);
        console.log('传入参数:', args);
        
        // 调用原始函数(保留原功能)
        const result = originalFn.apply(this, args);
        
        // ✅ 执行后逻辑
        console.log('返回结果:', result);
        console.groupEnd();
        
        return result; // 返回原函数的结果(可选修改)
    };
}

// 测试:Hook 全局的 console.log
simpleHook(window.console, 'log');

4.2 Prototype chain method Hook

Many front-end API (such as XHR, Canvas) methods are hung in the constructorprototypeOn, the Hook prototype can intercept calls to all instances.

/**
 * 通用原型链 Hook 函数
 * @param {function} constructor - 构造函数(比如 XMLHttpRequest)
 * @param {string} methodName - 要 Hook 的原型方法名
 */
function prototypeHook(constructor, methodName) {
    const originalFn = constructor.prototype[methodName];
    constructor.prototype[methodName] = function(...args) {
        console.group(`🎯 原型链 Hook ${constructor.name}.${methodName}`);
        console.log('当前实例:', this);
        console.log('传入参数:', args);
        
        const result = originalFn.apply(this, args);
        
        console.log('返回结果:', result);
        console.groupEnd();
        
        return result;
    };
}

// 测试:Hook 所有 XMLHttpRequest 的 open 方法
prototypeHook(XMLHttpRequest, 'open');

4.3 Conditional Breakpoint Hook

Automatically add debugger breakpoints to function calls that meet specific conditions, so you no longer have to manually find the location and breakpoint!

/**
 * 通用条件断点 Hook 函数
 * @param {object} obj - 原始函数所在的对象
 * @param {string} methodName - 要 Hook 的函数名
 * @param {function} condition - 判断是否触发断点的函数,参数是原函数的参数
 */
function conditionalBreakpointHook(obj, methodName, condition) {
    const originalFn = obj[methodName];
    obj[methodName] = function(...args) {
        // 符合条件就触发断点
        if (condition(...args)) {
            debugger;
            console.warn(`⚠️ 条件断点触发: ${methodName}`);
        }
        return originalFn.apply(this, args);
    };
}

// 测试:当 localStorage.setItem 的 key 是 'token' 或 'password' 时触发断点
conditionalBreakpointHook(
    window.localStorage,
    'setItem',
    (key) => ['token', 'password'].includes(key)
);

5. Practical case: analysis of token generation on scrape login page

We use **https://login1.scrape.center/**(崔庆才老师的爬虫练习站)为例,看看怎么用 Hook to find the login token generation logic.

5.1 Early Observation

  1. Open the website and press F12 to switch to the Network panel
  2. Enter your username and password (for exampleadmin/123456) and click Login
  3. findloginRequest and found that there is only one encrypted one in Form Datatokenfield, no clear text password

5.2 Speculation and Hook

  • The encrypted token looks like Base64 encoding
  • The common API for doing Base64 on the browser side iswindow.btoa()
  • Direct Hookbtoa, and print the call stack to find the function that called it

5.3 Complete script

// ==UserScript==
// @name         Scrape登录Token Hook
// @namespace    https://github.com/yourname/
// @version      1.0.0
// @description  分析 scrape.center 登录 token 的生成
// @author       你的名字
// @match        https://login1.scrape.center/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 保存原始 btoa
    const originalBtoa = window.btoa;
    // 替换 btoa
    window.btoa = function(input) {
        console.group('🔑 btoa Token Hook');
        console.log('加密前的明文:', input);
        console.trace('调用栈(往上翻找到加密逻辑!)');
        debugger; // 自动触发断点,方便调试
        
        const result = originalBtoa(input);
        
        console.log('加密后的 token:', result);
        console.groupEnd();
        
        return result;
    };
})();

5.4 Analysis results

After installing the script, refresh the website and enter the password again to log in:

  1. The console will print the plaintext before encryption:{"username":"admin","password":"123456"}
  2. You can find it in the call stackloginfunction calledbtoa
  3. It turns out that the user name and password are JSON serialized and then directly Base64 encoded.

6. Advanced techniques and anti-Hook confrontation

6.1 Asynchronous function Hook (Promise / async-await)

Today’s front-end APIs (e.g.fetchaxios.get) are mostly asynchronous. When hooking asynchronous functions, you need to pay attention to error capture and return Promise processing.

/**
 * 通用异步函数 Hook
 * @param {object} obj - 原始函数所在的对象
 * @param {string} methodName - 要 Hook 的异步函数名
 */
function asyncHook(obj, methodName) {
    const originalFn = obj[methodName];
    obj[methodName] = async function(...args) {
        const startTime = performance.now();
        console.group(`⚡ 异步 Hook ${methodName}`);
        console.log('传入参数:', args);
        
        try {
            const result = await originalFn.apply(this, args);
            console.log('✅ 执行成功,耗时:', (performance.now() - startTime).toFixed(2), 'ms');
            console.log('返回结果:', result);
            return result;
        } catch (err) {
            console.error('❌ 执行失败:', err);
            throw err; // 抛出错误,不影响原逻辑的错误处理
        } finally {
            console.groupEnd();
        }
    };
}

// 测试:Hook 全局的 fetch API
asyncHook(window, 'fetch');

6.2 Property access Hook (getter/setter)

If you want to monitor whether the properties of an object are assigned or read, you cannot use the function Hook, but useObject.definePropertyOverride getters and setters.

/**
 * 通用属性访问 Hook
 * @param {object} obj - 属性所在的对象
 * @param {string} propName - 要 Hook 的属性名
 */
function propertyHook(obj, propName) {
    let internalValue = obj[propName]; // 用内部变量存储属性值
    Object.defineProperty(obj, propName, {
        get() {
            console.log(`📖 读取属性 ${propName}:`, internalValue);
            return internalValue;
        },
        set(newValue) {
            console.log(`✏️ 修改属性 ${propName}:`, newValue);
            internalValue = newValue;
        },
        configurable: true, // 必须设为 true,否则无法再次修改属性
        enumerable: true // 保持原属性的可枚举性
    });
}

// 测试:Hook window.location.href
propertyHook(window.location, 'href');

6.3 Simple anti-Hook confrontation

Many websites now provide anti-Hook protection. Common methods include:

  1. Check whether the API has been modified: For example, comparebtoa.toString()the result
  2. Freeze Object/Property: UseObject.freeze()Object.seal()or setwritable: false
  3. Code Obfuscation: Makes it difficult for you to locate Hook points

Simple coping strategies

// 1. 从 iframe 里获取“干净”的原始 API(如果网站有 iframe 的话)
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
const cleanBtoa = iframe.contentWindow.btoa;
// 使用 cleanBtoa...

// 2. 绕过 writable: false 的限制(重新 defineProperty)
function bypassWritable(obj, propName, newFn) {
    Object.defineProperty(obj, propName, {
        value: newFn,
        writable: true,
        configurable: true,
        enumerable: true
    });
}

Note: The above technologies are only for learning and research. Please do not interfere with or attack online services without authorization.


7. Best Practices

  1. Precise control scope: use@matchOnly run scripts on required websites
  2. Principle of Least Permission: Only apply for necessary@grantPermissions, for example, if cross-domain is not required, set it tonone
  3. Do a good job in error handling: Add Hook logictry-catch, to avoid script crashes affecting the original website
  4. Performance first: Do not use functions that are called frequently (such asrequestAnimationFrame, CanvasdrawMethod) Do too much complex logic
  5. Code reusability: Encapsulate commonly used Hook methods into universal functions

8. Summary

This article takes you from 0 to 1 to master the core skills of browser-side JS Hook:

  • What is Hook and its common uses -Basic use and script development of Tampermonkey
  • Three commonly used Hook modes (simple function, prototype chain, conditional breakpoint)
  • Practical analysis of scrape login page token generation
  • Asynchronous functions, attribute access Hooks and simple anti-Hook countermeasures

After mastering these skills, you can start analyzing more complex front-end logic. Please be sure to use it only for learning and testing, not for illegal purposes~