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

IT技术 javascript greasemonkey
2021-02-06 01:55:11

我想停止从站点执行一行,以便浏览器读取整个页面,除了那一行。或者浏览器可能会简单地跳过该 javascript 函数的执行。

或者

有没有办法我可以以某种方式调整javascript,以便javascript中的随机数生成函数不生成随机数,但我想要的数字...

我无权访问托管脚本的站点,因此所有这些都需要在客户端完成。

5个回答

火狐目前支持beforescriptexecute事件(截至4版,于2011年3月22日发布

与该事件和// @run-at document-start指令,Firefox和Greasemonkey的现在似乎做好拦截特定的<script>标签。

这对于 Chrome+Tampermonkey 仍然是不可能的对于 Firefox+Greasemonkey 以外的任何东西,您都需要使用下面其他答案中所示的技术来编写完整的浏览器扩展。

checkForBadJavascripts函数封装了这一点。例如,假设页面有一个<script>像这样标签:

<script>
    alert ("Sorry, Sucka!  You've got no money left.");
</script>

你可以checkForBadJavascripts像这样使用

checkForBadJavascripts ( [
    [   false, 
        /Sorry, Sucka/, 
        function () {
            addJS_Node ('alert ("Hooray, you\'re a millionaire.");');
        } 
    ]
] );

以获得更好的消息。(^_^)
有关更多信息,请参阅checkForBadJavascripts 中的内联文档


要查看完整脚本中的演示,请首先访问jsBin 上的此页面您将看到 3 行文本,其中两行是 JS 添加的。

现在,安装此脚本查看源代码;它也在下面。)并重新访问该页面。您将看到 GM 脚本删除了一个坏标签,并用我们的“好”JS 替换了另一个。


请注意,只有 Firefox 支持该beforescriptexecute事件。它从 HTML5 规范中删除,没有指定等效的功能。



完整的 GM 脚本示例(与 GitHub 和 jsBin 中的相同):

鉴于此 HTML:

<body onload="init()">
<script type="text/javascript" src="http://jsbin.com/evilExternalJS/js"></script>
<script type="text/javascript" language="javascript">
    function init () {
        var newParagraph            = document.createElement ('p');
        newParagraph.textContent    = "I was added by the old, evil init() function!";
        document.body.appendChild (newParagraph);
    }
</script>
<p>I'm some initial text.</p>
</body>


使用这个 Greasemonkey 脚本:

// ==UserScript==
// @name        _Replace evil Javascript
// @include     http://jsbin.com/ogudon*
// @run-at      document-start
// ==/UserScript==

/****** New "init" function that we will use
    instead of the old, bad "init" function.
*/
function init () {
    var newParagraph            = document.createElement ('p');
    newParagraph.textContent    = "I was added by the new, good init() function!";
    document.body.appendChild (newParagraph);
}

/*--- Check for bad scripts to intercept and specify any actions to take.
*/
checkForBadJavascripts ( [
    [false, /old, evil init()/, function () {addJS_Node (init);} ],
    [true,  /evilExternalJS/i,  null ]
] );

function checkForBadJavascripts (controlArray) {
    /*--- Note that this is a self-initializing function.  The controlArray
        parameter is only active for the FIRST call.  After that, it is an
        event listener.

        The control array row is  defines like so:
        [bSearchSrcAttr, identifyingRegex, callbackFunction]
        Where:
            bSearchSrcAttr      True to search the SRC attribute of a script tag
                                false to search the TEXT content of a script tag.
            identifyingRegex    A valid regular expression that should be unique
                                to that particular script tag.
            callbackFunction    An optional function to execute when the script is
                                found.  Use null if not needed.
    */
    if ( ! controlArray.length) return null;

    checkForBadJavascripts      = function (zEvent) {

        for (var J = controlArray.length - 1;  J >= 0;  --J) {
            var bSearchSrcAttr      = controlArray[J][0];
            var identifyingRegex    = controlArray[J][1];

            if (bSearchSrcAttr) {
                if (identifyingRegex.test (zEvent.target.src) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
            else {
                if (identifyingRegex.test (zEvent.target.textContent) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
        }

        function stopBadJavascript (controlIndex) {
            zEvent.stopPropagation ();
            zEvent.preventDefault ();

            var callbackFunction    = controlArray[J][2];
            if (typeof callbackFunction == "function")
                callbackFunction ();

            //--- Remove the node just to clear clutter from Firebug inspection.
            zEvent.target.parentNode.removeChild (zEvent.target);

            //--- Script is intercepted, remove it from the list.
            controlArray.splice (J, 1);
            if ( ! controlArray.length) {
                //--- All done, remove the listener.
                window.removeEventListener (
                    'beforescriptexecute', checkForBadJavascripts, true
                );
            }
        }
    }

    /*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
        See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
        Note that it does not work on acripts that are dynamically created.
    */
    window.addEventListener ('beforescriptexecute', checkForBadJavascripts, true);

    return checkForBadJavascripts;
}

function addJS_Node (text, s_URL, funcToRun) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    //--- Don't error check here. if DOM not available, should throw error.
    targ.appendChild (scriptNode);
}
不幸的是,由于 FF 是唯一实施的供应商,before/afterscriptexecute 于 2016 年 4 月从 HTML5 标准中删除。
2021-04-06 01:55:11

答案取决于未提供的详细信息(最好是确切的页面和代码行),但一般情况下您可以这样做:

  1. 如果有问题的JS 代码没有立即触发(在 之后触发DOMContentLoaded),那么您可以使用 Greasemonkey 替换有问题的代码。例如:

    var scriptNode          = document.createElement ("script");
    scriptNode.textContent  = "Your JS code here";
    document.head.appendChild (scriptNode);
    

    完毕。

  2. 如果JS 代码立即触发,那么它会变得更加复杂。
    首先,获取脚本的副本并对其进行所需的更改。将其保存在本地。

  3. 有问题的脚本是在文件中还是在主页 HTML(<script src="Some File>vs <script>Mess O' Code</script>)中?

  4. 如果脚本在文件中,请安装Adblock Plus并使用它来阻止加载该脚本。然后使用 Greasemonkey 将修改后的代码添加到页面。例如:

    var scriptNode          = document.createElement ("script");
    scriptNode.setAttribute ("src", "Point to your modified JS file here.");
    document.head.appendChild (scriptNode);
    
  5. 如果脚本位于主 HTML 页面中,则安装NoScript(最佳)或YesScript并使用它来阻止来自该站点的 JavaScript。
    这意味着您随后需要使用 Greasemonkey 来替换该站点中您想要运行的所有脚本。

方法一:使用 MutationObserver

beforescriptexecute不再适用于 Firefox,也不适用于 Chrome。幸运的是,还有一种替代方法 using MutationObserver,它得到了广泛的支持。一般的想法是在页面加载开始时添加一个 MutationObserver,每当有新节点添加到 DOM 时,它都会运行回调。在回调中,检查<script>您要更改或删除标签是否存在如果它存在,您可以篡改它(例如更改它的textContent,或将其src指向其他地方)。新添加的script标签只有回调完成后才会运行,因此这是一种拦截和更改页面Javascript的有效方式。这是一个实时片段示例:

这是一个示例用户脚本,它阻止 jQuery 加载<head>到 Stack Overflow此处:

// ==UserScript==
// @name             Example jQuery removal
// @include          https://stackoverflow.com*
// @run-at           document-start
// @grant            none
// ==/UserScript==

if (document.head) {
  throw new Error('Head already exists - make sure to enable instant script injection');
}
new MutationObserver((_, observer) => {
  const jqueryScriptTag = document.querySelector('script[src*="jquery"]');
  if (jqueryScriptTag) {
    jqueryScriptTag.remove();
    observer.disconnect();
  }
})
  .observe(document.documentElement, { childList: true, subtree: true });

如果你安装这个,你会看到jQuery的加载失败,导致Stack Overflow的JS产生很多错误。

确保尽快附加 MutationObserver - 您需要@run-at document-start在页面加载<head>. (如果使用 Tampermonkey / Chrome,您可能需要启用实验性即时脚本注入以可靠地实现这一点 - 转到 Tampermonkey 设置,配置模式:高级,滚动到底部,将实验性注入模式设置为即时。)

如果您正在为其他人编写用户脚本,任何您正在使用此技术,请确保包含即时脚本注入的说明,因为默认情况下,注入在 Chrome 上不是即时的。

请注意,观察者是使用附加的

.observe(document.documentElement, { childList: true, subtree: true });

这武官观测到的<html>元素,添加和删除的即时儿童手表childList: true,并添加和删除节点手表及其后代中的任何位置subtree: true这种递归侦听器很有用,但它在大型动态页面上的计算成本也很高,因此请确保在达到目的后将其删除。

在一个巨大的页面上,调用querySelector每个突变可能代价高昂,因此您可能想要遍历mutations(观察者回调的第一个参数)和突变的addedNodes替代:

您还可以通过textContent在观察者回调中分配给内联脚本来调整内联脚本以下代码段显示了如何将随机数生成器函数更改为始终返回 10,而不是 1-6:


方法 2:使用 DevTools 本地覆盖

方法 1 是您在为他人编写用户脚本时可以使用的技术但是,如果用户脚本仅供您自己使用,则有一种更简单的方法可以在某些情况下工作。请参阅Chrome Dev Tools - Modify javascript and reload问题的答案:通过转到 Chrome Devtools 中的 Sources -> Overrides 选项卡,并启用本地覆盖,您可以告诉浏览器加载 a 的本地副本,.js而不是下载站点的版本。然后您可以根据需要编辑本地副本,它将运行而不是内置脚本。调整较大的JavaScript文件时,这是非常有用的-这是很多比MutationObserver方法更易于管理。

但是,有一些缺点:

  • 要修改内嵌<script>// code here</script>标记,您必须下载页面的本地副本。(因此,如果页面通过 HTML 响应提供动态内容,则必须重新保存并重新调整.html覆盖以使用新内容,或者必须返回 MutationObserver 方法。)
  • 这是一种开发人员可能能够理解的技术(经过一些试验),但它不是那么用户友好。它可能不是您想要让临时用户脚本消费者完成的那种事情。
@ThePhysicist 您找到了解决方法吗?我也有同样的问题,firefox 无论如何都会执行脚本。在 chrome 中它按预期工作
2021-03-16 01:55:11
方法 1 在 Firefox 中不起作用,$仍被定义为 Firefox 不关心您对添加的(外部)脚本元素做了什么,无论进行任何修改,它都会加载并执行它。不过,该技术适用于内联脚本。
2021-03-22 01:55:11
很抱歉它对你不起作用,但是当我在 FF 中尝试它时它对我来说很好用。见截图:i.stack.imgur.com/liFw6.png
2021-03-23 01:55:11
奇怪的。我正在使用 Firefox 81.0 进行测试(也使用 80.0 进行了测试),但似乎不起作用。我还在自己的 PoC 代码中尝试了 DOM 突变观察器方法,无论我对脚本标签做什么(删除它、更改srctypedefer属性),它仍然会被加载和执行。您确定这有效吗并且没有其他原因会阻止您(在您的屏幕截图上有一个重要通知,上面写着有关其他域中被阻止的 JS 的内容,这似乎不是来自您的代码)?您使用的是哪个版本的 Firefox?
2021-04-05 01:55:11

您可以使用所谓的书签。

构建要在其他站点上运行的 js 文件: your.js

使用以下代码制作一个 HTML 页面:

<html>
<body>
  <a href="javascript:(function(){var s=document.createElement('SCRIPT');s.src='/url/to/your.js?'+(Math.random());document.getElementsByTagName('head')[0].appendChild(s);})()">
    Drag'n Drop this to your bookmarks
  </a>
</body>
</html>

替换/url/to/your.js为您的 js 文件的路径。

在浏览器中加载那个小页面,然后将链接拖放到书签栏中。

转到您要破解的网站,然后单击您刚刚创建的书签。
这将加载your.js到页面中并运行代码。

注意:这?'+(Math.random())部分是为了避免你的 js 被缓存,这不是强制性的,但在你开发时很有帮助your.js

使用 TamperMonkey?您需要// @grant在 TamperMonkey 标头下方添加的所有内容// @require http://urlofyoursite.com/myfile.js. 例如,这是我的 TamperMonkey 东西的最顶部:

// ==UserScript==
// @name         Project
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://docs.google.com/presentation/*
// @grant        none
// @require http://cdnjs.cloudflare.com/ajax/libs/annyang/2.1.0/annyang.min.js
// ==/UserScript==