Hooking 浏览器 API (XMLHttpRequest, fetch, localStorage, eval 等):如何通过 JavaScript 注入实现运行时行为监听和篡改?

JavaScript API Hooking:浏览器里的“窃听风云”

各位观众老爷们,大家好! 今天咱们聊点刺激的——JavaScript API Hooking,也就是浏览器里的“窃听风云”。 放心,不是教大家搞破坏,而是让你更懂浏览器,更好地保护自己(当然,如果你想搞点小恶作剧,后果自负哈!)。

啥是API Hooking?简单来说,就是截胡!

想象一下,浏览器里的各种API就像一个个小邮递员,负责传递信息。比如 XMLHttpRequest 负责发请求,localStorage 负责存数据,eval 负责执行代码。API Hooking 就是在你家门口(API调用前/后)埋伏一个“窃听器”,监听甚至篡改这些邮递员传递的信息。

为啥要Hooking?

  • 调试神器: 追踪API调用,了解代码行为,尤其是在调试第三方库的时候。
  • 安全审计: 监控敏感数据泄露,比如用户密码、银行卡号啥的。
  • 功能增强: 修改API的行为,添加自定义逻辑,实现一些酷炫的功能。
  • 恶意行为分析: 识别恶意脚本,比如窃取用户信息、植入恶意代码。

准备工作:JavaScript注入大法

想要Hooking,首先得把我们的“窃听器”放进去。 这就得用到JavaScript注入。 简单说,就是把我们的JS代码塞到目标网页里去。 方法有很多,比如:

  • 浏览器书签: 创建一个包含JS代码的书签,点击书签即可注入。
  • 浏览器扩展: 开发一个浏览器扩展,在指定网页上自动注入JS代码。
  • 开发者工具: 在浏览器的开发者工具里,直接执行JS代码。
  • 代理服务器: 通过代理服务器修改HTTP响应,注入JS代码。

这里我们用最简单粗暴的开发者工具来演示。 打开你想“窃听”的网页,按下F12,进入Console选项卡,然后就可以开始写代码了。

主角登场:Hooking实战

接下来,我们开始Hooking几个常用的API,看看怎么监听和篡改它们。

1. XMLHttpRequest Hooking:拦截网络请求

XMLHttpRequest (简称XHR) 是浏览器发送HTTP请求的主要方式。 Hooking它,可以监听请求的URL、参数、Header,甚至修改响应内容。

代码示例:

(function() {
  var originalXHR = window.XMLHttpRequest;

  function MyXHR() {
    var xhr = new originalXHR();

    // 保存原始的 open 和 send 方法
    var originalOpen = xhr.open;
    var originalSend = xhr.send;

    // 重写 open 方法,记录请求信息
    xhr.open = function(method, url) {
      this._myUrl = url; // 记录 URL
      this._myMethod = method; // 记录 method
      console.log('XHR open:', method, url);
      originalOpen.apply(xhr, arguments);
    };

    // 重写 send 方法,记录请求数据
    xhr.send = function(data) {
      this._myData = data; // 记录 data
      console.log('XHR send:', data);
      originalSend.apply(xhr, arguments);
    };

    // 监听 onreadystatechange 事件,记录响应信息
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        console.log('XHR response:', xhr.status, xhr.responseText);
      }
    };

    return xhr;
  }

  window.XMLHttpRequest = MyXHR;

  console.log('XMLHttpRequest Hooked!');
})();

代码解释:

  1. 保存原始对象: 先把原始的 window.XMLHttpRequest 保存起来,免得被我们自己覆盖了。
  2. 创建自定义对象: MyXHR 函数是我们自定义的XHR构造函数。
  3. 重写 opensend 方法: 在自定义的 opensend 方法里,记录请求的URL、Method和数据,然后调用原始的 opensend 方法,保证请求正常发送。
  4. 监听 onreadystatechange 事件: 监听 onreadystatechange 事件,在请求完成时,记录响应状态码和响应内容。
  5. 替换原始对象:window.XMLHttpRequest 替换成我们自定义的 MyXHR,这样所有新的XHR请求都会经过我们的“窃听器”。

使用方法:

把这段代码复制到浏览器的开发者工具的Console里,然后执行。 之后,网页上所有的XHR请求都会被记录到Console里。

进阶:篡改请求和响应

除了监听,我们还可以篡改请求和响应。 比如,修改请求Header:

xhr.setRequestHeader = function(header, value) {
  console.log('XHR setRequestHeader:', header, value);
  originalSetRequestHeader.apply(xhr, arguments);
};

或者,修改响应内容:

Object.defineProperty(xhr, 'responseText', {
  get: function() {
    var originalResponseText = Object.getOwnPropertyDescriptor(originalXHR.prototype, 'responseText').get.apply(this);
    console.log('XHR responseText:', originalResponseText);
    return originalResponseText + ' // Modified by Hooking!'; // 添加后缀
  }
});

注意事项:

  • Hooking XHR 可能会影响网页的正常功能,所以要谨慎使用。
  • 一些网站会检测XHR Hooking,并采取反制措施。

2. Fetch API Hooking:新一代网络请求

fetch 是比 XMLHttpRequest 更现代的API,也更方便Hooking。

代码示例:

(function() {
  var originalFetch = window.fetch;

  window.fetch = async function(url, options) {
    console.log('Fetch URL:', url);
    console.log('Fetch Options:', options);

    // 修改请求参数
    let modifiedOptions = options || {};
    modifiedOptions.headers = modifiedOptions.headers || {};
    modifiedOptions.headers['X-Hooked-By'] = 'MyHookingScript';

    const response = await originalFetch(url, modifiedOptions);

    // 修改响应内容
    const originalText = await response.clone().text();
    console.log('Fetch Response:', originalText);

    const modifiedResponse = new Response(originalText + ' // Modified by Hooking!', {
      status: response.status,
      statusText: response.statusText,
      headers: response.headers
    });

    return modifiedResponse;
  };

  console.log('Fetch API Hooked!');
})();

代码解释:

  1. 保存原始对象: 保存原始的 window.fetch
  2. 重写 fetch 方法: 在自定义的 fetch 方法里,记录请求的URL和参数,然后修改请求Header,最后调用原始的 fetch 方法。
  3. 修改响应内容: 获取原始响应内容,添加后缀,然后创建一个新的 Response 对象,返回给调用者。

使用方法:

把这段代码复制到浏览器的开发者工具的Console里,然后执行。 之后,网页上所有的 fetch 请求都会被记录到Console里,并且响应内容会被修改。

注意事项:

  • fetch 返回的是 Promise,所以需要使用 async/await 来处理。
  • 修改响应内容需要创建一个新的 Response 对象。

3. localStorage Hooking:监听本地存储

localStorage 是浏览器用来存储数据的API。 Hooking它,可以监听数据的读取和写入。

代码示例:

(function() {
  var originalSetItem = localStorage.setItem;
  var originalGetItem = localStorage.getItem;
  var originalRemoveItem = localStorage.removeItem;
  var originalClear = localStorage.clear;

  localStorage.setItem = function(key, value) {
    console.log('localStorage setItem:', key, value);
    originalSetItem.apply(localStorage, arguments);
  };

  localStorage.getItem = function(key) {
    var value = originalGetItem.apply(localStorage, arguments);
    console.log('localStorage getItem:', key, value);
    return value;
  };

  localStorage.removeItem = function(key) {
    console.log('localStorage removeItem:', key);
    originalRemoveItem.apply(localStorage, arguments);
  };

  localStorage.clear = function() {
    console.log('localStorage clear');
    originalClear.apply(localStorage, arguments);
  };

  console.log('localStorage Hooked!');
})();

代码解释:

  1. 保存原始方法: 保存原始的 setItemgetItemremoveItemclear 方法。
  2. 重写方法: 在自定义的方法里,记录操作的key和value,然后调用原始的方法。

使用方法:

把这段代码复制到浏览器的开发者工具的Console里,然后执行。 之后,网页上所有的 localStorage 操作都会被记录到Console里。

进阶:数据加密

除了监听,我们还可以对存储的数据进行加密,防止敏感信息泄露。

localStorage.setItem = function(key, value) {
  var encryptedValue = btoa(value); // Base64编码
  console.log('localStorage setItem (encrypted):', key, encryptedValue);
  originalSetItem.call(localStorage, key, encryptedValue);
};

localStorage.getItem = function(key) {
  var encryptedValue = originalGetItem.call(localStorage, key);
  var value = encryptedValue ? atob(encryptedValue) : null; // Base64解码
  console.log('localStorage getItem (decrypted):', key, value);
  return value;
};

注意事项:

  • 加密算法要足够安全,防止被破解。
  • 不要把加密密钥存储在客户端,否则就失去了意义。

4. eval Hooking:拦截代码执行

eval 函数可以执行字符串形式的JavaScript代码。 Hooking它,可以监听甚至阻止代码的执行。

代码示例:

(function() {
  var originalEval = window.eval;

  window.eval = function(code) {
    console.log('eval code:', code);

    // 阻止执行恶意代码
    if (code.indexOf('dangerousFunction()') !== -1) {
      console.warn('eval blocked dangerous code!');
      return;
    }

    var result = originalEval.apply(window, arguments);
    return result;
  };

  console.log('eval Hooked!');
})();

代码解释:

  1. 保存原始对象: 保存原始的 window.eval
  2. 重写 eval 方法: 在自定义的 eval 方法里,记录要执行的代码,然后判断代码是否包含恶意代码,如果是,则阻止执行。

使用方法:

把这段代码复制到浏览器的开发者工具的Console里,然后执行。 之后,网页上所有的 eval 调用都会被记录到Console里,并且可以阻止执行恶意代码。

注意事项:

  • eval 函数本身就存在安全风险,尽量避免使用。
  • Hooking eval 可能会影响网页的正常功能,所以要谨慎使用。

Hooking 其他 API

除了上面这些,还有很多API可以Hooking,比如:

  • document.cookie:监听Cookie的读取和写入。
  • window.location:监听URL的跳转。
  • console.log:监听控制台输出。
  • addEventListener:监听事件的触发。

防御Hooking:网站的反制措施

聪明的网站开发者当然不会坐视不管,他们会采取一些反制措施,防止API被Hooking。 常见的反制措施有:

  • 检测API是否被重写: 检查 window.XMLHttpRequest 是否等于原始的 XMLHttpRequest
  • 使用 Object.freeze 冻结API: 冻结API后,无法修改。
  • 使用 Proxy 代理API: 使用 Proxy 代理API,可以拦截Hooking行为。
  • 代码混淆: 增加Hooking的难度。

总结:Hooking的艺术

API Hooking 是一门艺术,也是一种强大的技术。 它可以帮助我们更好地理解浏览器,更好地保护自己,甚至可以用来创造一些有趣的东西。 但是,也要记住,Hooking 是一把双刃剑,要谨慎使用,避免造成不必要的麻烦。

表格总结:

API 功能 Hooking用途 注意事项
XMLHttpRequest 发送HTTP请求 监听请求URL、参数、Header,修改响应内容 可能会影响网页的正常功能,一些网站会检测XHR Hooking
fetch 发送HTTP请求 监听请求URL、参数、Header,修改响应内容 fetch 返回的是 Promise,修改响应内容需要创建一个新的 Response 对象
localStorage 存储数据 监听数据的读取和写入,对数据进行加密 加密算法要足够安全,不要把加密密钥存储在客户端
eval 执行字符串形式的JavaScript代码 监听代码的执行,阻止执行恶意代码 eval 函数本身就存在安全风险,尽量避免使用,Hooking eval 可能会影响网页的正常功能
document.cookie 读取和写入Cookie 监听Cookie的读取和写入 注意Cookie的安全属性(HttpOnly、Secure)
window.location URL跳转 监听URL的跳转,阻止跳转 防止恶意跳转
console.log 控制台输出 监听控制台输出,记录调试信息 可以用来追踪代码执行流程
addEventListener 监听事件的触发 监听事件的触发,修改事件处理函数 可以用来修改网页的行为

好了,今天的“窃听风云”就讲到这里,希望大家有所收获! 记住,技术无罪,关键在于如何使用。 祝大家玩得开心! (别忘了点赞、收藏、转发哦! 😉)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注