Защита от ботов, основанная на различии в работе с большими числами в JavaScript и PHP

Недавно мне пришлось разбираться с защитой от ботов, используемой на нескольких довольно популярных ресурсах.
На первый взгляд защита показалась обычной установкой куки через javascript, справиться с которой — дело 15-ти минут. В самом деле, после небольшого исследования стало понятно где что делается и какие параметры куда передаются, остается только переписать небольшую функцию с javascript на php и дело в шляпе.
Но все оказалось не так просто. И хотя в итоге защита была сломана, на это потребовалось далеко не 15 минут, и сам принцип защиты оказался для меня новым и довольно интересным.

Итак, обо всем по порядку.

Поверхностный осмотр


Защита работает следующим образом.
Скрипт главной страницы сайта index.php ожидает куку, в которой одним из параметров будет указан хеш, вычисленный из IP-адреса посетителя.
Если кука не передается, то index.php перенаправляет посетителя на другую страницу, содержащую javascript код, который вычисляет необходимый параметр, записывает его в куку и возвращает нас обратно на главную страницу.

Чтобы обычный php-бот, выполняющий GET и POST запросы через CURL, смог проходить через такую защиту, нужно переписать вычисление хеша с javascript на php и затем дописывать в заголовок запроса нужную куку.

Вскрытие


Теперь подробнее.
Запускаем Firefox, отключаем javascript и включаем Firebug.
Запрашиваем главную страницу index.php и смотрим заголовки запроса и ответа.

Запрос:

GET
http://example.com


Заголовки этого запроса не представляют для нас интереса.
А вот заголовки ответа:

Status: 302 Moved Temporarily

Connection keep-alive
Content-Type text/html
Date XXX GMT
Location
http://example.com/govalidateyourself#98765:1234:11.22.33.44:/index.php

Server YTS/1.20.0
Transfer-Encoding chunked


После чего Firefox автоматически переходит на указанный в заголовке Location, получая следующий заголовок ответа:

Accept-Ranges bytes
Connection keep-alive
Content-Type text/html; charset=utf-8
Date XXX GMT
Last-Modified YYY GMT
Server YTS/1.20.0
Set-Cookie addr=1234:11.22.33.44; path=/
Transfer-Encoding chunked


Где 11.22.33.44 — мой IP-адрес, 1234 — какое-то число, логика вычисления которого неизвестна.

Сама страница содержит ссылку на js-код
http://example2.com/validator/va.js
и надпись «No javascript».
Без js нас дальше не пустят.

После того как все запросы-ответы записаны, включаем javascript, очищаем cookie и делаем все заново.
Сейчас нас интересует то, что будет происходить после запроса страницы валидации.

На этот раз загружается главная страница сайта, и вот заголовок последнего запроса:

Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Connection keep-alive
Cookie addr=5678:11.22.33.44; urine=aabbccdd; v=1
Host example.com
Referer
http://example.com/govalidateyourself

User-Agent какой-то Firefox


Константа 1234 из прошлого ответа сервера в этот раз изменилась на 5678, IP-адрес остался тем же. Судя по всему это ID запроса, присваиваемый сервером и хранящийся в cookie. Ну что ж, его надо сохранить и просто записывать в куки в неизменном виде во время запросов.

А вот параметр urine=aabbccdd — это уже интересно. Раз он не приходил от сервера — значит он был получен у нас, и что-то подсказывает мне что это дело рук va.js.

Самое время посмотреть что там внутри. На первый взгляд полное болото, в которое лучше не влезать:

if(document.cookie==""){document.write("Cookies error")}else{function poo(a,b){var c=a.length,d=b^c,e=0,f;while(c>=4){f=a.charCodeAt(e)&255|(a.charCodeAt(++e)&255)<<8|(a.charCodeAt(++e)&255)<<16|(a.charCodeAt(++e)&255)<<24;f=(f&65535)*1540483477+(((f>>>16)*1540483477&65535)<<16);f^=f>>>24;f=(f&65535)*1540483477+(((f>>>16)*1540483477&65535)<<16);d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16)^f;c-=4;++e}switch(c){case 3:d^=(a.charCodeAt(e+2)&255)<<16;case 2:d^=(a.charCodeAt(e+1)&255)<<8;case 1:d^=a.charCodeAt(e)&255;d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16)}d^=d>>>13;d=(d&65535)*1540483477+(((d>>>16)*1540483477&65535)<<16);d^=d>>>15;return d>>>0}function coo(a){var b=a+"=";var c=document.cookie.split(";");for(var d=0;d<c.length;d++){var e=c[d];while(e.charAt(0)==" ")e=e.substring(1,e.length);if(e.indexOf(b)==0)return e.substring(b.length,e.length)}return null}var dt=new
Date,expiryTime=dt.setTime(dt.getTime()+1000e5);var dt2=new
Date,expiryTime=dt2.setTime(dt2.getTime()+2e4);var addr=window.location.hash.split(":")[2];var a=poo(addr,47).toString(16);for(var i=0,z="";i<8-a.length;i++)z+="0";a=z+a;a=a.substring(6)+a.substring(4,6)+a.substring(2,4)+a.substring(0,2);var refurl=window.location.hash.split(":")[3];document.cookie="urine="+a+"; expires="+dt.toGMTString()+"; path=/";if(!coo("v")){document.cookie="v=1; expires="+dt2.toGMTString()+"; path=/";setTimeout("window.location = refurl",300)}else if(coo("v")<3){var c=coo("v");c++;document.cookie="v="+c+"; expires="+dt2.toGMTString()+"; path=/";setTimeout("window.location = refurl",300)}else if(coo("v")>=3){document.write("Too many redirects from: "+document.referrer)}}


Но немного терпения, и после форматирования все выглядит читабельно и довольно понятно.
Есть две функции coo() и poo(), и код который пишет нужную нам куку и отправляет обратно на index.php.

Функция сoo() не представляет особого интереса, она получает значение указанного параметра из куки, и легко переписывается на php простым регулярным выражением.

А вот функция poo(), которая считает параметр urine:


function poo( a, b )
{
   var c = a.length, d = b^c, e = 0, f;
		
   while( c >= 4 )
   {
      f = a.charCodeAt( e ) & 255 | ( a.charCodeAt( ++e ) & 255 ) << 8 | ( a.charCodeAt( ++e ) & 255 ) << 16 | ( a.charCodeAt( ++e ) & 255 ) << 24;
		
      f = ( f & 65535 ) * 1540483477 + ( ( ( f >>> 16 ) * 1540483477 & 65535 ) << 16 );
      f ^= f >>> 24;
			
      f = ( f & 65535 ) * 1540483477 + ( ( ( f >>> 16 ) * 1540483477 & 65535 ) << 16 );
			
      d = ( d & 65535 ) * 1540483477 + ( ( ( d >>> 16 ) * 1540483477 & 65535 ) << 16 )^f;
			
      c -= 4;
			
      ++e
   }
			
   switch( c )
   {
      case 3:
         d ^= ( a.charCodeAt( e + 2 ) & 255 ) << 16;
			
      case 2:
         d ^= ( a.charCodeAt( e + 1 ) & 255 ) << 8;
			
      case 1:
         d ^= a.charCodeAt( e ) & 255;
			
      d = ( d & 65535 ) * 1540483477 + ( ( ( d >>> 16 ) * 1540483477 & 65535 ) << 16 )
   }
		
   d ^= d >>> 13;
		
   d = ( d & 65535 ) * 1540483477 + ( ( ( d >>> 16 ) * 1540483477 & 65535 ) << 16 );
   d ^= d >>> 15;
		
   return d >>> 0
}



Во время вызова ей передаются такие параметры:

var a = poo( addr, 47 ).toString( 16 );


a — это и есть уже готовое значение параметра urine (дальше оно только дополняется нулями если содержит меньше 8 символов).
addr — наш IP-адрес 11.22.33.44.
47 — константа.

Теперь все выглядит понятно.
php-бот, пробивающий эту защиту, должен работать по следующему алгоритму.

1. Делаем GET-запрос
http://example.com/index.php
Cтавим опцию получать заголовки ответа:

curl_setopt( $ch, CURLOPT_HEADER, 1 );


И заодно включаем автоматический переход в случае редиректа:

curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );


В этом случае curl сам выполнит переход на новый location, и нам нет нужды программировать второй запрос. И мы получим заголовки обоих ответов, в первом заголовке будет Location, во втором — первая кука, содержащая ID запроса.

2. Парсим заголовки, получаем ID запроса и свой IP-адрес (если мы используем разные трюки то мы можем его сразу и не знать, а здесь его нам любезно подсказывают — очень удобно).
Считаем параметр urine, записываем в куку и отправляем новый GET-запрос на index.php. Защита пройдена.

Кука прописывается так:

$headers = array(
   "Cookie: " . $cookie_str, // "addr=5678:11.22.33.44; urine=aabbccdd; v=1"
   /* другие заголовки по желанию/необходимости */
);

curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );


Итак, остался последний штрих — вычисление urine.

Грабли


Нужно просто переписать функцию poo() на php.
Для начала немного гуглим и пишем аналоги для пары js-функций и операторов, которых нет в php:

// php js functions
function charCodeAt( $str, $i )
{
   return ord( substr( $str, $i, 1 ) );
}

// char at
function charAt( $str, $i )
{
   return $str{ $i };
}

//unsigned shift right (js >>>)
function zeroFill( $a, $b )
{ 
   $z = hexdec( 80000000 ); 
		
   if( $z & $a ) 
   { 
      $a = ( $a >> 1 ); 
      $a &= ( ~ $z ); 
      $a |= 0x40000000; 
      $a = ( $a >> ( $b - 1 ) ); 
   } 
   else 
   { 
      $a = ( $a >> $b ); 
   } 
		
   return $a; 
} 


Теперь все готово, и можно переписать poo():

//
function poo( $a, $b )
{
   $c = strlen( $a );
   $d = $b ^ $c;
   $e = 0;
   $f = '';
		
   while( $c >= 4 )
   {
      $f = charCodeAt( $a, $e ) & 255 | ( charCodeAt( $a, ++$e ) & 255 ) << 8 |
         ( charCodeAt( $a, ++$e ) & 255 ) << 16 | ( charCodeAt( $a, ++$e ) & 255 ) << 24;
		
      $f = ( $f & 65535 ) * 1540483477 + ( ( ( zeroFill( $f, 16 ) ) * 1540483477 & 65535 ) << 16 );
      $f ^= zeroFill( $f, 24 );
			
      $f = ( $f & 65535 ) * 1540483477 + ( ( ( zeroFill( $f, 16 ) ) * 1540483477 & 65535 ) << 16 );
			
      $d = ( $d & 65535 ) * 1540483477 + ( ( ( zeroFill( $d, 16 ) ) * 1540483477 & 65535 ) << 16 )^$f;
			
      $c -= 4;
			
      ++$e;
   }
			
   switch( $c )
   {
      case 3:
         $d ^= ( charCodeAt( $a, $e + 2 ) & 255 ) << 16;
			
      case 2:
         $d ^= ( charCodeAt( $a, $e + 1 ) & 255 ) << 8;
			
      case 1:
         $d ^= charCodeAt( $a, $e ) & 255;
			
      $d = ( $d & 65535 ) * 1540483477 + ( ( ( zeroFill( $d, 16 ) ) * 1540483477 & 65535 ) << 16 );
   }
		
   $d ^= zeroFill( $d, 13 );
		
   $d = ( $d & 65535 ) * 1540483477 + ( ( ( zeroFill( $d, 16 ) ) * 1540483477 & 65535 ) << 16 );
   $d ^= zeroFill( $d, 15 );
		
   return zeroFill( $d, 0 );
}


Сохраняем, запускаем и обламываемся — результаты js и php версий не совпадают.
В чем дело?
Добавляем код в js и php для вывода результата после каждой строки вычислений и смотрим в чем дело.

Оказывается простые арифметические операторы php в отличие от javascript плохо умеют работать с большими числами.

Например выражение

( 18220025198660 & 65535 ) * 1540483477 + ( ( ( 18220025198660 >>> 16 ) * 1540483477 & 65535 ) << 16 );


в javascript будет равно 22188624159636, а аналогичное в php

( 18220025198660 & 65535 ) * 1540483477 + ( ( ( zeroFill( 18220025198660, 16 ) ) * 1540483477 & 65535 ) << 16 )


будет равно немного другому числу 22188624159600

Когда несколько подобных формул вычисляются подряд то ошибка накапливается, давая в итоге совсем другой результат. В некоторых выражениях php по умолчанию предполагает что результат является типом int и ограничивает максимальное значение до 4 млрд (на 32-х разрядных системах).

Похожие проблемы с большими числами есть и у Perl.

Для точных вычислений в php необходимо использовать функции библиотеки BC Math. Вместе с этим нужно добавить приведение к типу float.

В результате проб и ошибок получаем код, дающий те же результаты что и javascript. Но это требует дополнительных времени и усилий.
Код не самый оптимальный, для большей ясности вычисления выполняются по шагам.

//
function poo( $a, $b )
{
   $c = strlen( $a );
   $d = $b ^ $c;
   $e = 0;
   $f = '';
		
   while( $c >= 4 )
   {
      $f = charCodeAt( $a, $e ) & 255 | ( charCodeAt( $a, ++$e ) & 255 ) << 8 |
         ( charCodeAt( $a, ++$e ) & 255 ) << 16 | ( charCodeAt( $a, ++$e ) & 255 ) << 24;

      $f = bcadd( bcmul( $f & 65535, 1540483477 ), ( floatval( ( bcmul( ( zeroFill( $f, 16 ) ), ( 1540483477 & 65535 ) ) ) ) << 16 ) );

      $xx = zeroFill( $f, 24 );

      $f = floatval( $f ) ^ floatval( $xx );

      //
      $f = floatval( $f );
			
      $f1 = bcmul( $f & 65535, 1540483477 );
      $f2 = ( floatval( ( bcmul( ( zeroFill( $f, 16 ) ), ( 1540483477 & 65535 ) ) ) ) << 16 );

      $f = bcadd( $f1, $f2 );

      $d1 = bcmul( $d & 65535, 1540483477 );
      $d2 = ( floatval( ( bcmul( ( zeroFill( $d, 16 ) ), ( 1540483477 & 65535 ) ) ) ) << 16 );

      $d = bcadd( $d1, $d2 );
      $d = floatval( $d ) ^ floatval( $f );

      $c -= 4;
			
      ++$e;
   }

   switch( $c )
   {
      case 3:
         $d = floatval( $d ) ^ ( ( charCodeAt( $a, $e + 2 ) & 255 ) << 16 );
			
      case 2:
         $d = floatval( $d ) ^ ( ( charCodeAt( $a, $e + 1 ) & 255 ) << 8 );
			
      case 1:
         $d = floatval( $d ) ^ ( charCodeAt( $a, $e ) & 255 );
			
         $d1 = bcmul( $d & 65535, 1540483477 );
         $d2 = ( floatval( ( bcmul( ( zeroFill( $d, 16 ) ), ( 1540483477 & 65535 ) ) ) ) << 16 );

         $d = bcadd( $d1, $d2 );
   }

   $d = floatval( $d ) ^ zeroFill( $d, 13 );
		
   $d1 = bcmul( floatval( floatval( $d ) & 65535 ), 1540483477 );
   $dd21 = zeroFill( $d, 16 );
   $dd22 = floatval( bcmul( $dd21, 1540483477 & 65535 ) );
   $dd23 = floatval( $dd22 << 16 );
   $d2 = $dd23;

   $d = bcadd( $d1, $d2 );

   $d = floatval( $d ) ^ zeroFill( $d, 15 );

   if( $d < 0 )
   {
      $res = bindec( decbin( ~0 ) ) - abs( $d ) + 1;
   }
   else
   {
      $res = $d;
   }

   return $res;
}


И для функции zeroFill() добавляем в самое начало:

$a = floatval( $a );


Заключение


Мои боты свое дело сделали, а вы можете использовать описанную здесь защиту в своих целях. Если ее модифицировать, например динамически менять делающий вычисления код, то подобный взлом станет еще более трудной задачей. И если за вас никто не захочет взяться всерьез то этой защиты будет достаточно.

А вообще, лучшая защита от ботов — это капча. Даже самый хитрый javascript может быть выполнен ботами, использующими что-нибудь типа Perl-модуля Mechanize.
Поделиться публикацией

Похожие публикации

Комментарии 44

    +20
    node.js решил бы вашу проблему за пару минут =)
      –1
      Верно, но в моем случае проще было заморочиться на php чем переписывать все остальное.
        +3
        проще было взять питон:
        >>> print ( 18220025198660 & 65535 ) * 1540483477 + ( ( ( 18220025198660 >> 16 ) * 1540483477 & 65535 ) << 16 )
        22192919126932
          +4
          Думаю вам не предлагали переписать всю на ноду, а просто запустить маленький node-сервер рядом, который бы принимал куку и выдавал ответ.

          А в php коде просто делать GET с кукой на этот сервер.
            +3
            Пожалуй соглашусь, такой вариант лучше.
        +8
        А вообще, лучшая защита от ботов — это капча. Даже самый хитрый javascript может быть выполнен ботами

        А для всего остального (включая капчу) — есть индусы.
          +1
          да, антигейт работает на совесть и совсем недорого.
            +11
            Не всегда в стандартной капче есть смысл. Сейчас ее пихают все кому не лень и куда не лень. В большинстве же случаев, 99,99% стандартных ботов можно отсеять простейшей js ловушкой. В оставшихся же случаях все равно обойдут самописным скриптом или антикапчей.
            К слову большая часть каптч, которые так раздражают большинство, препрекрасно ломаются и без антикапчи. Т.е. смысла в такой защите от ботов — 0 и банальная js заглушка отсеивала бы ботов намного эффективнее.

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

            Допустим, даже если код сломают, то, как ни крути, спам-сервер, который это будет все разгадывать не сможет переварить больше определенного числа каптч в минуту.
            Главное тут свой серверный код придумать так, чтобы не пришлось тех же вычислений каждый раз для клиентской стороны выполнять. Можно придумать что-то на основе алгоритмов закрытого/открытого ключа или сделать ход конем и считать решения для Bitcoin. Хз, правда, как последнее можно приспособить для этих нужд, но теоретически очень даже возможно, а дополнительным бонусом от ботов будет прибавка в виде Btc на счету. :)

            Ух понаписал, хоть садись и реализовывай. :)
              +1
              можно отсеять простейшей js ловушкой

              Приведите пару примеров, если не затруднит.
                0
                А погуглите в сторону JavaScript Captcha, есть множество решений. Хотя лучше конечно что-то свое или хотя бы с минимальными изменениями.

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

                А ловушка может быть самой элементарной, по типу скрытого поля, которое появляется только в процессе генерации на стороне клиента. От залетных спамботов часто выручает.
                Можно кстати издеваться так над ними. Сделать капчу и скрытое js поле, если в POST запросе скрытое js поле не передалось, капчу считаем неверной. пусть долбится и тратит хозяйские деньги на антикапчу. Если авторы забыли сделать ограничение на число попыток, весь баланс быстро подойдет к нолю. :)
                  0
                  Скрытое поле заполняемое яваскриптом (jquery) боты на моих сайтах уже обходят ;(
                  Вот думаю как бы по простому усложнить им жизнь. Разумеется без капчи.
            +8
            Для тех, кто не в курсе — в js нет целых чисел, всё — double-ы. Соответственно, появляется пара лишних знаков в мантиссе :)
              –5
              Смысл поста в том, что вы открыли для себя bc math?..
                +2
                Нет, пост о механизме защиты и о том как ее обходить. bc math только инструмент
                  +1
                  Вы всегда читаете между строк?
                    0
                    Просто человек увидел в тексте знакомый инструмент BC Math и счел своим долгом оставить сей комментарий, даже не прочитав статью. Все мы хотим казаться умными, ну с кем не бывает? ;)
                  +6
                  А на 64-бит системах пробовали?
                    0
                    Можно использовать gecko или webkit в боте. Отправлять запросы через него. :)
                      –2
                      Что за бред вы несёте? =)))
                        +1
                        Почему же бред? Инициализируется библиотека, например gecko, загружается страница с нужной формой, ставятся нужные значения в поля формы и нажимается кнопка сабмита. Javascript выполняется — запрос уходит. Всё автоматизировано.
                          –1
                          Не знаю. Неукладывается подобное в голове моей.
                            0
                            Можно ещё проще(без иксов), есть PECL, считающий яваскрипт на основе куска файрфокса.
                            Есть монгодб, для которого есть нативный PHPшный туннель.
                            Можно интернет эксплорер запустить в конце концов, в виде HTA файла.
                              0
                              http://iopus.com/imacros/

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

                              Спамер использующий этот софт даже не узнает, что тут была какая-то защита :D
                        +3
                        На JS все числа — 64битные double.
                        В PHP такие тоже есть. Достаточно все вычисления проводить в double, применяя, где необходимо, doubleval.
                          0
                          У меня почему-то ваша php-функция zeroFill( 18220025198660, 16 ) возвращает 278015521, а javascript 18220025198660 >>> 16 даёт 11809. В чем может быть дело?
                            –2
                            В zeroFill добавили первой строкой $a = floatval( $a );?
                              0
                              Не помогает, тот же результат.
                                –2
                                Сейчас протестил, следующий код:

                                $k = zeroFill( 18220025198660, 16 );
                                echo «test = '». $k. "'";

                                k = 18220025198660 >>> 16;
                                alert( «test = '» + k + "'" );

                                Оба примера пишут test = '11809'

                                Сейчас тестил на Windows Vista, 32 bit
                                  0
                                  Очень странно. Мне php стабильно выдает 278015521 (тестирую, правда на маке, но какая разница?). Это, кстати, не простое число. Именно его выдает руби как результат `18220025198660 >> 16`. Ничего не понимаю :)
                                    0
                                    кстати, на php можно писать:
                                    echo "test = '$k'";
                              +13
                              coo, poo и urine? Действительно болото.
                                0
                                Как говорится из песни слова не выкинешь
                                Можете спросить у создателя «нескольких довольно популярных ресурсов», почему он решил назвать эти функции именно так
                                  0
                                  Я бы спросил тоже самое у создателей libcaca (хорошая библиотека, кстати): caca.zoy.org.
                                  0
                                  substr( $str, $i, 1 )?
                                  –1
                                  Для обхода подобной защиты проще всего использовать какой-нибудь браузерный движок и к примеру Selenium.
                                    +3
                                    Пусть минусуют. Пост из раздела «демон на php» или «социальная сеть на joomla».
                                    Из своего опыта, использование на сайте javascript против ботов описанным выше способом бесполезно.

                                    Если вы хотите защитить ваш ресурс от антигейта, приходится каждые 2 недели — месяц менять скрипты капчи. Советы:
                                    1. На русскоязычных сайтах используйте русскоязычные капчи. На антигейт приходится всегда повышать цену за капчу — пусть спамеры больше денег потратят. Пропускайте гласные буквы в слове капчи (заставляя пользователя додумывать курс правописания первых классов).
                                    2. Рубите капчу после генерации на две-три картинки, отображая их на странице в правильном порядке. На антигейт два индуса не смогут ввести капчу в один <input />.
                                    3. Высший пилотаж: использовать js для записи действий пользователя. Самый простой пример — передать на сервер координаты клика по капче. По-сложнее, сделать капчу «собери пазл».


                                    Сколько бы мы не придумывали способов защит, у спамеров есть свой софт, который обучается обходить защиты и пользуется общей базой, доступы к которой стоят $10-$30/мес. Поэтому, вашу капчу(скрипт) взломают один раз, и, если вы не будете обновлять принципы ее работы, когда-нибудь очень скоро ваша капча(скрипт) станет бесполезной
                                      0
                                      2. Порезанную капчу можно склеить обратно перед отправкой индусам
                                        0
                                        да, можно. но в базах для программ спам-ботов только список ссылок на форумы, блоги, чаты и пр. Изначально хрумер пытается сам разобраться где что на сайте находится и если он не может распознать капчу сам, отсылает ее в антигейт. Сам хрумер пока такие капчи не распознает и отсылает индусам каждую капчу отдельно, потом тупит
                                      +1
                                      Если ее модифицировать, например динамически менять делающий вычисления код

                                      Ничего особо не измениться.
                                      Кстати, а зачем мучиться с переписыванием функции на пхп, если можно взять js код и подумать как выполнить его в своей программе?
                                        +2
                                        function poo(), var urina;
                                        Кто эти писаки?
                                          0
                                          > на нескольких довольно популярных ресурсах
                                          Назвать эти ресурсы популярными можно лишь с бо-о-о-льшой натяжкой.

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

                                          Самое читаемое