javascript - 如何访问被重写的对象的内置方法?

标签 javascript object overriding userscripts tampermonkey

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

考虑以下代码:

// Overriding the native method to something else
document.querySelectorAll = null;

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

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

由于网站也可以很容易地将引用设置为null,我需要一种方法来找到访问网站不会的querySelectorAll方法的方法'无法覆盖

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

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


备注:

自从 Tampermonkey due to browser limitations 不能在文档的开头运行我的脚本,我无法保存对我想使用的方法的引用,像这样:

// the following code cannot be run at the beginning of the document
var _originalQuerySelectorAll = document.querySelectorAll;

最佳答案

至少有3种方法:

  1. 使用用户脚本沙箱。 las,由于 Tampermonkey 和 Violentmonkey 设计缺陷/错误,这目前仅适用于 Greasemonkey(包括版本 4+)。更多内容见下文。
  2. 使用@run-at document-start。除了这也不适用于快速页面。
  3. 删除函数覆盖。这通常有效,但容易受到目标页面的干扰/来自目标页面的干扰。如果页面更改函数的 prototype,则可以被阻止。


另见 Stop execution of Javascript function (client side) or tweak it


请注意,下面的所有脚本和扩展示例都是完整的工作代码
你可以根据 this JS Bin page 测试它们通过改变:
*://YOUR_SERVER.COM/YOUR_PATH/*
至:
https://output.jsbin.com/kobegen*



用户脚本沙箱:

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

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

在适当的沙盒中,目标页面可以覆盖 document.querySelectorAll 或其他所有它想要的 native 函数,并且用户脚本将看到它自己的、完全未触及的实例,无论如何。

应该总是有效:

// ==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] }

但是,无论是在 Chrome 还是 Firefox 中,Tampermonkey 和 Violentmonkey 都没有正确地进行沙盒处理
目标页面可以篡改 Tampermonkey 脚本看到的 native 功能,即使启用了 Tampermonkey 或 Violentmonkey 版本的沙箱。
这不仅是一个设计缺陷,还是一个安全缺陷,也是潜在攻击的载体。

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

list .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 处运行脚本应该允许脚本在更改之前捕获 native 函数。
例如:

// ==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 实际上都没有在任何其他页面代码之前注入(inject)和运行,因此此方法在快速页面上失败。

请注意,在 list 中使用 "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] }

缺点是:

  1. 如果页面更改了原型(prototype),将无法工作。例如:
    Document.prototype.querySelectorAll = null;
  2. 该页面可以看到或重新进行此类更改,尤其是如果您的脚本 很快也会触发。

通过制作私有(private)副本缓解第 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"): NodeList10: body, length: 1,...

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

关于javascript - 如何访问被重写的对象的内置方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53977791/

相关文章:

javascript - 在 JavaScript 中如何检测天气类型是 Array 还是 Object?

javascript - 按值对对象属性进行排序

javascript - 将联系表单信息存储到 JavaScript (ES6) 中的类中?

java - 无法理解 JPanel setBackground 方法行为

c# - 基类的相同方法的不同返回类型

python - 如果子类重写了Python中父类的方法,如何引发错误?

javascript - 如何提前退出 Promises 的组合

Javascript E4X : Select a node based on an attribute value

javascript - then(function) 没有得到 angularjs 中返回的 promise 值的更新

javascript - 在对象字面量上扩展原型(prototype)