We need to go deeper: обходим скрипт который обходит адблок


    Немного пятничный пост. В последнее время заметил что есть реклама которая пробивает сразу 2(!) расширения блокирующие её. И обычно это очень низкокачественная и навязчивая реклама. Решил разобраться как так, и, возможно, даже попробовать побороть эту дрянь. Кому интересно — прошу под кат (осторожно, много картинок).

    А чем мешает то?


    Я — ярый сторонник блокировки рекламы. Особенно — низкокачественной. Если я не хочу смотреть рекламу на определённом сайте — то такая реклама это деньги на ветер для рекламодателя (бесполезный показ), неуважение ко мне, и нулевой профит вообще для кого либо. Оговорюсь сразу — есть сайты на которых я сознательно вырубаю адблоки (тот же хабр, например). Это сайты которые выбирают тематическую рекламу, а не «АЙФОНЫ СО СКЛАДА 70% СКИДКА!».

    И тут на днях на одном дружественном мне сайте (новостной портал в моём городе) появляется ну ооочень низкопробная реклама в стиле «электрики долго скрывали — чтобы экономить электричество надо всего лишь...». Сначала я подумал что их взломали, но потом оказалось что админ этого сайта просто ввязался в какую-то партнёрку которая работает поверх адблока. И мало того что такая реклама это просто стыд, так она ещё и ломала местами вёрстку. Закрыть её стало делом принципа.

    Попытка 1 — в тупую, или...


    … «пфф, да ща напишу кастомный фильтр для адблока». Пишем фильтр, сохраняем, тестим… А страница то ломается, прям совсем. И косметические фильтры (блокировка по css) тоже не работают. Как так? Но, видимо, поэтому и не было такого фильтра «из коробки». Как окажется позже — наш «зловред» хитрый и заинлайнен, а хромообразные браузеры не позволяют расширениям блокировать инлайновые скрипты. Ну и ну…

    Попытка 2 — меняем адблок


    Слово «адблок» уже давно стало нарицательным, как джип или ксерокс. Так что на самом деле у меня стоит не adblock а ublock (надеюсь это не сочтут за рекламу?). Поэтому для начала тестим работу разных блокировщиков из топа выдачи, которые обещали скрываться от хитрозадых скриптов, и блокировать даже их. Выхлопа, на самом деле, это не дало, и часть рекламы продолжала пролезать (хотя и не вся, но всё же).

    Попытка 3 — смотрим врагу в глаза


    И нет, я не имею ввиду админа ресурса :) Приступим к самому интересному. Лезем смотреть исходники страницы, в надежде найти нашу гадость. И… Бинго!



    Огромная обфусцированная кодина, которая явно тут не спроста. Смотрим похожие сайты (в плане рекламы) — да, это явно оно. И обфусцировано добротно, не банальным p.a.c.k.e.r-ом.

    Немного распакуем, чтобы понять где зарыта собака. Если присмотреться на монстра, можно заметить что это самовызывающаяся функция, внутри которой есть три основные части — большая функция, какой-то массив с числами и небольшой обработкой его же, и вызов первой функции. Скопируем массив и его обработку, запускаем, смотрим вывод.



    Переменная «а» у нас хранит весь набор слов которые нельзя было заменить на однобуквенные. А в первой части мы видим много фрагментов вида а[42], a[69], и т.д. Обфускация оказалась не такой уж и сложной. Делаем замену которая не гарантирует нам работающий код, но сильно облегчит чтение.



    Уже видно более-менее читаемый код. Пропускаем через beautifier, и находим желаемый участок кода (я удалил неважный код чтобы поместиться в одном скрине).



    Эта зараза делает запрос на определённый юрл, и в случае отвала (попадания в фильтр адблока, например) — вызывает window.stop(), чем ломает всю страницу. Довольно хитро, и встроенным функционалом адблока это, вроде, не решаемо. А заблокировать весь скрипт не позволяет хром, ибо инлайн.

    Что же делать?

    После некоторых раздумий, у меня получилось расширение для браузера (ещё одно, да-да) которое занимается чисто блокировкой этой рекламы. Пока что оно заточено именно на эту сеть, потому как других я не знаю. Если кто захочет — форкайте, кидайте пулл реквесты, или отпишитесь в комментах — добавлю поле для добавления своих адресов.

    Работает оно следующим образом — перехватываем запрос на «проверочный» адрес, с помощью жёлудей и спичек элегантного костыля задерживаем его на 5 секунд, а в это время быстро-быстро отправляем сообщение на страницу. Страница принимает это сообщение, и тут же переопределяет window.stop на пустую функцию. Тем временем зловред таки получает отказ, ехидно улыбается, лупит по window.stop, и… ничего. Скрипт доволен (он уверен что всё сломал и пользователь таки отключит адблок), расширение довольно (потому что, как говорит мой коллега, на каждый хитрый зад найдётся хитрый мужской половой орган), пользователь (то есть — мы) доволен. Вероятно, в скором времени разработчики этой заразы что-то придумают и на это, но, пока-что, реклама успешно рубится.

    Расширение заливать никуда не стал, ибо надо платный аккаунт, это proof-of-concept, сегодня пятница, и ещё 1000 и 1 малозначимая отмазка. Так что код на гитхаб.

    UPD. В комментариях возникли вопросы, поэтому тезисно о главном:
    Почему не блокировать все инлайн скрипты — потому что это сломает логику многих сайтов, не подходит
    Почему не возвращать 200 (обманывать) — потому что хром так не умеет. Либо 500ую, либо пропускаем
    Почему не переопределять window.stop сходу — потому что надо успеть во временной отрезок когда DOM-блок head уже существует, но аякс ещё не вернулся. Это ограничение extensions API.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 62
    • 0

      Думаю, проще было бы переопределить window.stop() сразу, сохранив старую функцию в другом объекте, и как минимум логать к ней доступ по stackTrace или как оно в джаваскрипте устроено. Можно даже парсить его, определяя, насколько глубоко сидит вызвавший кролик, и насколько вызов стопа в этом месте валиден, и пропускать внутрь, если да. По идее, должно сломать всех любителей останавливать все скрипты в окне.

      • 0
        Думаю, проще было бы переопределить window.stop() сразу

        А как? Chrome extension не может напрямую модифицировать windows объект (в документации так написано, да и тестил). Поэтому нужно только вставлять в тело html инлайн скрипт который это сделает. А вставить его надо до того как запустится наш зловред. А пока документ не создал блок head — нам некуда инлайнить. А если head уже отрисовали — то уже поздно, так как зловред в нём. Как-то так.
        • 0
          В этом коде window.stop не используется. Хотя есть ребята, что сделали по образу и подобию через window.stop, но по факту способов убить страницу на столько много, что надоест переопределять.

          Есть способ лучше, переопределить XHR, как сделали в user script на форуме список для адблока. Конечно, это обходится, но, как минимум, код на сайтах нужно будет менять.
        • 0
          Довольно хитро, и встроенным функционалом адблока это, вроде, не решаемо. А заблокировать весь скрипт не позволяет хром, ибо инлайн.
          Вообще-то uBlock умеет отключать инлайн-скрипты (надо включить галочку «Я опытный пользователь»):



          Или можно добавить фильтр CSP, который тоже будет блокировать инлайн-скрипты или даже хитро решать какие запускать с помощью параметра nonce, но для этого уже надо отдельное расширение.
          • 0
            А если не надо вырубать вообще все инлайн скрипты, а только этот? Лично у меня не получилось составить такой фильтр. Если умеете — научите :)
            • +1
              И в догоночку — сайт который меня и смутил — CMS на DLE. И там дофига логики в виде инлайн js. Поэтому блокировать всё через CSP — не выход
              • 0
                body > script:nth-child(N), где N это его порядковый номер
                но это не спасет, если они сменят порядок скриптов
                • 0
                  Взял подопытного на главной странице
                  artemjuliawedding.com/js/vendor/modernizr-2.6.2.min.js
                  Добавил правило (варианты пробовал)
                  artemjuliawedding.com## head > script:nth-child(2)
                  Не работает что-то в ABP. Для самых педантичных работает правило .js$script
            • 0
              Смотря на исходники увидел URL сети — не проще его было заблокировать?
              Блокировка есть ведь разная бывает. при желании можно и 200 отдать.
              Посмотрел свои списки — этот домен оказался в рабочих proxy-anonimazer списках.
              • +1
                Извиняюсь, но вы читали пост? Если
                не проще его было заблокировать?
                если скрипт видит что этот юрл заблокирован — он ломает всю страницу. Собственно в этом и вся проблема
                • 0
                  А, а про «200» отдать — может я что-то упустил в документации, но вроде нельзя возвращать произвольные хедеры в ответ скрипту. Или блокируем, или нет, третей опции не дано. Плюс если оно увидит что есть адблок, и при этом получило 200ую — начнёт совать рекламу, с чем мы, собственно, и боремся.
                  • +1
                    /etc/hosts
                    127.0.0.1 evil.network

                    /etc/nginx/sites-enabled/evil.network
                    location ~ / {
                    return 200 'bye';
                    }
                    • +1
                      С таким успехом можно сам сайт к себе перенаправить, вырезать из html ненужные скрипты, и отдавать обратно :)
                      • +2
                        разница большая — между «делать что-то с контентом» и «зараутить мусор в девнул».
                        у меня много таких сетей как раз зароучено на 127.0.0.1.
                        Те, кто не мешают жить при этом — просто довольствуются 404м. те, кому надо чтоб сервер спел и сплясал — получают 200тку.
                        • +1
                          Ну это нормальное решение для девелопера, но не очень для рядового пользователя. Кстати, я не совсем понял из кода этой заразы, так как он специально запутан, но по-моему если оно получает 200, но респонс ему не подходит — то оно считает это провалом.
                          • 0
                            Не считает. Но поправить это не проблема. Тогда вместо /dev/null прийдется делать эмулятор. Для каждого сайта.
                        • 0
                          Пришёл к такому-же решению, когда не смог при помощи uBlock победить одну раздражающую текстовую рекламу. Раздражает, потому что красным цветом выбивается из сине-голубой гаммы сайта и шрифты заголовка рекламного блока значительно крупнее информативного сайта. uBlock, кстати, блок прячет, но только при первой блокировке, а потом эта зараза снова вылазит. Наверно потому что в скрипте рефрешер периодически обновляет спрятанный блок. А индентификторы блока на каждой странице свои.
                          Апач всё-равно крутится на компе, страницей/сайтом пользуюсь с разных компов. Так почему бы ходить не на оригинальный сайт, а на локальную страничку, в которой вырезана вся дрянь?
                          • 0
                            как-то давно баловался с proxomitron, а как Вы предлагаете вырезать из страниц рекламу?
                            • 0
                              Сейчас у меня этим занимается скрипт на perl, поэтому реализовать можно любой достаточно сложный алгоритм поиска и удаления. Вот сегодня научил его подгружать новую версию скрипта, включенного в страницу, когда разработчики сайта, внеся изменения, меняют и его имя (типа с script.013.js на script.014.js).
                        • 0
                          Какое-то время оно работать будет. Но чувствую, что следующим шагом упыри начнут не просто ждать 200 а ждать подписи переданного хеша секретным ключом сервера.
                        • 0
                          Есть еще возможность сделать redirect на страницы закодированные в data url (html страницу, картинку или скрипт):
                          chrome.webRequest.onBeforeRequest.addListener(function(e){
                          	// some conditions
                          	return {redirectUrl: 'data:text/html;charset=utf-8,<html style="background:#000;"><script>window.close();</script></html>'}; 
                          });

                          • 0
                            А с чего вы взяли, что ответ будет интерпретироваться исполняться как html со скриптами?
                            • 0
                              Если редиректить страницу, открывающуюся в новой вкладке, или в iframe, то будет выведена вся закодированная страница со всеми скриптами.

                              П. С. решение проверено на практике
                      • 0

                        Причина-то в администраторе сайта. Почему, используя методы социальной инженерии, не принудить отказаться?
                        Например, запустить в этой сети баннер 'наш админ иванов и.и. — ....'?

                        • 0
                          Кому вы предлагаете это запускать? Владельцу сайта, который сознательно установил подобный код, чтобы не лишать сайт дохода от пользователей с блокировщиками.
                          • 0
                            В баннерной сети же. Из которой этот ширпотреб и лезет на сайт (и никак не контролируется админом сайта, насколько я знаю)
                            • 0
                              насколько я знаю

                        • 0
                          Во, может тут кто-нибудь мне подскажет как заблокировать Яндекс.директ который порой пролезает сквозь блокер? Например на тех же сайтах Яндекса
                          • 0
                            А дайте пример такого сайта, ради интереса)
                            • 0
                              у меня есть смутные воспоминания где-то полугодовой давности о борьбе с рекламой в почтовом интерфейсе то ли яндекса, то ли майлру.
                              Суть была в том, что при попытке косметически заблочить рекламный блок, также блочился и список папок почты, что разумеется, неприемлемо. Я тогда плюнул, и решил потерпеть.

                              Сейчас вот проверил — в яндексовой почте сетевой рекламы не вижу даже при отключенном адблокере (иногда всплывает внизу панель с рекомендацией мобильного приложения).

                              А вот на мэйле реклама пролезает, и в коде страницы творится что-то, на мой взгляд довольно безнадежное — все идентификаторы обфусцированы примерно так:
                              <div id="MkgdI63" class="MkgdI5P MkgdI26"></div>

                              причём, при каждом заходе на страницу эти строки меняются. То есть косметические фильтры ublock тут не пригодны.
                              • 0
                                Кстати да, с этим беда. Я на страницу mail.ru себе stylish делал, чтобы был только бокс с количеством новых писем, и черный фон (без рекламы, новостей, и.т.д.). Пару дней назад стиль сломался, снова всё вылезло. Ну ёпт…

                                причём, при каждом заходе на страницу эти строки меняются.

                                А меняются вооообще-воообще все названия, или есть статические? Тогда к ним можно было бы привязаться, и потом :child(n) делать
                                • 0
                                  А что, проблема генерировать случайное к-во пустых div перед нужным, чтобы :child(n) не работало?
                                  • 0
                                    есть статические — относящиеся к шапке и подвалу. Всё что в середине — выглядит вот так.
                                    Уточнение: всё это веселье — если смотреть инспектором, или через интерфейс «заблокировать элемент» в uBlock, то есть в уже сформированном DOM. В исходном коде страницы во встречающихся фрагментах html идентификаторы не обфусцированы. Только попробуй, догадайся, какой див к какому элементу относится.
                                • 0
                                  www.liveinternet.ru/stat/ua например. Адблок под Хромоподобными браузерами не справляется. uBlock- да.
                                  • 0
                                    Я.Музыка например
                                    • 0
                                      На Я.Музыке ghostery сказал, что заблокировал 6 элементов, среди которых и Yandex.Direct.
                                      В правом сайдбаре остались одна картинка-рекомендация, ведущая на какие-то тизеры.
                                      Там, кстати, исходного html практически нет — всё на скриптах, так что победить можно только редактируя уже сформированную страницу, правя на лету скрипты и перехватывая запросы.
                                    • 0
                                      А дайте пример такого сайта, ради интереса)

                                      Тут — https://habrahabr.ru/post/338580/#comment_10434492
                                      • 0
                                        drive2.ru юблок вообще отказывается грузить, если не добавить яндекс в исключения, например.
                                      • 0
                                        У меня кончилось блоком всего от Яндекса. На DNS. И mail.ru тоже, глобально, все, что относилось к mail.ru. Слишком тесно сплели функционал и рекламу, которая для меня очень навязчива. Пришлось отказаться от функционала.
                                        • 0
                                          См. мой коммент чуть выше. Там пример, когда блока сетей Яндекса недостаточно, потому что сайт-партнёр устанавливает прокси на своём домене, и через этот прокси загружаются как все баннеры, как и остальной контент сайта.
                                      • +1
                                        >>не adblock а ublock
                                        Как дети подрастут — буду им рассказывать сей хрестоматийный пример, как сложно заработать репутацию и легко её потерять.
                                        • +1
                                          «Ничего личного, это просто бизнес» — другой хрестоматийный пример, расскажите им позже, как подрастут еще.
                                        • 0
                                          Было бы интересно сделать возможность менять ID для рекламы на чьё-нибудь, кого не против поддержать (можно сделать через webRequest.onBeforeRequest)

                                          Т.е. ходишь по интернету и большинство просмотренной рекламы идёт на добрые дела.
                                          • 0
                                            Интересно, не проверяют ли рекламные площадки хедер referrer.
                                            • 0
                                              А как вы определите что там за id используется у конкретной рекламы?
                                            • +4

                                              Встречал похожий пример борьбы с adblock'ом, только не на самой рекламе, а на модальнике с просьбой отключить adblock.
                                              Выпиливание модальника из DOM или скрывание с помощью css приводило к перезагрузке страницы. Аналогично нашел обфусцированный inline script, который с интервалом в 2 секунды проверял целостность модальника.
                                              Вот такой хак со stackoverflow для остановки всех интервалов позволил убрать назойливый модальник.
                                              Может и вам в вашем расширении однажды пригодиться :)

                                              • +1
                                                Жму плечами и вкидываю сайт (как ни странно, на действительно нужных мне серьезных сайтах таким не страдают) в blacklist. При попытке захода на него предлагает или поискать нормальный источник или посмотреть котяток.

                                                Пока ни одного случая, когда в блеклист попадало что-то нужное, ибо страдают таким только откровенные помойки, из того что я сталкивался.

                                                Так что режем хвост по самую голову. У нас тут serious business, нам не до шуток ;)
                                              • 0
                                                ublock этот код обходит (со своими стандартными списками). Там сейчас появилось что-то вроде встроенного user script. Но очень топорным способом сейчас это делается.

                                                adblock вообще очень инертен в этой борьбе. Там только вебсокет нормально блокировать научились в конце августа. Видимо, неудачи с монетизацией сильно подкосили их энтузиазм.
                                                • 0
                                                  var startTime = new Date().getTime();
                                                          var randNumber;
                                                  
                                                          while ((new Date().getTime() - startTime) < 5000){
                                                              randNumber = Math.random(); // because an error can happen if this block is empty, or needless
                                                          }
                                                  


                                                  Без обид, но вы бы еще майнер встроили…
                                                  • 0
                                                    Я знаю что код странный, но на то есть причины. While с пустым телом как-то нестабильно себя ведёт, очень нестабильно в плане времени. Пробовал делать просто какой-то мусор в теле — видимо компилятор палит то что он не используется далее, оптимизирует, и в итоге получается снова пустое тело. И только вот так получилось заставить нормально держать ровно 5 секунд ¯\_(ツ)_/¯
                                                    • 0
                                                      Код не то. чтобы странный, он 5 секунд выедает 100% ядра в бесконечном цикле. После изменения кода на такой, что будет делать 3-4 попытки, пользователю прийдется 15-20 сек ждать пока ваш рандомайзер открутится. Не говоря уже о том, что могут пошутить и бесконечное число попыток сделать, при другом сценарии это мешать не будет, а вот с вашим решением в доме появится лишняя печка.
                                                      • 0
                                                        На самом деле это одна из причин почему я не допиливал это расширения до продакшн-вида, чтобы можно было залить его в маркет, и т.д. Потому что хром не умеет по-человечески задерживать запрос, а мой костыль — ну очень уж не оптимальный. Но, как proof-of-concept — почему бы и нет?
                                                        • 0
                                                          Не мучайте браузеры тех, кто решится ваше расширение поставить. Вот пример для этого прокси из ruadlist js fixes:

                                                          // piguiqproxy.com circumvention prevention
                                                              scriptLander(
                                                                  function()
                                                                  {
                                                                      let _open = XMLHttpRequest.prototype.open;
                                                                      let blacklist = /[/.@](piguiqproxy\.com|rcdn\.pro)[:/]/i;
                                                                      XMLHttpRequest.prototype.open = function(method, url)
                                                                      {
                                                                          if (method === 'GET' && blacklist.test(url))
                                                                          {
                                                                              this.send = () => null;
                                                                              this.setRequestHeader = () => null;
                                                                              console.log('Blocked request: ', url);
                                                                              return;
                                                                          }
                                                                          return _open.apply(this, arguments);
                                                                      };
                                                                  }
                                                              );
                                                          • 0
                                                            Я выложил не готовое расширение, а код на github. Человек который знает как его установить себе и запустить — наверняка прочитает исходники, и не будет мучать свой браузер не зная того) Да и опять таки — я нигде не говорил что у меня шикарное решение которое подойдёт всем и каждому.

                                                            P.S. ruadlist js затестил, работает, блокирует. Спасибо за совет)
                                                            • 0
                                                              Прекрасно! Чем меньше параллельных костылей, тем легче браузеру. :)
                                                              • +2
                                                                Кстати, здравствуйте. Если что, то я автор JS Fixes. :)

                                                                А таймаут в 5 секунд без загрузки системы можно сделать через async await, например. Смотря где применить так-как промис async возвращает сразу, а вот код внутри уже можно тормозить через дичь вроде:
                                                                await new Promise((r,j)=>setTimeout(()=>r(), 5000));
                                                                • 0
                                                                  Вот это поворот! Спасибо за совет, попробую, как-то и не подумал о таком способе использования async/await
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                      • 0
                                                        По-моему, это тот случай, когда не зазорно ещё и автоматически прокликивать эту рекламу в бэкграунде. Вот — vc.ru/21708-adnauseam
                                                        • 0
                                                          Открою секрет, такая реклама в основном по cpa работает и «скликать» ее можно разве что сделав фейковый заказ.
                                                        • 0
                                                          Просьба проверить ту рекламу под RU AdList JS Fixes и RU AdList CSS Fixes,
                                                          если это возможно (не уверен, что в хроме все будет работать).
                                                          И зарепортить разрабу, пусть включит в новый выпуск.
                                                          Все же лучше объединять усилия. :)
                                                          • 0
                                                            А uMatrix пробывал? Он блокирует по запрашиваемым ресурсам.

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.