⬆️ ⬇️

AdBlockBlock - bypass ad blockers. Method 1

Doing something that violates the sacred will of the local gentlemen to the content, which only they want to see around themselves is, of course, a ungrateful and karmically dangerous thing. But the arms race between advertising blockers and advertising systems cannot be avoided, so we need to talk about it. Now, when the total volume of cut traffic revolves around 1% - everything is somewhat vyalenko, but there are already looking around site owners who receive less than 30% of the money from advertising. Advertising networks are beginning to communicate with each other, exchange specifications, there is already some Israeli startup on this topic - I think it is tempting to increase the income immediately and by interest with minimal effort. In the Russian segment, everything is still costing with admonitory ads like “You have disabled advertising - this prevents us from developing” or simply ignoring the fact of the existence of such users. I must say, let it all remain so .



Here, only in the mode of minimal proof of performance - we will bypass the most common type of ad blockers - by the URL pattern. The method should support:





To achieve the result, we will mask all URLs of advertising networks through a kind of proxy between the publisher server and the advertiser. The method is not the cheapest in terms of resources, it is also potentially dangerous for advertisers: the source of fraud cannot be done without assessing the quality of traffic through clicks or conversions, and for publishers, if Google is banned, the entire site will be banned. Accordingly, it requires a certain threshold of trust between them, but these are all separate issues. Yes, all coincidences or references are random, took what came to hand.

')

So, we want all the site calls for those who have detected ad blockers, like

ads.*.ru/228129/prepareCode?pp=g&ps=bugf&p2=ezfl&pct=a&plp=a&pli=a&pop=a'





Replace with local, indistinguishable from the content of the site, for example



/meduza/2015/09/28/shapitorbFgFQ4Y7_Z3jaPSRix09Pn5VyrnV5Pcki64JcXvjIyzlgYXerh3yMMgY8DB5vleYAkS_gbRiHDSyQHU7QscAd38-1tKyYnnLjLSlpHq6aJ4sEo





For clarity, I'll start with what will be needed.



1. Take an advertising tag, encrypt the first entry point (the one that is in the ad tag itself) manually



 <!-- : 990x90js--> <!--: < >--> <script type="text/javascript"> <!-- (function(){ //var link = 'http://ads.XXXXXX.ru/228129/prepareCode?pp=g&ps=bugf&p2=ezfl&pct=a&plp=a&pli=a&pop=a', var link = '/meduza/2015/09/28/shapitorbFgFQ4Y7_Z3jaPSRix09Pn5VyrnV5Pcki64JcXvjIyzlgYXerh3yMMgY8DB5vleYAkS_gbRiHDSyQHU7QscAd38-1tKyYnnLjLSlpHq6aJ4sEo', params = 'phone'; new AXXXBanner(link, params).createBanner(); })(); //--> </script> 


2. Slightly expand the server configuration (in this case, nginx) by starting to process such URLs in a special mode and returning the processing of the request at the mercy of the script:



  location /meduza/2015/09/28/shapito { set $prefix "http://localhost:9090/meduza/2015/09/28/shapito"; rewrite shapito(.*)$ /adbb.php?query=$1&url_mask_prefix=$prefix&ua=$http_user_agent&remote_addr=$remote_addr; } 


All - after this ad will be shown.



How it works.



Retreat, choice of means
I wanted to write all the logic on Lua, but I was too lazy to rebuild nginx and, since PHP is potentially closer to the site owners, I used it. Trivial code was written for proof-of-concept, in two pages, can be reproduced in any language - attached at the end. There is a dependency on mbcrypt and curl.



Work algorithm.



1. Decrypt the passed string with the configuration key, receiving the target URL



2. Take a domain from there, encrypt it



ads.XXXXXX.ru -> pPM9l7raWppVawqO





3. For this key, look in the cookies sent to us, find the value of the form



niFJ2HLxzm27hCLnQUvcmLx62sEU-worI4tjmSAfqxNSMR6DSZ279lampNh_CN2jlu7FXaVk0WRVt-HMxy4vdm0uEncngawC6RvcKBwRXrT0wIi0icl4BvSXPJzH99C_5-mTmneEISfz





And we will decrypt it, having received the original cookies, once sent to us from this server for installation.



4. If the URL contains click markers, do nothing more - just give 302 redirect and exit.



5. Otherwise, we make a request to the target URL, passing the cookie, the source user User-Agent and the real IP in the X-Forwarded-For header.



6. Analyze the response from the server:



5.1. If a Set-Cookie was sent - repack them (the key is an encrypted domain, the value is an encrypted Set-Cookie from the call results), and install them, you get something like



Set-Cookie:oI5upmClXJaq6DY4QWT5g5ZsvQ=niFJ3pLNsqSh0x19ux1HB-3XQiMb3XDhuJC5Byrefm_xOIDJBlZ2FL5q2zvyVtPcNOimtTk-lfoY; expires=Mon, 28-Dec-2015 08:23:57 GMT; Max-Age=7776000





5.2. If the content-type does not contain text or javascript - for example, images, gif-pixels - we give the content as-is, adjusting the caching to taste (but it is better not necessary). And we leave



5.3. For all other content-types, we search for the URL in the response body by a pattern, encrypt each one, and using the prefix transmitted from the server, we replace the calls with masked ones, that is, places like



 object_1986381203 += '<a href="http://ads.XXXXX.ru/228129/goLink?pr=gleurtb&p5=dcxnh&p1=bohgi&p2=ezfl" target="_blank"><img src="http://content.XXXXX.ru/150924/XXXX/507850/1421973.jpg" width="990" height="90" alt="" border=0></a>'; 


Will be given away like this:



 object_1986381203 += '<a href="/meduza/2015/09/28/shapitorbFgFQ4Y7_Z3jaPSRix09Pn5VyrnV5P" target="_blank"><img src="/meduza/2015/09/28/shapitorbFgFQ4Y7_Z3jaPSRix09Pn5VyrnV5Pcki64JcXvjIyzlgYXerh3yMMgY8DB5vleYA" width="990" height="90" alt="" border=0></a>'; 


We give the changed content and exit.



6. The browser itself will make the necessary calls, for each of which everything will be completed with p.1



Script configuration section



 $CONF = array( 'mask_urls' => array( 'prefix' => '/meduza/2015/09/28/shapito', //default, will be overriden by `url_mask_prefix` 'url_search_patterns' => array( '@https?://[^\\\'\"\n\r\?]+@i', ), ), 'redirect_if_contains' => array( '/click', '/reference', '/link', '/goLink' ), 'encrypt' => array ( 'iv' => '2uV17Dil', 'key' => 'JbaSyaXwD46qIlKdt8mJ4', 'cipher' => MCRYPT_GOST, 'mode' => MCRYPT_MODE_CFB ), 'cookie' => array( 'expire' => 90*24*60*60, ), 'url_call' => array(), ); 


Once again, this is a concept - you cannot use it in production! He will make an anonymous proxy from your server.



adbb.php
 <?php $CONF = array( 'mask_urls' => array( 'prefix' => '/meduza/2015/09/28/shapito', //default could be taken from `url_ 'url_search_patterns' => array( '@https?://[^\\\'\"\n\r\?]+@i', ), ), 'redirect_if_contains' => array( '/click', '/reference', '/link', '/goLink' ), 'encrypt' => array ( 'iv' => '2uV17Dil', 'key' => 'JbaSyaXwD46qIlKdt8mJ4', 'cipher' => MCRYPT_GOST, 'mode' => MCRYPT_MODE_CFB ), 'cookie' => array( 'expire' => 90*24*60*60, ), 'url_call' => array(), ); require_once __DIR__."/adbb_functions.php"; if ( !array_key_exists('query', $_REQUEST) or !$query = $_REQUEST['query']) return; adbb_debug_log('Request params', $_REQUEST); $extra_query = ''; if (($delpos = strpos($query, '&')) !== false) { adbb_debug_log('Extra parameters passed, getting encypted part only. @ position ', $delpos); $extra_query = substr($query, $delpos); adbb_debug_log('Extra query ', $extra_query); $query = substr($query, 0, $delpos); adbb_debug_log('Encrypted query after cut ', $query); } adbb_debug_log('We should have successfully decrypted url with the key for all further logic, decrypting...'); if ( !$url = adbb_decrypt($query, $CONF['encrypt']) ) { adbb_debug_log('Failed. Exiting'); return; } adbb_debug_log('Decrypted URL', $url); if (array_key_exists('url_mask_prefix', $_REQUEST)) { adbb_debug_log('Overriding initial url_mask_prefix ['.$CONF['mask_urls']['prefix'].'] to', $_REQUEST['url_mask_prefix']); $CONF['mask_urls']['prefix'] = $_REQUEST['url_mask_prefix']; } $extra_args = $_REQUEST; unset($extra_args['query'], $extra_args['ua'], $extra_args['remote_addr'], $extra_args['url_mask_prefix']); $url_to_call = $url . (strpos($url, '?') === false ? '?' : '&' ) . http_build_query($extra_args).'&'.$extra_query; adbb_debug_log('Looking for cookies to send to remote host...'); $domain_cookies = ''; $parse = parse_url($url_to_call); $domain_to_call = $parse['host']; if ($domain_to_call) { $domain_cookie_key = adbb_encrypt($domain_to_call, $CONF['encrypt']); adbb_debug_log('Will look for cookies for domain ['.$domain_to_call.'], key', $domain_cookie_key); if (isset($_COOKIE) and array_key_exists($domain_cookie_key, $_COOKIE) and $_COOKIE[$domain_cookie_key]) { adbb_debug_log('Found something, will try to decrypt cookie value ', $_COOKIE[$domain_cookie_key]); if ($json_encoded_cookies = adbb_decrypt($_COOKIE[$domain_cookie_key], $CONF['encrypt']) and $cookies = @json_decode($json_encoded_cookies) ) { adbb_debug_log('Going to send cookies decrypted', $cookies); $domain_cookies = implode('; ', $cookies); } } } adbb_debug_log('Checking if redirect is needed...'); foreach ($CONF['redirect_if_contains'] as $needle) { if (strpos($url_to_call, $needle) !== false ) { $url_to_call .= '&cookies='.urlencode($domain_cookies); adbb_debug_log('URL contains '.$needle.' going to redirect to ', $url_to_call); header('Location: '.$url_to_call); exit; } } adbb_debug_log('About to call remote URL ', $url_to_call); adbb_debug_log('With User-Agent ', $_REQUEST['ua']); adbb_debug_log('With X-Forwarded-For ', $_REQUEST['remote_addr']); adbb_debug_log('With Cookie ', $domain_cookies); $result = adbb_call_url( $url_to_call, '', $_REQUEST['ua'], array( 'X-Forwarded-For' => $_REQUEST['remote_addr'], 'Cookie' => $domain_cookies ) ); if ($result) { adbb_debug_log('Call successfull.'); $info = $result['info']; $header = $result['header']; $content = $result['content']; adbb_debug_log('Remote response info', $info); adbb_debug_log('Remote response headers', $info); $parse = parse_url($url); $domain = $parse['host']; if (array_key_exists('Set-Cookie', $header[count($header)-1])) { adbb_debug_log('Found Set-Cookie in response, taking last one'); $domain_set_cookies = $header[count($header)-1]['Set-Cookie']; $domain_cookies = adbb_translate_cookie_values($domain_set_cookies); adbb_debug_log('Translated domain cookies', $domain_cookies); $json_encoded_cookies = json_encode($domain_cookies); adbb_debug_log('Json encoded cookies', $json_encoded_cookies); $encrypted_cookies_domain = adbb_encrypt( $domain, $CONF['encrypt'] ) ; $encrypted_cookies_values = adbb_encrypt( $json_encoded_cookies, $CONF['encrypt'] ) ; adbb_debug_log('About to set encrypted cookie for domain ['.$domain.'] as ['.$encrypted_cookies_domain.'] with value ', $encrypted_cookies_values); setcookie($encrypted_cookies_domain, $encrypted_cookies_values, time()+$CONF['cookie']['expire']); } if (strpos($info['content_type'], 'text') === false and strpos($info['content_type'], 'javascript') === false ) { adbb_debug_log('Non-text content type '.$info['content_type'].', passing as is. Cache headers not implemented'); header('Content-Type: '.$info['content_type']); echo $content; } else { adbb_debug_log('Initial content in remote response', $content); $new_content = adbb_mask_urls($content, $CONF['mask_urls'], $CONF['encrypt']); echo $new_content; } } 




adbb_functions.php
 <?php function adbb_encrypt($data, $encrypt_conf) { $iv = $encrypt_conf['iv']; return rtrim(strtr( base64_encode(mcrypt_encrypt($encrypt_conf['cipher'], $encrypt_conf['key'], $data, $encrypt_conf['mode'],$iv)), '+/', '-_'), '='); } function adbb_decrypt($data, $encrypt_conf) { $encrypted = base64_decode(str_pad(strtr( $data, '-_', '+/'), strlen( $data ) % 4, '=', STR_PAD_RIGHT)); return mcrypt_decrypt( $encrypt_conf['cipher'], $encrypt_conf['key'], $encrypted, $encrypt_conf['mode'], $encrypt_conf['iv'] ); } function adbb_translate_cookie_values($set_cookies) { $keys = array(); foreach ($set_cookies as $sc) { if ($pos = strpos($sc, ';')) { $keys[] = substr($sc, 0, $pos); } else { $keys[] = $sc; } } return $keys; } function adbb_get_headers_from_curl_response($headerContent) { $headers = array(); // Split the string on every "double" new line. $arrRequests = explode("\r\n\r\n", $headerContent); // Loop of response headers. The "count() -1" is to //avoid an empty row for the extra line break before the body of the response. for ($index = 0; $index < count($arrRequests) -1; $index++) { foreach (explode("\r\n", $arrRequests[$index]) as $i => $line) { if ($i === 0) $headers[$index]['http_code'] = $line; else { list ($key, $value) = explode(': ', $line); if (array_key_exists($key, $headers[$index])) { if (is_array($headers[$index][$key])) { $headers[$index][$key][] = $value; } else { $t = $headers[$index][$key]; $headers[$index][$key] = array( $t ); $headers[$index][$key][] = $value; } } else { $headers[$index][$key] = $value; } } } } return $headers; } function adbb_call_url($url, $cookie, $ua, $headers) { $ch = curl_init(); $curlopt_headers = array(); foreach ($headers as $k => $v) $curlopt_headers[] = $k.': '.$v; $options = array( CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => $ua, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 5, CURLOPT_MAXREDIRS => 5, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_COOKIE => $cookie, CURLOPT_HTTPHEADER => $curlopt_headers, CURLOPT_VERBOSE => 1, CURLOPT_HEADER => 1, ); curl_setopt_array($ch, $options); $response = curl_exec($ch); if (!$response) return false; $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = adbb_get_headers_from_curl_response(substr($response, 0, $header_size)); $content = substr($response, $header_size); $info = curl_getinfo($ch); return array( 'header' => $header, 'content' => $content, 'info' => $info ); } function adbb_mask_urls($content, $mask_conf, $encrypt_conf) { $prefix = $mask_conf['prefix']; $url_search_patterns = $mask_conf['url_search_patterns']; foreach ($url_search_patterns as $pattern) { $matches = array(); if (preg_match_all($pattern, $content, $matches)) { adbb_debug_log('Url matches ',$matches); foreach ($matches[0] as $m) { $encrypted_url = adbb_encrypt($m, $encrypt_conf); $content = str_replace($m, $prefix.$encrypted_url, $content); } } } return $content; } function adbb_debug_log($message, $obj = false) { // $s = $message.($obj ? ' ( '.(is_string($obj) ? $obj : var_export($obj,true) ) . ' )' : '' )."\n"; // static $f = null; // if (!$f and $f = @fopen("/tmp/adbb_tmp_log".date("Ymd"),"a+")) { // fputs($f, "== New call =="); // } // if ($f) { // fputs($f, $s); // } //echo $s ; } 




To get the primary login link itself, run with the same configuration.



 adbb_encrypt('http://ads.XXX.ru/XXXX/prepareCode?pp=g&ps=bugf&p2', $CONF['encrypt']); 




Thanks for attention.

Source: https://habr.com/ru/post/267911/



All Articles