You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需调试器/源码修改,如何通过书签脚本修改BoundFunctionObject闭包变量

访问并修改JavaScript闭包变量的迂回方案

针对你提出的场景(无法手动调试、无源码修改权限、不能仅做视觉修改),以下是几个可行的迂回实现方案:


1. 浏览器扩展前置注入脚本

利用浏览器扩展的提前注入能力(设置run_at: document_start),在页面原生脚本执行前拦截并修改代码逻辑,将闭包变量暴露到全局:

实现思路

  1. 编写Chrome/Firefox扩展,配置content script在文档开始时注入;
  2. 重写Function构造函数,捕获目标IIFE的创建过程;
  3. 修改IIFE的函数体,将internal_variable挂载到全局对象(如window)。

示例代码(扩展content script)

// 保存原生Function构造函数
const originalFunction = window.Function;

// 重写Function,拦截目标脚本的函数创建
window.Function = function(...args) {
  const functionBody = args[0];
  // 匹配目标IIFE的特征代码(可根据实际场景调整)
  if (functionBody.includes('let internal_variable = 0;') && functionBody.includes('_internal_buttonClicked')) {
    // 修改函数体,把内部变量暴露到全局
    const modifiedBody = functionBody.replace(
      'let internal_variable = 0;',
      'let internal_variable = 0; window.__exposedInternalVar = internal_variable;'
    );
    return originalFunction.apply(this, [modifiedBody, ...args.slice(1)]);
  }
  return originalFunction.apply(this, args);
};

后续你的书签脚本就可以直接执行window.__exposedInternalVar = -100,实现对闭包变量的修改。


2. 利用Chrome DevTools Protocol(CDP)自动调试

通过CDP实现自动化调试流程,无需手动操作调试器,自动设置断点、修改闭包变量:

实现思路

  1. 使用Puppeteer/Playwright等工具连接浏览器;
  2. 自动在_internal_buttonClicked函数执行处设置断点;
  3. 断点命中时,通过CDP接口获取闭包作用域,修改internal_variable的值;
  4. 自动恢复脚本执行,完成修改。

示例代码(Puppeteer)

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto('你的目标页面URL');

  // 启用CDP调试会话
  const cdpSession = await page.target().createCDPSession();
  await cdpSession.send('Debugger.enable');

  // 监听断点触发事件
  cdpSession.on('Debugger.paused', async (event) => {
    const callFrame = event.callFrames[0];
    // 找到闭包作用域
    const closureScope = callFrame.scopeChain.find(scope => scope.type === 'closure');
    if (closureScope) {
      // 修改internal_variable的值为-100
      await cdpSession.send('Debugger.setVariableValue', {
        scopeNumber: closureScope.scopeNumber,
        variableName: 'internal_variable',
        newValue: { value: -100 }
      });
      // 恢复脚本执行
      await cdpSession.send('Debugger.resume');
    }
  });

  // 设置断点:匹配_internal_buttonClicked函数的代码位置
  await cdpSession.send('Debugger.setBreakpointByUrl', {
    url: page.url(),
    lineNumber: 5, // 对应目标函数的行号(需根据实际页面调整)
    columnNumber: 10
  });

  // 触发按钮点击,让函数执行到断点
  await page.click('#myButton');

  // 验证修改结果
  const buttonText = await page.$eval('#myButton', el => el.innerText);
  console.log('修改后按钮文本:', buttonText); // 应为"Clicked -99 times!"

  await browser.close();
})();

这个方案完全自动化,不涉及手动调试,满足你的要求。


3. 劫持Function.prototype.bind捕获闭包(限未压缩代码)

如果目标页面代码未被压缩混淆,可通过重写bind方法,结合函数toString()提取闭包逻辑:

实现思路

  1. 页面脚本执行前注入代码,重写Function.prototype.bind;
  2. 当目标函数被绑定时,通过toString()获取其源码;
  3. 构造一个可访问闭包的辅助函数,将内部变量暴露出来。

示例代码(前置注入)

const originalBind = Function.prototype.bind;
Function.prototype.bind = function(...args) {
  const boundFn = originalBind.apply(this, args);
  // 匹配目标函数的特征
  if (this.toString().includes('_internal_buttonClicked')) {
    // 构造可提取闭包的函数
    const extractClosure = new Function(`
      ${this.toString()}
      return () => ({ internal_variable });
    `)();
    // 将闭包变量挂载到绑定后的函数上
    boundFn.__closure = extractClosure();
  }
  return boundFn;
};

之后书签脚本可通过buttonClicked.__closure.internal_variable = -100修改值,但此方法仅适用于未压缩的代码,webpack打包后的压缩代码会失效。


方案局限性说明

  • 浏览器扩展方案需要用户安装扩展,拥有浏览器权限;
  • CDP自动化方案依赖Node.js环境和自动化工具,适合批量或场景化修改;
  • 劫持bind的方案仅对未压缩代码有效,通用性较差。

内容的提问来源于stack exchange,提问作者Kaia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:17:03