BlockAdBlock Advertising Anti-Blocking Reverse Engineering

Si vous utilisez des bloqueurs de publicités, vous pourriez rencontrer BlockAdBlock . Ce script détecte votre bloqueur et ne l'autorise pas sur le site tant que vous ne le désactivez pas. Mais je me demandais comment ça fonctionnait. Comment l'anti-bloquant détecte-t-il les bloqueurs? Et comment les bloqueurs réagissent-ils à cela et comment bloquent-ils les anti-bloquants?

Histoire de l'ingénierie inverse


La première chose que j'ai faite a été de regarder leur site . BlockAdBlock propose un configurateur avec des paramètres: l'intervalle d'attente et l'apparence de l'avertissement, générant différentes versions du script.

Cela m'a fait penser aux versions. Et s'il ne pouvait pas voir une seule version, et tout d'un coup? Alors je l'ai fait. Je suis remonté dans le temps en utilisant la Wayback Machine . Après cela, j'ai téléchargé toutes les versions de BlockAdBlock et les ai hachées.

Liste de toutes les versions de BlockAdBlock, avec sha1sum


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

Seules six versions diffèrent les unes des autres, et la dernière d'entre elles se réfère à 2016, bien que certains sites utilisent toujours BlockAdBlock. C'est génial, car il nous suffira de rétroconcevoir la seule version, puis d'inverser les différences. Spoiler: nous verrons le développement d'idées et même les restes de code de débogage.

J'ai posté toutes les versions sur GitHub . Si vous voulez regarder les différences, alors dans ce dépôt chaque commit est une nouvelle version.

Déballage


Le code du script n'est pas minifié, mais emballé par un JS-packer de Dean Edwards avec des changements cosmétiques et sans aucune modification de la logique 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,{}))

Heureusement, ce n'est pas un problème pour nous. La faiblesse du packer est que tout le code lors du déballage est transmis à eval(). Si nous remplaçons eval()par quelque chose comme console.log(), alors nous obtenons soudainement tout le code source, et le packer est vaincu 2.

Après avoir fait cela pour chaque version, nous pouvons examiner chacune d'entre elles et voir quelles fonctions y ont été ajoutées au fil du temps.

v1 :? - novembre 2015: scénario initial


Code source

Commençons par une étude 20151007203811.jspubliée vers novembre 2015 3. Bien que cette première version ne bloque pas très bien les bloqueurs, elle vous permet d'évaluer l'architecture BlockAdBlock sans les ordures qui se sont accumulées au fil des ans.

Architecture


En trois phrases:

  • BlockAdBlock est une fermeture qui renvoie un objet avec trois fonctions:
    • bab() établit l'appât et appelle un chèque check
    • check() vérifie si le bloqueur a bloqué l'appât, provoquant arm
    • arm() recouvrir
  • Le point d'entrée bab()démarre ensuite après un laps de temps spécifié.
  • Trois fonctions sont générées avec des arguments définis dans le configurateur BlockAdBlock .

Le code est construit autour d'une fermeture affectée à un objet global avec un nom aléatoire.

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 + ''] = ...

L'auteur a prévu une randomisation approfondie pour éviter le blocage statique.

Puis retourne un objet avec trois fonctions: bab, checket arm.

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

Ce sont mes propres noms. Toutes les variables sont minifiées et certaines sont spécialement obscurcies.
Le point d'entrée est bab()appelé setTimeout().

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

bab_elementidnon utilisé dans aucune version du code. setTimeoutpassé sous forme de chaîne.

Une fermeture a des variables externes. Deux d'entre eux sont utilisés pour enregistrer l'état dans un script:

  • adblockDetected égal à 1 si un bloqueur de publicités est détecté.
  • nagMode- Il s'agit d'une option de configuration. S'il est installé, le script ne bloque pas l'accès à la page, mais ne vous active qu'une seule fois.

D'autres variables pour contrôler l'apparence et le comportement sont définies dans le configurateur .

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 défini ici pour tenter de brouiller le domaine BlockAdBlock.

bab: créer une bannière leurre


La principale méthode de travail avec BlockAdBlock consiste à créer des «appâts» ou des «appâts» à partir d'éléments publicitaires qui ressemblent à de vraies bannières. Il vérifie ensuite si leur bloqueur s'est bloqué.

Cela crée un leurre: un faux div qui prétend être une annonce mais est caché à la vue.

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);
    ...

Apparemment, il est passed_eiddestiné à configurer l'identifiant de l'appât, mais il n'est pas utilisé.

Après cela, il est vérifié si l'appât a été supprimé par le bloc d'annonces.

    ...
    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)
}

Si l'appât n'existe plus, l'élément est supprimé (et nous commençons la superposition).

La fonction checkfonctionnera si elle Predicateretourne une valeur trueet démarre arm.

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

Comme le test est checkdéclenché plusieurs fois, comme indiqué ci-dessus, il est adblockDetectedréglé sur le premier test correct afin d'éviter des déclenchements répétés arm.

Mode grognement


Le script a une fonction appelée «mode nag»: dans ce mode, BlockAdBlock ne dira qu'une seule fois pour désactiver le bloqueur de publicités, mais ne vous bloquera pas à chaque fois que vous visiterez. Cela se fait en définissant l'élément localStoragelors de la première visite.

Si nous pouvions installer cet élément nous-mêmes, pourrions-nous désactiver le verrouillage pour toujours? Malheureusement, BlockAdBlock vérifie à l'avance si le script a été configuré pour le mode nag, donc cette méthode ne fonctionnera pas lorsqu'elle fonctionne en mode par défaut, c'est-à-dire avec un verrouillage d'accès.

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)
        }
    };
    ...

Malheureusement, il est nagModeinstallé dans le configurateur, et par défaut, il est égal 0.

BlockAdBlock version 1


Les bloqueurs de publicités utilisent des filtres dits: des lignes de code qui peuvent bloquer les demandes du réseau et masquer des éléments sur la page. En créant des éléments d'appât, BlockAdBlock lance spécifiquement ces filtres.

Avec cette défense simple, BlockAdBlock est efficace contre tous les principaux bloqueurs de publicités tels que uBlock Origin, AdBlock Plus et Ghostery. Pour contrer cela, nous devons écrire notre propre filtre, qui n'est activé que sur les sites qui exécutent BlockAdBlock.

Écrire des filtres pour un bloqueur est un peu compliqué. Nous avons besoin d' un filtre de contenu qui bloque les éléments de la page générés après le chargement. Puisque l'appât a un identifiant banner_ad, nous créons une exception pour masquer les élémentsmarqué #@#pour tous les éléments #avec un identifiant banner_adet le mettre dans la liste des filtres utilisateur de notre bloqueur.

Le résultat est le suivant:

localhost#@# #banner_ad

J'ai localhost ici pour démonstration, vous pouvez le remplacer par votre URL.

Cela désactive avec succès BlockAdBlock. La solution peut sembler simple, mais elle fonctionne depuis longtemps avec succès dans la liste de filtres Anti-AdBlock-Killer .

Version 2 (novembre 2015 - janvier 2016): plusieurs améliorations


Différence de code source
v1 / v2

Création d'appâts: moins de bugs


Il y a une erreur subtile dans la première version de la création de l'appât: le div créé n'a pas de contenu, donc un div est généré avec une hauteur de 0 et une largeur de 0. Plus tard, le code vérifie si le div est supprimé en vérifiant sa hauteur et sa largeur. Mais comme sa hauteur était nulle, BlockAdBlock fonctionnait toujours 4.

L'erreur d'une div vide est corrigée.

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

Un div enfant est créé avec du contenu.

Détection de bloqueurs à travers de fausses bannières graphiques


Dans cette méthode, nous créons une fausse image avec un nom aléatoire doubleclick.net. Les bloqueurs de publicités bloqueront l'image, pensant qu'il s'agit d'une bannière publicitaire. Mais cela ne nécessite aucune modification de notre filtre.

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

randomStr()génère une chaîne de longueur arbitraire.

Une autre différence notable était l'utilisation d'une minuterie setIntervalau lieu d'une simple vérification unique pour voir si un déclencheur est installé. Il revérifie si la bannière graphique est affichée et si son attribut n'est pas modifié src, vérifiant le contenu de l'appât.

Nouveau setIntervalet vérifiez la disponibilité des images:

    ...
    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

Pourquoi indexof ('click')? Parce que la source de l'image src="doubleclick.net/abcdefg.jpg"et nous vérifions si le fragment de la chaîne est conservé click.

Version 3 (novembre 2015 - mars 2016): appât généralisé


Différence de code source
v2 / v3

Création d'appâts: identificateurs aléatoires


Le seul changement dans cette version, bien que significatif, est l'apparition d'identificateurs aléatoires pour l'appât. Le nouvel identifiant est extrait de la liste des identifiants lors du chargement de la page et est utilisé pour l'appât, qui est maintenant placé au milieu de la page.

Une longue liste d'identifiants aléatoires démontre une bonne connaissance du sujet.

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"
];

Génération d'ID aléatoire


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

Comme cela fonctionne à chaque démarrage, l'identifiant sera différent à chaque fois.

BlockAdBlock, troisième à dernière version


BlockAdBlock exploite l'angle mort des bloqueurs de publicités: si un filtre ignore tous les identifiants ci-dessus, il ignorera également les vraies publicités. Ainsi, BlockAdBlock force le bloqueur de publicités à se rendre inutile .

Dans les bloqueurs, nous pouvons exécuter des scripts arbitraires avant de charger la page. Vous pouvez essayer de supprimer l'objet BlockAdBlock à l'avance. Mais pour cela, nous avons besoin du nom de l'objet BlockAdBlock, qui est randomisé à chaque démarrage.

uBlock Origin a adopté une approche différente. Puisque le code est exécuté par une fonction eval, nous pouvons définir notre propre fonction evalqui bloquera l'exécution si nous trouvons BlockAdBlock. Dans JS, un objet est capable de cela Proxy: vous pouvez remplacer n'importe quelle propriété et méthode dans n'importe quel objet.

Cela peut être contourné en ne passant pas la charge utile BlockAdBlock initiale eval, mais en l'utilisant directement, nous utilisons donc également le proxy comme point d'entrée: l'appel setTimeout. Puisqu'il est setTimeoutpassé par une chaîne, pas une fonction, nous vérifions cette chaîne.

Implémentation dans uBlock Origin ( source ):

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 
};

Liste des signatures: définition des modèles de code. La fonction checkvérifie la chaîne après evalpour la conformité avec ces modèles.

Ensuite, proxy les fonctions evalet setTimeout.

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);
        }
    }
});

Puisque nous utilisons maintenant un scriptlet , un fragment de code personnalisé spécial exécuté par le bloqueur, le filtre change un peu:

localhost## +js(nobab)

Il ne démarre pas par défaut sur tous les sites pour des raisons de performances, vous devez donc l'attribuer à chaque site à l'aide d'un script.

Version 4 (janvier 2016 - avril 2016): fonctions expérimentales



Différence de code source v3 / v4

La méthode décrite pour bloquer les anti-bloquants a été développée en janvier 2016, selon l' histoire des commits d'uBlock Origin , et n'a pas changé conceptuellement depuis sa création. BlockAdBlock n'a jamais essayé de contourner ce filtre en modifiant son architecture. Au lieu de cela, il a continué à développer de nouvelles fonctionnalités. Et quand nous allons à la page BlockAdBlock, nous voyons un onglet intéressant: "Avez-vous besoin de plus de puissance anti-blocage?"



Bien que ces méthodes de protection ne soient disponibles que via un onglet spécial, elles sont incluses dans tous les scripts et sont exécutées via des variables portant les noms correspondants. La quatrième version implémente deux méthodes:

  • aDefOne, «Protection spécifique pour les sites AdSense»
  • aDefTwo, «Fonction spéciale de sécurité»

Commentaires de débogage aléatoire


Avant de vous dire au revoir, je dois mentionner encore une chose. Dans le processus de reverse engineering, une fonction a retenu mon attention: le

débogage console.log()directement dans le code!

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

Cela n'est possible que si global consolelog, par exemple, est défini window.consolelog = 1.

Les commentaires de débogage ne sont disponibles que dans cette version. Si je n'avais pas inversé toutes les versions, je ne les aurais jamais remarquées. Ils fournissent des informations précieuses sur le fonctionnement du code.

Sécurité avancée: AdSense


Toutes ces méthodes de protection spéciales sont encodées dans check, et non dans arm, comme le suggère l'architecture. Peut-être qu'un nouveau développeur qui ne connaît pas la base de code a commencé à travailler sur le produit.

Si AdSense est actif sur la page, nous vérifions les annonces. S'ils ont disparu à cause d'un bloqueur, BlockAdBlock est activé.

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
    }
    ...

scriptExistsvérifie la totalité de la page pour un script avec une URL donnée. Dans ce cas, le script AdSense 5.

L'URL du script est comparée à tous les scripts de la page. Pour une raison quelconque, l'URL est tronquée à 15 caractères.

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
};

Protection avancée: élément spécial


Contrairement à la première, cette méthode est accompagnée d'une clause de non-responsabilité: "Veuillez tester après l'installation pour garantir la compatibilité avec votre site."

Cette protection spéciale ne fonctionne que si aucun bloqueur n'est trouvé et qu'il n'y a pas de script AdSense sur la page. Voici l'extrait de code correspondant 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
                }
            }
        }
    }

La méthode suppose que les propriétaires de sites utilisent uniquement AdSense: si un script AdSense n'existe pas, alors quelque chose ne va pas.

Pourquoi y a-t-il eu un avertissement? Cette méthode tente d'activer le script AdSense. S'il ne se charge pas, le bloqueur a probablement bloqué la demande réseau, donc BlockAdBlock est déclenché. Mais cela peut ruiner certains sites, d'où l'avertissement.

Si AdSense n'a pas été chargé, la superposition démarre.

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
    }
}

Lorsqu'un échec se produit au niveau du réseau, cela fonctionne onerrorcomme si un bloqueur de publicités fonctionnait.

En effet, la plupart des bloqueurs de publicités y répondent et bloquent la demande. Mais il y a un bloqueur que je n'ai pas encore mentionné. Parlez du navigateur Brave .

Réponse de Brave Browser


Jusqu'à présent, nous avons étudié comment l'anti-blocage est détecté dans uBlock Origin. Et cela fonctionne, seul un filtre spécifique est requis pour chaque site où BlockAdBlock est installé. Le navigateur Brave est impressionnant en ce qu'il détecte et contourne BlockAdBlock de toutes les versions sans aucune action nécessaire de la part de l'utilisateur. Pour ce faire, il simule la requête directement au niveau de la couche réseau 6.

Au lieu de bloquer la demande, ad_status.jsil l'ignore, mais charge de fausses annonces Google de 0 octet . Cette astuce astucieuse trompe BlockAdBlock, car elle onerrorne fonctionne que si la demande réseau échoue.



Version 5 (mars 2016 - novembre 2016)


Différence de code source
v4 / v5

Protection avancée: favicons de spam


Le seul changement dans cette version est que la deuxième protection étendue a été réécrite, bien qu'elle adhère toujours au même principe de base: vérifier les requêtes réseau qui seront bloquées par le bloqueur. Mais maintenant, les favicons se chargent à la place d'AdSense.

Brave élude cette attaque de la même manière. Il ne bloque pas les demandes, mais crée de fausses images 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
    };
}

La fonction baitImagespeut être appelée fréquemment, avec un nombre aléatoire d'images, pour contourner les bloqueurs statiques.

Version 6 (avril 2016 - novembre 2016): Brave lock



Différence de code source Les

méthodes BlockAdBlock v5 / v6 , bien que simples à première vue, sont progressivement devenues plus complexes et plus efficaces. Mais le dernier ennemi invaincu est resté: le navigateur Brave.

Protection avancée: définition d'un faux favicon


Pourquoi BlockAdBlock a-t-il cessé d'essayer de charger un script pour charger des images (favicons)? La réponse se trouve dans le code qui s'ajoute à la protection via des favicons anti-spam, et qui est activé contre la protection de Brave.

Vérification de la réponse pour une fausse image:

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()
            }
        }
    };
}

Si la taille du favicon est inférieure à 8 × 8, il s'agit probablement d'un faux du navigateur Brave.

Avec cette astuce, BlockAdBlock contourne le déguisement de Brave et des autres bloqueurs qui exécutent ce code (la plupart, comme uBlock Origin, le bloquent en premier).

Après cette mise à jour, vers la fin de novembre 2016, BlockAdBlock a disparu d'Internet. Bien que leurs «méthodes de sécurité avancées» fonctionnent, elles n'ont jamais été activées pour la plupart des utilisateurs. C'était la dernière mise à jour. Le dernier post sur Twitter et sur le site a été publié fin 2017.

Cependant, l'héritage de BlockAdBlock perdure. Bien qu'il soit trivial de le bloquer de nos jours, ce script est toujours utilisé par certains sites modernes.

Conclusion


Qui va gagner la course aux armements entre les bloqueurs de publicité et les anti-bloquants qui bloquent les bloqueurs de publicité? Seul le temps nous le dira. À mesure que la course aux armements se développe, les anti-bloquants devront utiliser des méthodes plus sophistiquées et un code spécial, comme le montre l'évolution de BlockAdBlock.

D'autre part, les bloqueurs ont l'avantage de systèmes stables et de puissants outils de filtrage via des listes de filtres, ainsi que l'accès à JavaScript. Avec un tel système, il suffit qu'une personne comprenne comment vaincre l'ennemi - et mette à jour la liste des filtres avec de nouveaux sites.

En analysant l'évolution de BlockAdBlock, ainsi que les différentes réponses des bloqueurs de publicités, nous avons pu brosser un tableau d'une petite guerre entre BlockAdBlock et les bloqueurs. Ce faisant, nous avons appris quelles méthodes sont utilisées dans les hostilités.

Mon code de reverse engineering est publié sur GitHub . Merci pour la lecture.



1. Si vous voulez avoir une idée de comment cela fonctionne, essayez de coller l'exemple ci-dessous dans la console JS, puis examinez le code. Si vous êtes intéressé par son fonctionnement interne, voici le code source . [retour]

2. Ne croyez pas? Essayez de passer evalà console.logla première ligne.
[rendre]

3. L'horodatage indique 201510, alors peut-être octobre. Mais nous ne savons pas quand le script a changé. Tout ce que nous savons:

  • 2015-10, une version a été enregistrée: 20151007203811.js
  • 2015-11 il y a une nouvelle version: 20151113115955.js

À notre connaissance, le script pourrait être modifié la veille du deuxième horodatage. Ainsi, j'aborde prudemment la datation. [retour]

4. Pendant la correction de cette erreur, des tests ont été développés pour v1. [retour]

5. Merci à McStroyer sur Reddit, qui a attiré l'attention sur ce point. [retour]

6. Le composant Brave ad blocker est ouvert, nous pouvons donc regarder le code source pour avoir une idée de son fonctionnement.


Merci à François Marie d' avoir signalé le code source de Brave. [retour]

All Articles