如何访问被覆盖的对象的内置方法?

3 javascript overriding object userscripts tampermonkey

一个网页正在将一个内置的 javascript 方法设置为null,我正在尝试找到一种方法来调用用户脚本中的重写方法。

考虑以下代码:

// Overriding the native method to something else
document.querySelectorAll = null;
Run Code Online (Sandbox Code Playgroud)

现在,如果我尝试执行document.querySelectorAll('#an-example'),我会得到异常Uncaught TypeError: null is not a function。原因是该方法已更改为null并且不再可访问。

我正在寻找一种方法来以某种方式恢复对用户脚本中方法的引用。问题是网站可以覆盖对任何东西的引用(甚至包括Document,ElementObject构造函数)。

由于该网站还可以方便地设置参考null,我需要一种方法来找到一种方法来访问querySelectorAll方法的网站将无法覆盖

挑战在于,在我的用户脚本在页面上执行时,任何方法(例如createElementgetElementsByTagName(除了它们的prototypes))都可以被覆盖null

我的问题是,如果它们也被覆盖,我该如何访问DocumentHTMLDocument构造函数方法?


笔记:

由于浏览器限制, Tampermonkey无法在文档的开头运行我的脚本,因此我无法保存对我想要使用的方法的引用,如下所示:

// the following code cannot be run at the beginning of the document
var _originalQuerySelectorAll = document.querySelectorAll;
Run Code Online (Sandbox Code Playgroud)

Bro*_*ams 5

至少有3种方法:

  1. 使用用户脚本沙箱。唉,由于 Tampermonkey 和 Violentmonkey 设计缺陷/错误,这目前仅适用于 Greasemonkey(包括版本 4+)。下面更多。
  2. 使用@run-at document-start. 除了这也不适用于快速页面。
  3. 删除函数覆盖。这通常有效,但容易对目标页面造成更多干扰/来自目标页面。并且可以在页面更改prototype功能时被阻止。


另请参阅停止执行 Javascript 函数(客户端)或调整它


请注意,下面的所有脚本和扩展示例都是完整的工作代码
你可以测试他们对这个JS斌页:通过更改
      *://YOUR_SERVER.COM/YOUR_PATH/*
到:
      https://output.jsbin.com/kobegen*



用户脚本沙箱:

这是首选方法,适用于 Firefox+Greasemonkey(包括 Greasemonkey 4)。

当设置@grant为无以外时,脚本引擎应该在浏览器专门为此目的提供的沙箱中运行脚本。

在适当的沙箱中,目标页面可以随心所欲地覆盖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);
Run Code Online (Sandbox Code Playgroud)

和产量:

document.querySelectorAll: 函数 querySelectorAll() { [本机代码] }

但是,无论是在 Chrome 还是 Firefox 中,Tampermonkey 和 Violentmonkey 都不能正确地进行沙箱处理
目标页面可以篡改 Tampermonkey 脚本看到的本机功能,即使打开了 Tampermonkey 或 Violentmonkey 版本的沙箱。
这不仅仅是一个设计缺陷,它还是一个安全缺陷和潜在漏洞利用的载体。

我们知道 Firefox 和 Chrome 不是罪魁祸首,因为 (1) Greasemonkey-4 正确设置了沙箱,以及 (2) Chrome 扩展程序正确设置了“孤立世界”。也就是说,这个扩展:

清单.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"
}
Run Code Online (Sandbox Code Playgroud)

取消覆盖.js:

console.log ("document.querySelectorAll: ", document.querySelectorAll);
Run Code Online (Sandbox Code Playgroud)

产量:

document.querySelectorAll: 函数 querySelectorAll() { [本机代码] }

正如它应该。



使用@run-at document-start

从理论上讲,运行脚本 atdocument-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);
Run Code Online (Sandbox Code Playgroud)

这有时适用于足够慢的页面和/或网络。

但是,正如 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);
Run Code Online (Sandbox Code Playgroud)

产生:

document.querySelectorAll: 函数 querySelectorAll() { [本机代码] }

缺点是:

  1. 如果页面改变了原型,将无法工作。例如:
    Document.prototype.querySelectorAll = null;
  2. 该页面可以看到太多或改造这样的变化,特别是如果你的脚本大火很快

通过制作私有副本来缓解第 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") );
Run Code Online (Sandbox Code Playgroud)

产生:

goodfunc ("body"): NodeList 1 0: body, length: 1,...

并且goodfunc()即使页面 remolests 也会继续工作(对于您的脚本)document.querySelectorAll

  • 感谢您的广泛撰写,如果可以的话,我会多次投票! (2认同)
  • @Unknown,在正确的沙盒环境中,应该是后者,您的脚本会看到当前文档的 DOM,不受页面对函数所做的任何更改的影响。但是您的页面确实会看到对 HTML 节点所做的更改。请参阅 [Google 对“孤立世界”的介绍](https://developer.chrome.com/extensions/content_scripts#isolated_world) 和 [“Xray 视觉”的 MDN 文档](https://developer.mozilla.org/ en-US/docs/Mozilla/Tech/Xray_vision)。 (2认同)
  • Tampermonkey 中有一种即时注入模式,据说可以保证在任何页面脚本之前运行。它使用不推荐使用的同步 XHR 将脚本代码传递到内容脚本中,同时完全阻止在“document_start”处解析页面。 (2认同)
  • @wOxxOm,我已经测试过即时注入模式;它仍然没有及时开火。请注意,OP 链接的错误报告仍处于打开状态并暗示了很多。由于这些原因,我没有在这个答案中重申它。 (2认同)