Пример распознавания каптчи форума IPB2.0.x,2.1.х

Wednesday, December 5, 2007 22:44

Пример распознавания каптчи форума IPB2.0.x,2.1.х :

В этой статье я бы хотел продемонстрировать один достаточно распространенный способ взлома каптчи – банальное сопоставление по шаблону. В качестве примера я взял каптчу форумной движки IPB (русская поддержка – Для более подробной информации обратитесь к http://captcha.ru/ . На этом сайте можно найти информацию о различных методах взлома и построения подобного теста Тьюринга.

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

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

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

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

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

Набираете install GD и ждете.
Подробнее можно прочитать здесь.

Если у вас стоит *nix то сами разберетесь :P


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

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

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

#!perl -wuse 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] = ;
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 = "";

Первые несколько строк, надеюсь, объяснения не требуют? :)
Здесь интересен массив @correct в нем записаны расстояния на которые нам надо сдвигать координаты в изображении при сравнении с шаблоном (цифры то не плавают и ловить их нет надобности,спасибо создателям форума IPB :P ), далее станет яснее про что я. Ну а $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 запишется результирующая строка содержащая число на картинке кода защиты.

Архив (сам скрипт, шаблоны к нему и несколько каптч для теста):


Мнения? :)(с)Psixo

Комментарии (4)

  1. HagFruff

    6 December 2007 в 4:40 am

    Привет всем, млин тут комп не давно сломался и остался я без компа и инета аж на целых 5 дней! Епт такой тошняк был пока не починил, ведь есть все таки зависимость от компьютора как ни крути. Помню раньше не было и не надо )). А еще игры это вообще жесть затягивает. Не давно видел объявление на ряду с лечением табакокурения, алкогализма, в третей строчки было лечение от ИГРОМАНИИ. Во как! Докатились!
    Зависимость или свобода конечно все зависит от нас.

  2. Ким

    12 December 2007 в 6:16 pm

    Спасибо за статью, надо будет попробывать.

  3. News alter

    29 February 2008 в 7:22 pm

    Пишут, что уже и до более продвинутых captcha дошли руки у взломщиков.

  4. Agopalopy

    2 October 2008 в 11:20 am

    ривет сцуки драные!
    Порнушко там и тут ищи в гугле :) ихихи мудачек наебали тебя!

Оставить комментарий

Вы можете оставить комментарий или трекбэк с вашего сайта.