1

我正在编写一个用户脚本来防止网站设置document.body.innerHTML,这是网站检测广告拦截的典型标志:

(function() {
    'use strict';
    console.log("Loading ...");

    Object.defineProperty(document.body, "innerHTML", {
        set: function() {
            console.log("malicious activity detected");
            throw "Don't try to fool my adblock!";
        }
    });
    console.log("Test setting document.body ...");
    try {
        document.body.innerHTML = "";
    } catch (e) {
        console.log(e);
    }
}) ();

上面的用户脚本适用于 Chrome+Tampermonkey。但它在 Firefox+Greasemonkey-4 上的行为很奇怪。

控制台中的输出是:

Loading ...
测试设置 document.body ...
检测到恶意活动
不要试图欺骗我的广告拦截!

所以用户脚本加载成功,设置器也成功挂钩。但是在加载后我在控制台中尝试:

document.body.innerHTML = ""

它只是设置innerHTML不抛出错误,就好像尚未安装挂钩一样。我已经尝试了所有@run-at选项,但它们都不起作用。


OTAH,如果我Object.defineProperty()在控制台中使用,那么它可以按预期工作。因此我得出结论,Firefox 不尊重Object.defineProperty()用户脚本。

您也可以尝试访问此网站:https ://connectwww.com 。在 Chrome 中的 Tampermonkey 中安装上述用户脚本后,网站上的 adblock-detection 被成功拦截。但是用户脚本在 Firefox+Greasemonkey 上不起作用。

为什么 Firefox尊重Object.defineProperty()用户脚本?有什么解决方法吗?

旁注:

一些著名的用户脚本如 anti-adblock-killer 也适用于上述测试网站的 Chrome 但不适用于 Firefox,我想这是由于相同的原因。

4

1 回答 1

1

@grant none即使在模式下 , Greasemonkey 4也会对您的脚本进行沙箱处理。(这是Greasemonkey 4唯一比 Tampermonkey 或 Violentmonkey 做得更好的事情。)

所以你的脚本正在设置脚本的范围/副本innerHTML
在与目标页面范围共享的 Tampermonkey 中,但在 Greasemonkey 中,这两个范围更合适地分开。所以页面范围(和默认控制台)看不到变化。

在这种情况下,我认为这些unsafeWindow方法行不通。您必须注入覆盖代码

这是一个适用于 Greasemonkey 4+ 和 Tampermonkey 的脚本(也应该适用于 Violentmonkey,但我没有测试过)。它适用于 Chrome 和 Firefox:

// ==UserScript==
// @name     _Overriding Target page functions can be tricky with GM 4
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant    none
// @run-at   document-start
// ==/UserScript==
/* eslint-disable no-multi-spaces */

console.log("Loading ...");

function overrideIt () {
    //-- Necessary check because of scope madness in TM, VM, etc.
    if (document.body.innerHTML) {
        Object.defineProperty (document.body, "innerHTML", {
            set: function () {
                var scopeStr = (typeof GM === "object"  &&  GM.info) ? "script" : "page";
                console.log (`Malicious activity detected - ${scopeStr} scope`);
                throw "Don't try to fool my adblock!";
            }
        } );
    }
}
overrideIt ();

if (typeof unsafeWindow === "object") {
    console.log ("unsafeWindow detected.");
    addJS_Node (null, null, overrideIt);
}

console.log ("Test setting document.body ...");
try {
    document.body.innerHTML = "";
} catch (e) {
    console.log ("Caught: ", e);
}

//-- addJS_Node is a standard(ish) function
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    if (runOnLoad) {
        scriptNode.addEventListener ("load", runOnLoad, false);
    }
    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;
    targ.appendChild (scriptNode);
}
于 2019-02-02T06:00:08.607 回答