BlockAdBlock广告反阻止反向工程

如果使用广告拦截器,则可能会遇到BlockAdBlock该脚本检测到您的阻止程序,并且在您将其禁用之前,不允许它进入该站点。但是我想知道它是如何工作的。反阻止程序如何检测阻止程序?阻滞剂对此有何反应?它们如何阻滞抗阻滞剂?

逆向工程的历史


我做的第一件事是看他们的网站BlockAdBlock为配置器提供了以下设置:等待间隔以及警告的外观,生成不同版本的脚本。

这让我想到了版本。如果他看不到一个版本,在一次?所以我做了。我使用Wayback Machine时光倒流之后,我下载了所有版本的BlockAdBlock并对其进行了哈希处理。

具有sha1sum的所有版本的BlockAdBlock的列表


6d5eafab2ca816ccd049ad8f796358c0a7a43cf3 20151007203811.js
065b4aa813b219abbce76ad20a3216b3481b11bb 20151113115955.js
d5dec97a775b2e563f3e4359e4f8f1c3645ba0e5 20160121132336.js
8add06cbb79bc25114bd7a2083067ceea9fbb354 20160318193101.js
8add06cbb79bc25114bd7a2083067ceea9fbb354 20160319042810.js
8add06cbb79bc25114bd7a2083067ceea9fbb354 20160331051645.js
8add06cbb79bc25114bd7a2083067ceea9fbb354 20160406061855.js
8add06cbb79bc25114bd7a2083067ceea9fbb354 20160408025028.js
555637904dc9e4bfc6f08bdcae92f0ba0f443ebf 20160415083215.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20161120215354.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170525201720.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170606090847.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170703211338.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170707211652.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170813090718.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20170915094808.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171005180631.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171019162109.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171109101135.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171127113945.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171211042454.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20171227031408.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20180202000800.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20180412213253.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20180419060636.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20180530223228.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20180815042610.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20181029233809.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20181122190948.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20181122205748.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20190324081812.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20190420155244.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20190424200651.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20190903121933.js
d8986247cad3bbc2dd92c3a2a06ac1540da6b286 20200112084838.js

尽管有一些站点仍使用BlockAdBlock,但 只有六个版本彼此不同,最后一个是2016年。这很棒,因为对我们来说,反向工程唯一的版本,然后反向差异就足够了。剧透:我们将看到思想的发展,甚至是调试代码的残余。

所有版本发布在GitHub上如果要查看差异,那么在此存储库中,每个提交都是一个新版本。

开箱


脚本代码不是最小的,而是由Dean Edwards JS包装器进行包装,并进行了外观更改,并且未对逻辑进行任何修改1

eval(function(p, a, c, k, e, d) {
    e = function(c) {
        return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
    };
    if (!''.replace(/^/, String)) {
        while (c--) {
            d[e(c)] = k[c] || e(c)
        }
        k = [function(e) {
            return d[e]
        }];
        e = function() {
            return '\\w+'
        };
        c = 1
    };
    while (c--) {
        if (k[c]) {
            p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c])
        }
    }
    return p
}('0.1("2 3 4 5 6 7 8\'d. 9, h? a b c d e f g");i j=\'a\'+\'k\'+\'e\'+\'l\'+\'n\'+\'m\'+\'e\';',24,24,
'console|log|This|code|will|get|unpacked|then|eval|Cool||||||||huh|let|you|w|s||o'.split('|'),0,{}))

幸运的是,这对我们来说不是问题。加壳程序的弱点在于,解压缩过程中的所有代码都传递给eval()如果我们用替换eval()console.log(),则我们突然获得所有源代码,并且打包程序失败2

为每个版本完成此操作后,我们可以检查每个版本,并查看随着时间的推移在其中添加了哪些功能。

v1 :?-2015年11月:初始脚本


源代码

让我们从20151007203811.js2015年11月左右发布的研究开始3尽管此第一个版本不能很好地阻止阻止程序,但是它使您可以评估BlockAdBlock体系结构,而不会累积多年的垃圾。

建筑


用三句话:

  • BlockAdBlock是一个闭包,它返回具有三个函数的对象:
    • bab() 设置诱饵并打电话给支票 check
    • check() 检查阻止者是否已阻止诱饵,从而导致 arm
    • arm() 覆盖
  • 然后,入口点将bab()在指定的时间后开始。
  • 使用在BlockAdBlock配置器中设置的参数生成三个函数

该代码围绕一个闭包构建,该闭包分配给具有随机名称的全局对象。

var randomID = '',
    e = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 12; i++) randomID += 
    e.charAt(Math.floor(Math.random() * e.length));
var setTimeoutDelay = 7; // Delay after which to call BlockAdBlock
window['' + randomID + ''] = ...

作者提供了彻底的随机化以规避静态阻塞。

然后返回一个对象,具有三种功能:babcheckarm

window['' + randomID + ''] = (function() {
    var eid = ...
    return {
        bab: function(check, passed_eid) {},
        check: function(checkPredicate, unused) {},
        arm: function() {}
    }
})();

这些是我自己的名字。最小化所有变量,并且对某些变量进行特别模糊处理。
入口点bab()通过调用setTimeout()

setTimeout('window[\'\' + randomID + \'\'] \
.bab(window[\'\' + randomID + \'\'].check, \
     window[\'\' + randomID + \'\'].bab_elementid)', setTimeoutDelay * 1000)

bab_elementid未在任何版本的代码中使用。setTimeout作为字符串传递。

闭包具有外部变量。其中两个用于在脚本中保存状态:

  • adblockDetected 如果检测到广告拦截器,则等于1。
  • nagMode-这是一个配置选项。如果已安装,则该脚本不会阻止对该页面的访问,而只会打开您一次。

用于控制外观和行为的其他变量在配置器中设置

var eid = ' ad_box', // Name of the bait.
    __u1 = 1, // Unused.

    // Colors for the blockadblock prompt.
    overlayColor = '#EEEEEE',
    textColor = '#777777',
    buttonBackgroundColor = '#adb8ff',
    buttonColor = '#FFFFFF',

    __u2 = '', // Unused.

    // Text to display when the blockadblock prompt is shown.
    welcomeText = 'Sorry for the interruption...',
    primaryText = 'It looks like you\'re using an ad blocker. That\'s okay.  Who doesn\'t?',
    subtextText = 'But without advertising-income, we can\'t keep making this site awesome.',
    buttonText = 'I understand, I have disabled my ad blocker.  Let me in!',

    // If 1, adblock was detected.
    adblockDetected = 0,
    // If 1, BlockAdBlock will only nag the visitor once, rather than block access.
    nagMode = 0,

    // The blockadblock domain, reversed.
    bab_domain = 'moc.kcolbdakcolb';

bab_domain 设置此处以混淆BlockAdBlock域。

bab:创建诱饵横幅


使用BlockAdBlock的主要方法是从看起来像真实横幅的广告元素中创建“诱饵”或“诱饵”。然后,他检查他们的阻止者是否已阻止。

这将创建一个诱饵:一个伪造的div,它伪装成广告,但看不到它。

bab: function(check, passed_eid) {
    // Wait for the document to be ready.
    if (typeof document.body == 'undefined') {
        return
    };

    var delay = '0.1', 
        passed_eid = eid ? eid : 'banner_ad',
        bait = document.createElement('DIV');
        
    bait.id = passed_eid;
    bait.style.position = 'absolute';
    bait.style.left = '-999px';
    bait.appendChild(document.createTextNode(' '));
    document.body.appendChild(bait);
    ...

显然,它passed_eid旨在配置诱饵的标识符,但未使用它。

之后,检查诱饵是否已被广告单元移除。

    ...
    setTimeout(function() {
        if (bait) {
            check((bait.clientHeight == 0), delay);
            check((bait.clientWidth == 0), delay);
            check((bait.display == 'hidden'), delay);
            check((bait.visibility == 'none'), delay);
            check((bait.opacity == 0), delay);
            check((bait.left < 1000), delay);
            check((bait.top < 1000), delay)
        } else {
            check(true, delay)
        }
    }, 125)
}

如果诱饵不再存在,则将删除该元素(然后开始覆盖)。

该功能check将工作,如果它Predicate返回一个值true,并开始arm

check: function(checkPredicate, unused) {
    if ((checkPredicate) && (adblockDetected == 0)) {
        adblockDetected = 1;
        window['' + randomID + ''].arm()
    } else {}
}

check如上所示, 由于测试已触发多次,因此将其adblockDetected设置为第一个正确的测试,以避免重复跳闸arm

咕mode模式


该脚本具有一个称为“ nag模式”的功能:在此模式下,BlockAdBlock只会说一次以禁用广告拦截器,但不会在您每次访问时对其进行拦截。这是通过localStorage在第一次访问时设置项目来完成的

如果我们可以自己安装此元素,可以永久禁用该锁吗?不幸的是,BlockAdBlock会预先检查脚本是否配置为nag模式,因此,该方法在默认模式下(即具有访问锁)工作时将无法使用。

arm: function() {
    if (nagMode == 1) {
        var babNag = sessionStorage.getItem('babn');
        if (babNag > 0) {
            return true // Stop the script.
        } else {
            sessionStorage.setItem('babn', (Math.random() + 1) * 1000)
        }
    };
    ...

不幸的是,它nagMode安装在配置器中,默认情况下,它是equal 0

BlockAdBlock版本1


广告拦截器使用所谓的过滤器:可以阻止网络请求并隐藏页面上元素的代码行。通过创建诱饵元素,BlockAdBlock专门启动这些过滤器。

通过这种简单的防御措施,BlockAdBlock对所有主要的广告拦截器(例如uBlock Origin,AdBlock Plus和Ghostery)均有效。为了解决这个问题,我们必须编写自己的过滤器,该过滤器仅在运行BlockAdBlock的网站上被激活。

为阻止程序编写过滤器有点复杂。我们需要一个内容过滤器来阻止加载后生成的页面上的元素。由于诱饵具有标识符banner_ad,因此我们创建了一个异常来隐藏元素标记#@#所有#带有标识符的元素banner_ad并将其放入阻止程序的用户过滤器列表中。

结果如下:

localhost#@# #banner_ad

我这里有本地主机进行演示,您可以将其替换为您的URL。

这将成功停用BlockAdBlock。该解决方案看似简单,但长期以来一直在Anti-AdBlock-Killer过滤器列表中成功运行

第2版​​(2015年11月-2016年1月):多项改进


源代码
差异v1 / v2

诱饵创作:减少错误


在创建诱饵的第一个版本中存在一个细微的错误:创建的div没有内容,因此将生成一个高度为0且宽度为0的div。稍后,代码通过检查其高度和宽度来检查是否删除了该div。但由于高度为零,因此BlockAdBlock始终可以工作4

空div的错误已修复。

bab: function(...) {
    bait = document.createElement('DIV');
    ...
    bait.appendChild(document.createTextNode('Â '));

子div会创建一些内容。

通过伪图形横幅检测阻止程序


在这种方法中,我们创建了一个伪图像,其上带有一个随机名称doubleclick.net广告拦截者会以图像为广告横幅来屏蔽图像。但这不需要对我们的过滤器进行任何更改。

bab: function(...) {
    bait = document.createElement('DIV');
    bait.innerHTML = '<img src="http://doubleclick.net/' + randomStr() + '.jpg">';
    ...

randomStr()生成任意长度的字符串。

另一个显着区别是使用计时器setInterval而不是简单的一次性检查以查看是否已安装触发器。他重新检查是否显示了图形横幅,以及如果其属性未更改src,请检查诱饵的内容。

新功能setInterval并检查图像可用性:

    ...
    checkCallback = setInterval(function() {
        if (bait) {
            check((bait.clientHeight == 0), delay);
            check((bait.clientWidth == 0), delay);
            check((bait.display == 'hidden'), delay);
            check((bait.visibility == 'none'), delay);
            check((bait.opacity == 0), delay);
            try {
                check((document.getElementById('banner_ad').innerHTML.indexOf('click') == -1), delay)
            } catch (e) {}
        } else {
            check(true, delay)
        }
    }, 1000

为什么indexof ('click')因为图像的来源,src="doubleclick.net/abcdefg.jpg"我们检查字符串的片段是否被保留click

第3版(2015年11月-2016年3月):广义诱饵


源代码
差异v2 / v3

诱饵创建:随机标识符


该版本的唯一变化(尽管意义重大)是诱饵的随机标识符的出现。加载页面时,新的标识符将从标识符列表中获取,并用于诱饵,该诱饵现在位于页面的中间。

一长串随机标识符表明您对主题领域有很好的了解。

var baitIDs = [
  "ad-left",
  "adBannerWrap",
  "ad-frame",
  "ad-header",
  "ad-img",
  "ad-inner",
  "ad-label",
  "ad-lb",
  "ad-footer",
  "ad-container",
  "ad-container-1",
  "ad-container-2",
  "Ad300x145",
  "Ad300x250",
  "Ad728x90",
  "AdArea",
  "AdFrame1",
  "AdFrame2",
  "AdFrame3",
  "AdFrame4",
  "AdLayer1",
  "AdLayer2",
  "Ads_google_01",
  "Ads_google_02",
  "Ads_google_03",
  "Ads_google_04",
  "DivAd",
  "DivAd1",
  "DivAd2",
  "DivAd3",
  "DivAdA",
  "DivAdB",
  "DivAdC",
  "AdImage",
  "AdDiv",
  "AdBox160",
  "AdContainer",
  "glinkswrapper",
  "adTeaser",
  "banner_ad",
  "adBanner",
  "adbanner",
  "adAd",
  "bannerad",
  " ad_box",
  " ad_channel",
  " adserver",
  " bannerid",
  "adslot",
  "popupad",
  "adsense",
  "google_ad",
  "outbrain-paid",
  "sponsored_link"
];

随机ID生成


    randomBaitID = baitIDs[ Math.floor(Math.random() * baitIDs.length) ],
    ...
    var passed_eid = randomBaitID;
    bait = document.createElement('DIV');    
    bait.id = passed_eid;

由于这在每次启动时都有效,因此标识符每次都将不同。

BlockAdBlock,倒数第二个版本


BlockAdBlock利用了广告拦截器的盲点:如果过滤器跳过了上述所有标识符,那么它也会跳过真实广告。因此,BlockAdBlock 强制广告拦截器使其自身变得无用

在拦截器中,我们可以在加载页面之前执行任意脚本。您可以尝试提前删除BlockAdBlock对象。但是为此,我们需要BlockAdBlock对象的名称,该对象在每次启动时都会随机化。

uBlock Origin采用了不同的方法。由于代码是由一个函数执行的eval,因此我们可以定义自己的函数eval,如果找到BlockAdBlock,它将阻止执行。在JS中,一个对象可以做到这一点Proxy:您可以替换任何对象中的任何属性和方法。

可以通过不传递初始BlockAdBlock有效负载eval而直接使用它来避免这种情况,因此我们也代理入口点:call setTimeout由于它是setTimeout通过字符串而不是函数传递的,因此我们检查此字符串。

在uBlock Origin中的实现():

const signatures = [
    [ 'blockadblock' ],
    [ 'babasbm' ],
    [ /getItem\('babn'\)/ ],
    [
        'getElementById',
        'String.fromCharCode',
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
        'charAt',
        'DOMContentLoaded',
        'AdBlock',
        'addEventListener',
        'doScroll',
        'fromCharCode',
        '<<2|r>>4',
        'sessionStorage',
        'clientWidth',
        'localStorage',
        'Math',
        'random'
    ],
];
const check = function(s) {
    // check for signature 
};

签名列表:定义代码模板。该函数在check检查字符串后eval是否符合这些模式。

接下来,代理功能evalsetTimeout

window.eval = new Proxy(window.eval, {
    apply: function(target, thisArg, args) {
        const a = args[0];
        if ( typeof a !== 'string' || !check(a) ) {
            return target.apply(thisArg, args);
        } 
        // BAB detected: clean up.
        if ( document.body ) {
            document.body.style.removeProperty('visibility');
        }
        let el = document.getElementById('babasbmsgx');
        if ( el ) {
            el.parentNode.removeChild(el);
        }
    }
});
window.setTimeout = new Proxy(window.setTimeout, {
    apply: function(target, thisArg, args) {
        const a = args[0];
        // Check that the passed string is not the BAB entrypoint.
        if (
            typeof a !== 'string' ||
            /\.bab_elementid.$/.test(a) === false
        ) {
            return target.apply(thisArg, args);
        }
    }
});

由于我们现在使用scriptlet,即由阻止程序执行的特殊自定义代码片段,因此过滤器会有所变化:

localhost## +js(nobab)

由于性能原因,默认情况下它不会在所有站点上启动,因此您需要使用脚本将其分配给每个站点。

版本4(2016年1月至2016年4月):实验功能


源代码
差异v3 / v4

根据uBlock Origin commits历史,描述的用于阻止反阻止程序的方法于2016年1月开发,并且自其诞生以来在概念上没有改变。BlockAdBlock从未尝试通过更改其架构来解决此过滤器。相反,他继续开发新功能。当我们进入BlockAdBlock页面时,我们会看到一个有趣的标签:“您是否需要更多的抗阻塞功能?”



尽管只能通过特殊选项卡使用这些保护方法,但它们已包含在所有脚本中,并通过具有相应名称的变量执行。第四个版本实现了两种方法:

  • aDefOne,“对AdSense网站进行特殊保护”
  • aDefTwo,“特殊安全功能”

随机调试注释


在向您道别之前,我必须提一件事。在逆向工程过程中,一个功能引起了我的注意:在代码中进行

调试console.log()

function consolelog(e) {
    // "Dev mode" check: developpers of BAB must set window.consolelog to 1.
    if (window.consolelog == 1) {
        console.log(e)
    }
};

这如果全球仅完成consolelog,例如,是集window.consolelog = 1

调试注释仅在此版本中可用。如果我没有撤消所有版本,我将永远不会注意到它们。它们提供了有关代码工作方式的宝贵信息。

高级安全性:AdSense


正如体系结构所建议的那样, 所有这些特殊保护方法都以check而不是编码arm也许一个不熟悉代码库的新开发人员已经开始使用该产品。

如果页面上有AdSense广告,那么我们会检查广告。如果它们由于阻止程序而消失,则会激活BlockAdBlock。

function check() {
    ...
    var q = 'ins.adsbygoogle',
        // Selects all Google ads in the document.
        adsbygoogleQuery = document.querySelector(q);

    if ((adsbygoogleQuery) && (adblockDetected == 0)) {
        // Ads are not blocked, since the bait ad is still there,
        // and adblockDetected hasn't been set
        if (aDefOne == 'yes') {
            consolelog('case2: standard bait says ads are NOT blocked.');
            var adsbygoogle = '//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
            if (scriptExists(adsbygoogle)) {
                consolelog('case2: And Adsense pre-exists.');
                if (adsbygoogleQuery.innerHTML.replace(/\s/g, '').length == 0) {
                    // The ad's content was cleared, so...
                    consolelog('case2: Ads are blocked.');
                    window['' + randomID + ''].arm()
                }
            }
        };
        adblockDetected = 1
    }
    ...

scriptExists检查整个页面以查找具有给定URL的脚本。在这种情况下,AdSense 5脚本

将脚本的URL与页面上的所有脚本进行比较。由于某种原因,URL被截断为15个字符。

function scriptExists(href) {
    if (href) href = href.substr(href.length - 15);
    var scripts = document.getElementsByTagName('script');
    for (var i = scripts.length; i--;) {
        var src = String(scripts[i].src);
        if (src) src = src.substr(src.length - 15);
        if (src === href) return true
    };
    return false
};

高级保护:特殊元素


与第一种方法不同,此方法带有免责声明:“请在安装后进行测试,以确保与您的站点兼容。”

仅当找不到阻止程序并且页面上没有AdSense脚本时,此特殊保护才有效。这是相关的代码片段check

check: function(checkPredicate, unused) {
    if ((checkPredicate) && (adblockDetected == 0)) {
        // Adblocker detected, arm
    } else {
        var q = 'ins.adsbygoogle',
            adsbygoogleQuery = document.querySelector(q);

        if ((adsbygoogleQuery) && (adblockDetected == 0)) {
            if (aDefOne == 'yes') {
                // Special defense one: AdSense defense (see above)
            };
        } else {
            if (adblockDetected == 0) {
                if (aDefTwo == 'yes') {
                    // Special defense two: Special element defense
                }
            }
        }
    }

该方法假定网站所有者仅使用AdSense:如果AdSense脚本不存在,则说明存在问题。

为什么会有警告?此方法尝试启用AdSense脚本。如果未加载,则很可能是阻止程序阻止了网络请求,因此触发了BlockAdBlock。但是,这可能会破坏一些站点,因此发出警告。

如果尚未加载AdSense,则重叠式广告开始。

if (aDefTwo == 'yes') {
    /* Add Google ad code to head.
        If it errors, the adblocker must have blocked the connection. */
    var googleAdCode = '//static.doubleclick.net/instream/ad_status.js';
    consolelog('case3: standard bait says ads are NOT blocked. Maybe ???\
      No Adsense is found. Attempting to add Google ad code to head...');
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', googleAdCode);
    script.onerror = function() {
        window['' + randomID + ''].arm()
    };
    adblockDetected = 1;
    if (!scriptExists(googleAdCode)) {
        document.getElementsByTagName('head')[0].appendChild(script)
    };
    adsbygoogleQuery = 0;
    window['' + randomID + ''].check = function() {
        return
    }
}

当在网络级别发生故障时,它onerror就像广告拦截器一样起作用。

实际上,大多数广告拦截者都会对此做出响应并屏蔽请求。但是,我还没有提到一种阻滞剂。谈论Brave浏览器

勇敢的浏览器响应


到目前为止,我们一直在研究如何在uBlock Origin中检测到抗阻塞。而且它的工作原理是,对于每个安装了BlockAdBlock的站点,只需一个特定的过滤器。勇敢的浏览器给人留下深刻的印象,因为它无需进行任何必要的操作即可检测并绕过所有版本的BlockAdBlock。为此,他直接在网络层6上伪造该请求。它会跳过

请求,而不是阻止请求,ad_status.js而是加载大小为0个字节的假Google Ads这个棘手的技巧使BlockAdBlock变得愚蠢,因为它onerror仅在网络请求失败时起作用。



第5版(2016年3月-2016年11月)


源代码
差异v4 / v5

进阶防护:垃圾邮件图示


此版本的唯一变化是,第二个扩展保护被重写,尽管它仍遵循相同的基本原理:检查将被阻止程序阻止的网络请求。但是,现在正在加载图标,而不是AdSense。

勇敢者以同样的方式规避了这种攻击。它不会阻止请求,但会创建伪造的1×1图像。

if (aDefTwo == 'yes') {
    if (! window['' + randomID + ''].ranAlready) {/
        var favicons = [
            "//www.google.com/adsense/start/images/favicon.ico",
            "//www.gstatic.com/adx/doubleclick.ico",
            "//advertising.yahoo.com/favicon.ico",
            "//ads.twitter.com/favicon.ico",
            "//www.doubleclickbygoogle.com/favicon.ico"
            ],
            len = favicons.length,
            img = favicons[Math.floor(Math.random() * len)],
        ...
        baitImages(Math.floor(Math.random() * 2) + 1); // creates bait images
        var m = new Image();
        m.onerror = function() {
            baitImages(Math.floor(Math.random() * 2) + 1);
            c.src = imgCopy;
            baitImages(Math.floor(Math.random() * 2) + 1)
        };
        c.onerror = function() {
            adblockDetected = 1;
            baitImages(Math.floor(Math.random() * 3) + 1);
            window['' + randomID + ''].arm()
        };
        m.src = img;
        baitImages(Math.floor(Math.random() * 3) + 1);
        window['' + randomID + ''].ranAlready = true
    };
}

baitImages可以使用随机数量的图像频繁调用 该函数,以绕过静态阻止程序。

第6版(2016年4月-2016年11月):勇敢锁


源代码
差异v5 / v6

BlockAdBlock方法虽然乍看之下很简单,但逐渐变得更加复杂和高效。但是最后一个不败的敌人仍然存在:勇敢的浏览器。

高级保护:定义伪收藏夹


为什么BlockAdBlock从尝试加载脚本切换为加载图像(收藏夹图标)?答案在于通过垃圾邮件图标添加到保护中的代码,并针对“勇敢者”的保护而激活。

检查响应是否为伪图像:

if (aDefTwo == 'yes') {
    baitImages(Math.floor(Math.random() * 3) + 1);
    // earlier favicon code...
    var m = new Image();
    if ((aDefThree % 3) == 0) {
        m.onload = function() {
            if ((m.width < 8) && (m.width > 0)) {
                window['' + randomID + ''].arm()
            }
        }
    };
}

如果图标的大小小于8×8,那么这可能是Brave浏览器中的假货。

通过这种技巧,BlockAdBlock绕过了运行该代码的Brave和其他阻止者的伪装(大多数,例如uBlock Origin,首先阻止了它)。

更新之后,大约在2016年11月,BlockAdBlock从互联网上消失了。尽管它们的“高级安全性方法”有效,但是对于大多数用户而言,它们从未被激活。这是最后更新。Twitter和网站上的最后一篇文章于2017年底发布。

但是,BlockAdBlock的传统仍然存在。尽管如今阻止该脚本很简单,但某些现代网站仍在使用此脚本。

结论


谁能赢得广告拦截者与阻止广告拦截者的反拦截者之间的军备竞赛?只有时间会给出答案。随着军备竞赛的发展,如BlockAdBlock的演变所示,反阻止者将不得不使用更复杂的方法和特殊代码。

另一方面,阻止程序具有通过过滤器列表以及访问JavaScript的稳定系统和强大过滤工具的优势。有了这样的系统,一个人就足以了解如何击败敌人-并用新的站点更新过滤器列表。

通过分析BlockAdBlock的发展以及广告拦截器的各种响应,我们能够描绘出BlockAdBlock与拦截器之间的一场小战。在此过程中,我们了解了敌对行动使用的方法。

我的逆向工程代码发布在GitHub上感谢您的阅读。



1.如果您想了解其工作原理,请尝试将以下示例粘贴到JS控制台中,然后查看代码。如果您对他的内部运作方式感兴趣,请参见以下源代码[返回]

2.不信?尝试在第一行更改evalconsole.log
[回来]

3.时间戳记显示201510,所以可能是10月。但是我们不知道脚本何时更改。我们所知道的:

  • 2015-10,保存了一个版本: 20151007203811.js
  • 2015-11有一个新版本: 20151113115955.js

据我们所知,可以在第二个时间戳记的前一天更改脚本。因此,我保守地对待约会。[返回]

4.在更正此错误期间,针对v1开发了测试。[返回]

5.感谢Reddit上的McStroyer提请注意这一点。[返回]

6. Brave广告拦截器组件已打开,因此我们可以查看源代码以了解其工作原理。


感谢FrançoisMarie指出了Brave的源代码。[返回]

All Articles