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