如何用JavaScript阻止/覆写Chrome扩展函数或捕获其变量?
刚好做过类似的需求,我来给你拆解下怎么实现——不管是阻止Chrome扩展里的函数,还是捕获目标变量,核心要先搞懂Chrome的脚本隔离机制,再针对性地动手。
先搞懂关键前提:Chrome的脚本隔离
Chrome扩展的内容脚本和网页本身的页面脚本是运行在不同上下文的,两者的window对象完全独立。所以如果你直接在内容脚本里想访问网页(或其他扩展注入到网页的)变量/函数,根本拿不到。这也是为什么minerBlock要专门注入脚本到页面上下文里才能捕获Coinhive的挖矿变量。
核心实现方法
1. 注入脚本到页面上下文
要访问网页里的全局变量/函数,必须把你的代码注入到网页的上下文环境中。最常用的方式就是在内容脚本里创建一个<script>标签,把代码写进去再插入到页面DOM里。
比如minerBlock的核心逻辑就是这么做的:它的检查代码不是在自己的内容脚本里跑,而是注入到页面里,这样才能遍历网页的window对象,找到Coinhive的挖矿实例。
2. 识别并拦截目标变量/函数
注入脚本后,你就可以像网页自身的脚本一样操作全局对象了。针对不同场景有两种思路:
场景A:捕获并阻止已存在的实例
就像minerBlock那样,遍历全局对象,找出符合特征的目标实例,然后调用停止方法,甚至直接覆写它的关键函数:
// 注入到页面上下文的代码 for (const name in window) { const targetObj = window[name]; // 匹配Coinhive实例的特征:有isRunning、stop方法,以及_siteKey属性 if (targetObj && typeof targetObj.isRunning === 'function' && typeof targetObj.stop === 'function' && targetObj._siteKey) { console.log('找到目标实例:', name); // 先停止当前运行 targetObj.stop(); // 覆写start方法,阻止它再次启动 const originalStart = targetObj.start; targetObj.start = function() { console.log('已阻止目标函数启动'); // 可以选择不调用原方法,或者做其他处理 // originalStart.apply(this, arguments); }; // 更优雅的方式用Proxy拦截所有属性访问 window[name] = new Proxy(targetObj, { get(obj, prop) { if (prop === 'start') { console.log('拦截start调用'); return () => {}; } return obj[prop]; } }); } }
场景B:提前拦截变量初始化
如果目标脚本还没加载,你可以提前覆盖全局变量,让目标脚本初始化时拿到的是你定义的假对象。比如提前把Coinhive这个全局变量替换掉:
// 注入到页面上下文的代码(要在目标脚本加载前执行) window.Coinhive = { init: function() { console.log('已拦截Coinhive初始化'); // 返回一个假实例,让目标脚本以为初始化成功 return { start: () => {}, stop: () => {}, isRunning: () => false }; } };
确保代码执行时机
要让你的拦截代码生效,必须在目标脚本运行之前执行。在Chrome扩展的manifest.json里配置:
{ "content_scripts": [ { "matches": ["<all_urls>"], // 匹配你要作用的网站 "js": ["content-script.js"], "run_at": "document_start" // 在页面开始加载时就运行内容脚本 } ] }
这样内容脚本会在网页的任何脚本加载前运行,你注入的拦截代码就能提前占位。
关于捕获扩展内部变量的说明
如果目标变量是在其他扩展的内容脚本或背景脚本里,而没有暴露到页面上下文,那你基本没法直接捕获——因为Chrome的扩展隔离机制不允许跨扩展访问内部变量,除非对方扩展特意提供了接口或者存在漏洞。所以这类场景下,你只能针对它注入到页面的变量/函数下手。
内容的提问来源于stack exchange,提问作者Nicola Sivo

