如何访问被重写的对象的内置方法?
网页将内置的 javascript 方法设置为
null
,而我正尝试找到一种方法来调用用户脚本中的重写方法。
请考虑以下代码:
// Overriding the native method to something else
document.querySelectorAll = null;
现在,如果我尝试执行
document.querySelectorAll('#an-example')
,我将收到异常
Uncaught TypeError: null is not a function
。原因是该方法已更改为
null
并且不再可访问。
我正在寻找一种方法来以某种方式恢复用户脚本中对该方法的引用。问题是网站可以覆盖对任何内容的引用(甚至包括
Document
、
Element
和
Object
构造函数)。
由于网站也可以轻松地将引用设置为
null
,因此我需要一种方法来访问
网站无法覆盖
的
querySelectorAll
方法。
挑战在于,
任何方法
(例如
createElement
和
getElementsByTagName
(除了它们的
prototype
))都可以在页面上执行我的用户脚本时被覆盖为
null
。
我的问题是,如果
Document
或
HTMLDocument
构造函数方法也已被覆盖,我该如何访问它们?被覆盖了吗?
注意:
由于浏览器限制 Tampermonkey 无法在文档 开头 运行我的脚本,因此我无法保存对我想使用的方法的引用,如下所示:
// the following code cannot be run at the beginning of the document
var _originalQuerySelectorAll = document.querySelectorAll;
至少有 3 种方法:
- 使用 userscript 沙盒 。可惜的是,由于 Tampermonkey 和 Violentmonkey 的设计缺陷/错误,该方法目前仅适用于 Greasemonkey(包括版本 4+)。详情见下文。
-
使用
@run-at document-start
。不过,该方法在快速页面上也不起作用。 -
删除函数覆盖
。该方法通常有效,但容易受到目标页面的更多干扰。如果页面更改了函数的
原型
,则可以阻止它。
另请参阅
停止执行 Javascript 函数(客户端)或对其进行调整
请注意,下面的所有脚本和扩展示例都是
完整的工作代码
。
您可以通过更改以下内容针对
此 JS Bin 页面
对它们进行测试:
*://YOUR_SERVER.COM/YOUR_PATH/*
至:
https://output.jsbin.com/kobegen*
用户脚本沙盒:
这是首选方法,适用于 Firefox+Greasemonkey(包括 Greasemonkey 4)。
当将
@grant
设置为非 none 时,脚本引擎
应该
在浏览器专门为此目的提供的沙盒中运行脚本。
在适当的沙盒中,目标页面可以覆盖
document.querySelectorAll
或其他本机函数,而
无论如何,用户脚本都会看到自己的完全未受影响的实例
。
这 应该 始终有效:
// ==UserScript==
// @name _Unoverride built in functions
// @match *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant GM_addStyle
// @grant GM.getValue
// ==/UserScript==
//- The @grant directives are needed to restore the proper sandbox.
console.log ("document.querySelectorAll: ", document.querySelectorAll);
并产生:
document.querySelectorAll: function querySelectorAll() { [native code] }
但是,
Tampermonkey 和 Violentmonkey 都没有正确沙盒化
,无论是在 Chrome 还是 Firefox 中。
即使打开了 Tampermonkey 或 Violentmonkey 版本的沙盒,目标页面也可以篡改 Tampermonkey 脚本看到的本机函数。
这不仅仅是一个设计缺陷,它还是
安全缺陷
和潜在漏洞的载体。
我们知道 Firefox 和 Chrome 并不是罪魁祸首是因为 (1) Greasemonkey-4 正确设置了沙盒,并且 (2) Chrome 扩展程序正确设置了“Isolated World”。也就是说,这个扩展程序:
ma​​nifest.json:
{
"manifest_version": 2,
"content_scripts": [ {
"js": [ "Unoverride.js" ],
"matches": [ "*://YOUR_SERVER.COM/YOUR_PATH/*" ]
} ],
"description": "Unbuggers native function",
"name": "Native function restore slash use",
"version": "1"
}
Unoverride.js:
console.log ("document.querySelectorAll: ", document.querySelectorAll);
结果:
document.querySelectorAll: function querySelectorAll() { [native code] }
应该如此。
使用
@run-at document-start
:
从理论上讲,在
document-start
处运行脚本应该允许脚本在本机函数被更改之前捕获它。
例如:
// ==UserScript==
// @name _Unoverride built in functions
// @match *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant none
// @run-at document-start
// ==/UserScript==
console.log ("document.querySelectorAll: ", document.querySelectorAll);
这有时在页面和/或网络速度足够慢的情况下有效。
但是,正如 OP 已经指出的那样, Tampermonkey 和 Violentmonkey 实际上都没有在任何其他页面代码之前注入和运行 ,因此此方法在快速页面上会失败。
请注意,在清单中使用
"run_at": "document_start"
设置的 Chrome 扩展程序内容脚本
确实
在正确的时间和/或足够快地运行。
删除函数覆盖:
如果页面(轻微)覆盖了
document.querySelectorAll
之类的函数,则可以使用
delete
清除覆盖,如下所示:
// ==UserScript==
// @name _Unoverride built in functions
// @match *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant none
// ==/UserScript==
delete document.querySelectorAll;
console.log ("document.querySelectorAll: ", document.querySelectorAll);
结果为:
document.querySelectorAll: function querySelectorAll() { [native code] }
缺点是:
-
如果页面更改了原型,则不起作用。例如:
Document.prototype.querySelectorAll = null;
- 页面可以看到或重新进行此类更改,特别是如果您的脚本 触发得太早。
通过制作私人副本来缓解第 2 项:
// ==UserScript==
// @name _Unoverride built in functions
// @match *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant none
// ==/UserScript==
var foobarFunc = document.querySelectorAll;
delete document.querySelectorAll;
var _goodfunc = document.querySelectorAll;
var goodfunc = function (params) {return _goodfunc.call (document, params); };
console.log (`goodfunc ("body"): `, goodfunc("body") );
结果为:
goodfunc ("body"): NodeList 1 0: body, length: 1,...
即使页面干扰了
document.querySelectorAll
,
goodfunc()
仍将继续工作(对于您的脚本)。
Tampermonkey中的其他解决方案是通过iframe恢复原始 - 假设该站点的 csp 允许它,通常这样做。 P.S.如果页面不透彻,则可以通过原型访问原型,而无需iframe技巧:
987278086