Пример распознавания каптчи форума IPB2.0.x,2.1.х
Новая тема Написать ответ
# Ср Dec 05, 2007 9:44 pmБерия Зарег.: 20.11.2007 ; Сообщ.: 27Ответить с цитатой
Автор: Psixo
урл:[url=http://blog.secnull.cn/пример-распознавания-каптчи-форума-ipb20x2/]blog.secnull.cn[/url]

Пример распознавания каптчи форума IPB2.0.x,2.1.х :
В этой статье я бы хотел продемонстрировать один достаточно распространенный способ взлома каптчи - банальное сопоставление по шаблону. В качестве примера я взял каптчу форумной движки IPB (русская поддержка - http://www.ibresource.ru ) . Cтатья не ставить своей целью рассказать о всех тонкостях создания каптч или их взлома, она просто демонстрирует самый прямой и порой эффективный путь «взлома» подобной защиты.
Для более подробной информации обратитесь к http://captcha.ru/ . На этом сайте можно найти информацию о различных методах взлома и построения подобного теста Тьюринга.

Для хорошего понимания дальнейшего хода рассуждений и предлагаемых примеров необходимо знание основ языка Perl и желательно PHP.

Введение и немного про инструментарий:
Прежде чем приступать к объяснению необходимо сделать пару уточнений. Демонстрироваться будет процесс распознавания каптчи для ветки форума IPB2.0.x,2.1.х созданной с использование библиотеки GD2.



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

Для работы с предлагаемым примером требуется чтобы на вашей машине был установлен Perl и модуль для работы с GD. Подробней об использовании данного модуля в Perl вы можите прочесть здесь http://www.providerz.ru/articles/perl/perl...-gd-padala.html

Процесс установки в Windows+ActivePerl занимает пару минут.
Достаточно набрать в командной строке `ppm` после этого появится приглашение, вида:

Код:
C:\>ppm
PPM interactive shell (2.1.6) - type 'help' for available commands.
PPM>



Набираете install GD и ждете.
Подробнее можно прочитать здесь.
http://xpoint.ru/forums/programming/perl/m...read/2846.xhtml
Если у вас стоит *nix то сами разберетесь Razz


Описание алгоритма:

Чтобы излишни не загромождать изложение приведу цитату с http://captcha.ru/breakings/ipb/ о жертве этого эксперимента
«Из-за фиксированного шрифта, постоянного расположения символов, большого контраста фона и текста алгоритм уязвим к банальному сравнению символов с эталоном.
Алгоритм распознавания прост:
Выбираем из нужных знакомест пикселы, цвет которых темнее определенной границы (из-за того, что для компрессии используется алгоритм JPEG, искажающий цвета, некоторые изначально черные пиксели могут быть светлее) и сравниваем с соответствущим пикселом каждой из цифр эталонного шрифта (растянутый в высоту и ширину стандартный шрифт №5 из PHP). У какой из цифр больше совпадений — та и есть искомая.»
Остальное – от лукавого. В этих 10 строках описан весь прцесс взлома данной каптчи, кроме того, примерно по аналогичному сценарию распознаются еще очень многие разновидности каптч (сделаные кривыми руками), в некоторых изображение не столь контрастно и сначало необходимо добавит фильтр шумов, в других символы не так строго расположены и требуется сначало вычислить их расположение на изображении и т.д. и т.п. ^-^.


Скрипт:
Здесь я прокомментирую лишь основные моменты работы скрипта.

Код:
#!perl -w


use strict;
use GD;


#################################################################################################
#Функция распознования каптчи в IPB2.x.x использующей GD2
#################################################################################################
sub IPB2_Captch2
   {
    my ($example_pic, $template_pic) = @_;

  #Открываем изображение которое необходимо распознать
  my $main_img = newFromJpeg GD::Image($$example_pic) || return 0;

  my @correct = (0, 27, 54, 81, 108, 135);
  my $result = "";

  for(my $k = 0; $k < 6; $k++)
         {
           my $num_index = 0;
       my @ident_mas = (0,0,0,0,0,0,0,0,0,0);
   foreach  my $template ( @$template_pic )
           {
    my $tmp_image = newFromJpeg GD::Image( $template ) || next;

     for(my $i = 25; $i < 40; $i++)
        {
       for(my $j = 0; $j < 63; $j++)
         {
              #Извлекаем цвет пискселя для шаблона и распозноваемого изображения
            my $pixel = $main_img->getPixel($i+$correct[$k], $j);
            my ($r1, $g1, $b1) = $main_img->rgb($pixel);

            $pixel = $tmp_image->getPixel($i, $j);
            my ($r2, $g2, $b2) = $tmp_image->rgb($pixel);

            if( ($r1 < 35) && ($r2 < 35) &&
           ($g1 < 35) && ($g2 < 35) &&
           ($b1 < 35) && ($b2 < 35)    )
           {$ident_mas[$num_index]++;}
         }
      }
      $num_index++;
            }

         my $max = $ident_mas[0];
         my $max_id = 0;
         for(my $k2 = 1; $k2 < 10; $k2++)
           {
            if( $max < $ident_mas[$k2] )
            {$max = $ident_mas[$k2]; $max_id = $k2;}
           }
         $result .= $max_id;
        }

  return $result;
   }

#########################################################################
#########################################################################
my @template_ipb_2_x_2 = ("./template/PHP5/t0.jpg",
    "./template/PHP5/t1.jpg",
    "./template/PHP5/t2.jpg",
    "./template/PHP5/t3.jpg",
    "./template/PHP5/t4.jpg",
    "./template/PHP5/t5.jpg",
    "./template/PHP5/t6.jpg",
    "./template/PHP5/t7.jpg",
    "./template/PHP5/t8.jpg",
    "./template/PHP5/t9.jpg",
   );

if( !$ARGV[0] )
   {
       print "Captcha file: ";
       $ARGV[0] = <STDIN>;
       chop($ARGV[0]);
      };
my $captcha = $ARGV[0];
my $result =  IPB2_Captch2(\$captcha, \@template_ipb_2_x_2);
print "Captcha string: $result";



Первое на что следует обратить внимание - это массив @template_ipb_2_x_2 со списком файлов шаблонов, думаю понятно что в файле t0.jpg у нас будет хранится шаблон цифры 0,остальные цифры аналогично. Я не стал генерировать шаблоны в ходе выполнения скрипта, а предпочел более простой способ - удалил у себя, в тестовом форуме, код отвечающий за шумы в файле sources/ipsclass.php сгенерировал с его помощью необходимые мне цифры-шаблоны, на них можно взглянуть в прилагаемом к статье архиве.
Код генерации шума из ipclass.php :
Код:


  for ( $i = 1; $i <= $circles; $i++ )
  {
     $values = array(
        0  => rand(0, $tmp_x - 10),
        1  => rand(0, $tmp_y - 3),
        2  => rand(0, $tmp_x - 10),
        3  => rand(0, $tmp_y - 3),
        4  => rand(0, $tmp_x - 10),
        5  => rand(0, $tmp_y - 3),
        6  => rand(0, $tmp_x - 10),
        7  => rand(0, $tmp_y - 3),
        8  => rand(0, $tmp_x - 10),
        9  => rand(0, $tmp_y - 3),
        10 => rand(0, $tmp_x - 10),
        11 => rand(0, $tmp_y - 3),
        );
   
     $randomcolor = imagecolorallocate( $tmp, rand(100,255), rand(100,255),rand(100,255) );
     imagefilledpolygon($tmp, $values, 6, $randomcolor );
  }

  imagestring($tmp, 5, 0, 2, $content, $black);
 
  //-----------------------------------------
  // Distort by resizing
  //-----------------------------------------
 
  imagecopyresized($im, $tmp, 0, 0, 0, 0, $image_x, $image_y, $tmp_x, $tmp_y);
 
  imagedestroy($tmp);
 
  $white   = ImageColorAllocate($im, 255, 255, 255);
  $black   = ImageColorAllocate($im, 0, 0, 0);
  $grey   = ImageColorAllocate($im, 100, 100, 100 );
 
  $random_pixels = $image_x * $image_y / 10;
    
  for ($i = 0; $i < $random_pixels; $i++)
  {
     ImageSetPixel($im, rand(0, $image_x), rand(0, $image_y), $black);
  }
 
  $no_x_lines = ($image_x - 1) / 5;
 
  for ( $i = 0; $i <= $no_x_lines; $i++ )
  {
     // X lines
    
     ImageLine( $im, $i * $no_x_lines, 0, $i * $no_x_lines, $image_y, $grey );
    
     // Diag lines
    
     ImageLine( $im, $i * $no_x_lines, 0, ($i * $no_x_lines)+$no_x_lines, $image_y, $grey );
  }
 
  $no_y_lines = ($image_y - 1) / 5;
 
  for ( $i = 0; $i <= $no_y_lines; $i++ )
  {
     ImageLine( $im, 0, $i * $no_y_lines, $image_x, $i * $no_y_lines, $grey );
  }


Возможно я что-то пропустил, т.к. нет возможности сейчас проверить это, нет под рукой форума, по-моему это весь код генерации шумов, да не важно.
Внешний вид шаблона цифры:


Далее в скрипте идет просто принятие адреса распознаваемого изображения и вызов функции в которой и происходят основные действия, постараюсь кратко описать её работу.


Код:
 
   my @correct = (0, 27, 54, 81, 108, 135);
   my $result = "";


Первые несколько строк, надеюсь, объяснения не требуют? Smile
Здесь интересен массив @correct в нем записаны расстояния на которые нам надо сдвигать координаты в изображении при сравнении с шаблоном (цифры то не плавают и ловить их нет надобности,спасибо создателям форума IPB Razz ), далее станет яснее про что я. Ну а $result это и будет наша переменная в которую запишется результат распознавания.

Код:
my @ident_mas = (0,0,0,0,0,0,0,0,0,0);


В цикле для каждой из 6 цифр мы определяем кол-во совпавших пикселов со всеми 10-ью шаблонами(т.е. с цифрами 0..9). Т.е. в данном массиве будет храниться сколько было совпадений у данной цифры каптчи с тем или иным шаблоном, самое максимальное число совпавших пикселов - это и будет означать что мы нашли нужное число.

Код:
for(my $i = 25; $i < 40; $i++)
            {
             for(my $j = 0; $j < 63; $j++)


Думаю понятно что это запуск цикла в котором меняются координаты пикселей на сравнение.

Код:
my $pixel = $main_img->getPixel($i+$correct[$k], $j);


Вот собственно где нам и понадобился массив со сдвигом координат для того, чтобы переходить к следующей цифре на каптче.

Код:
if( ($r1 < 35) && ($r2 < 35) &&
                     ($g1 < 35) && ($g2 < 35) &&
                     ($b1 < 35) && ($b2 < 35)    )
                     {$ident_mas[$num_index]++;}


В этом условии мы проводим элементарную проверку отсеивающую шум, на картинках много различных "лишних" линий и цветных фигур, этим же условием мы оставляем только черные пикселы (условно черные, за черный цвет мы принимаем пикселы со значением RGB составляющих меньше 35) шаблона и каптчи, если условие верно, то пикселы совпали - увеличиваем кол-во совпадений для текущего шаблона.

Код:
my $max = $ident_mas[0];
          my $max_id = 0;
          for(my $k2 = 1; $k2 < 10; $k2++)
            {
             if( $max < $ident_mas[$k2] )
                {$max = $ident_mas[$k2]; $max_id = $k2;}
            }
          $result .= $max_id;


Поиск максимума в совпадении пикселе - лол. ну в общем я думаю ясно, берем максимум совпадений, смотрим какой цифре-шаблону он соответствует и присоединяем к результату.


В итоге мы так пройдем по всем 6 числам на каптчи и в $result запишется результирующая строка содержащая число на картинке кода защиты.

Архив (сам скрипт, шаблоны к нему и несколько каптч для теста): http://rapidshare.com/files/12724840/captch.zip.html
http://imort.fatal.ru/files/my/captcha_read.zip
http://imort.fatal.ru/files/my/captcha_read.rar
Мнения? Smile
Новая тема Написать ответ    ГЛАВНАЯ ~ ПОЛЕЗНЫЕ СТАТЬИ
 
Любое использование материалов, размещенных на ArmadaBoard.com, без разрешения владельцев ArmadaBoard.com запрещено.