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

Javascript 如何访问被重写对象的内置方法?,javascript,object,overriding,userscripts,tampermonkey,Javascript,Object,Overriding,Userscripts,Tampermonkey,一个网页正在将一个内置javascript方法设置为null,我正试图找到一种方法来调用userscript中被重写的方法 考虑以下代码: // Overriding the native method to something else document.querySelectorAll = null; 现在,如果我尝试执行document.querySelectorAll(“#示例”),我将得到异常未捕获类型错误:null不是函数。原因是该方法已更改为null,不再可访问 我正在寻找一种方

一个网页正在将一个内置javascript方法设置为
null
,我正试图找到一种方法来调用userscript中被重写的方法

考虑以下代码:

// Overriding the native method to something else
document.querySelectorAll = null;
现在,如果我尝试执行
document.querySelectorAll(“#示例”)
,我将得到异常
未捕获类型错误:null不是函数。原因是该方法已更改为
null
,不再可访问

我正在寻找一种方法,以某种方式恢复对userscript中方法的引用。问题是网站可以覆盖对任何内容的引用(甚至包括
文档
元素
对象
构造函数)

由于网站还可以轻松地将引用设置为
null
,因此我需要找到一种方法来访问
querySelectorAll
方法,而网站将无法覆盖该方法

挑战在于任何方法,例如
createElement
getElementsByTagName
(除了它们的
原型
s)都可以在页面上执行my userscript时被重写为
null

我的问题是,如果构造函数方法也被重写,我如何访问
文档
HTMLDocument
构造函数方法


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

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

至少有3种方法:

  • 使用用户脚本沙盒。唉,由于Tampermonkey和Violentmonkey的设计缺陷/bug,目前这只适用于Greasemonkey(包括版本4+)。下面是更多
  • 使用
    @在文档开始时运行
    。但这在快速页面上也不起作用
  • 删除功能覆盖。这通常是可行的,但可能会对目标页面产生更多干扰。如果页面更改函数的
    原型
    ,则可以阻止

  • 另请参见,


    请注意,下面的所有脚本和扩展示例都是完整的工作代码 您可以通过更改:
          <代码>*://YOUR_SERVER.COM/YOUR_PATH/*

    致:
          <代码>https://output.jsbin.com/kobegen*



    Userscript沙盒: 这是首选方法,适用于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:函数querySelectorAll(){[本机代码]}

    然而,无论是在Chrome还是Firefox中,Tampermonkey和Violentmonkey都不能正确地进行沙箱操作。
    目标页面可以篡改Tampermonkey脚本看到的本机函数,甚至可以篡改Tampermonkey或Violentmonkey版本的沙盒。
    这不仅仅是一个设计缺陷,它是一个安全缺陷,是潜在漏洞的载体

    我们知道Firefox和Chrome不是罪魁祸首,因为(1)Greasemonkey-4正确地设置了沙箱,(2)Chrome扩展正确地设置了“孤立世界”。即此扩展:

    manifest.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"
    }
    
    console.log ("document.querySelectorAll: ", document.querySelectorAll);
    
    Unoverride.js:

    {
        "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"
    }
    
    console.log ("document.querySelectorAll: ", document.querySelectorAll);
    
    收益率:

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

    应该如此



    使用
    @在文档开始时运行
    :
    理论上,在
    文档开始时运行脚本应该允许脚本在更改本机函数之前捕获它。
    例如:

    这有时在足够慢的页面和/或网络上起作用

    但是,正如OP已经指出的,无论是Tampermonkey还是Violentmonkey实际上都不会在任何其他页面代码之前注入和运行,因此这种方法在快速页面上失败

    请注意,清单中带有
    “run\u at”:“document\u 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:函数querySelectorAll(){[本机代码]}

    缺点是:

  • 如果页面更改了原型,将无法工作。例如:
    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(“主体”):节点列表0:body,长度:1


    即使页面删除了
    文档,
    goodfunc()
    也将继续工作(对于您的脚本)。querySelectorAll

    Tampermonkey中的其他解决方案是通过iframe恢复原始内容-假设站点的允许,这通常是可行的

    const builtin=new Proxy(document.createElement('iframe'){
    获取(帧,p){
    如果(!frame.parentNode){
    frame.style.cssText='显示:无!重要';
    document.documentElement.appendChild(框架);
    }
    返回frame.contentWindow[p];
    }
    });
    
    用法:

    console.log(内置的.document.queryselectoral.call(文档“*”);