Гуляя по просторам интернета, зашёл на один высокопосещаемый древний сайт рунета. Для того, чтобы скачать файлик с этого сайта, нужно угадать вот такую капчу:

В очередной раз видя картинку с цифрами — решился.
В голове уже давно проносились мысли, сломать какую-нибудь капчу :)
Ставлю себе задачу: Написать скрипт, который будет расшифровывать показанную капчу и выплевывать драгоценные циферки.
Название сайта специально не привожу — сами догадаетесь :)
Итак, поехали!
Анализируем картинку
Для начала надо просмотреть как можно больше таких капч, чтобы выявить сходства/различия, какие-то закономерности. Для этих целей я скачал порядка 50 капч. Среди них можно выбрать основные, которые содержат максимум различий:
Вообще люблю всматриваться в числа, так как в своё время много времени посвятил изучению математики :)
Рассматриваем, и понимаем:
- картинка черно-белая, в формате gif
- размер картинки может меняться, но цифры всегда стоят по центру (правда вертикально они выравнены не очень по центру)
- используется градиент, его направление может меняться в 2 стороны
- кроме градиента есть, "угловой градиент" (так я его обозвал, не пинайте :) ), тот который идёт из угла под углом 45 (ещё раз не пинайте :) ) это просто линия-диагональ, в моём понимании
- всего я выявил 6 разных шрифтов написания (точнее 3, другие 3 являются их наклонными версиями)
- пиксели всех цифр не темнее цвета #606060, но не одного цвета
- цифр 3-5 в капче, высотой не выше 14px
Ищем решение
В голове в течение получаса прокручиваются варианты, понятно одно:
картинку желательно обрезать, и поскольку используются шрифты одни и те же, и они никак не меняются, можно использовать "отпечатки". Под этим термином я понимаю то, что цифры у нас уже где-то лежат в базе, и нам нужно сверять их с картинкой.
Пришёл к такому решению:
- заводим массив с отпечатками
- обрезаем картинку со всех сторон, лишнее надо выбросить
- удаляем лишние цвета — это градиент и угловой градиент
- проходим по всем пикселям слева-направо сверху-вниз, и если цвет пикселя соответствует цвету цифры (>= #606060), то сверяем с отпечатками, со всеми по порядку
Реализация
- Готовим отпечатки
Всего их получается 6 * 10 = 60 штук, их помещаем в массив. Отпечатки я делал по цифрам из капч, для каждого шрифта. Это просто массив строчек, где в каждой строчке буквой "x" отмечен пиксель цифры.
Например, вот так выглядит цифра 2 первого шрифта:

- Открываем картинку
Это делается просто, через imagecreatefromgif($filename);
- Определяем направление градиента
Надо определить, в какую сторону смотрит градиент, это потребуется в следующих пунктах.
Это сделать просто, достаточно определить цвет первого пикселя (0, 0)
$color = imagecolorat($image, 0, 0) < 0x20 ? 'black' : 'white' ;
- Вычищаем угловые градиенты
Здесь нужно почистить угловые линии-градиенты, причём это лучше cделать до обрезания капчи.
Вот тут как раз нам нужно знать направление градиента, чтобы вычистить с нужной стороны.
Путём анализа выявляем, что перепад цвета с пикселя (1, 1) на (2, 2) и т.д. не может быть больше #202020.
Вычистить — это значит закрасить черным цветом, т.к. все цифры у нас не ниже цвета #606060.
Получаем такую картинку:

php-код вы можете просмотреть в аттаче (см. ссылку ниже)
- Режем капчу
На этом этапе отрезаем слева и справа по 12px.
Т.к. высота цифры не выше 14px, то снизу и сверху обрезаем лишнее, в зависимости от высоты всей капчи.
Получаем:

- Чистим градиент
Со всех сторон всё же остаются лишние полоски градиента. Их надо так же вычистить.
Проходим сперва сверху-вниз, потом слева-направо, берём цвет полоски, и если она сплошная (длина > 10px) и одного цвета — то считаем что это полоска градиента, и вычищаем её.
Итого получаем:

Но в некоторых случаях (~ 5%) всё же могут оставаться вот такие шумы:

Правда они нам всё равно не помешают :) Т.к. их цвет уже не подходит под цвет цифр.
- Сверяем с отпечатками
Проходим по всем пикселям сверху-вниз слева-направо, цвет которых подходит под цвет цифр и сверяем со всеми отпечатками по-порядочку.
Результаты
Тестирование
Для тестирования я скачал 200 таких капч, на моём домашнем ПК скрипт разобрал их ~ за
19 секунд.
Это примерно
10 капч в секунду.
Из этих 200 не было выявлено
ни одной ошибки, скрипт отлично отработал :)
Итоги
Я написал класс CapCrack, который разбирает капчу.
Если есть желание более подробно разобраться в алгоритме, или протестировать на своём ПК, можете взглянуть на код:
cap_crack.zip
На этом успехе я не остановился и решил попробовать написать скрипт для скачки файлов с сайта, в автоматическом режиме, но это уже совсем другая история :) достойная отдельной статьи…
P.S.
Это мой первый пост на Хабре, так что прошу строго не судить :)
комментарии (142)
Принципиально не использовать язык Х — просто глупо. Хороший программист должен уметь правильно выбрать инструмент.
Для настоящему программиста нет особой разницы на чем писать — на php, бейсике, c/c++, python, ruby или каком-либо еще языке программирования. Он везде напишет хороший код.
В данном случае автор руководствовался принципом, где быстрее и проще реализовать алгоритм. Да, можно было данный алгоритм реализовать на c++, потратив на это в N раз больше времени — но разве оно стоило того?
Какие красивые слова. А Вы сами-то на многих языках писали идеальный код? Так легко переключаетесь? В любой момент если надо на любом языке из списка «php, бейсике, c/c++, python, ruby» сбацаете любую задачу?
Я не защищаю теорию о том что php плох в этой задаче, это-то как раз не соответствует действительности, я ставлю под сомнение утверждение что «для настоящего программиста нет особой разницы на чем писать» — такое может сказать только человек, который никогда не работал в действительно крупных проектах. Всегда есть языки, которые знаешь лучше, и разработка на них выйдет быстрее и качественнее, чем «на любом». Ещё и про «ничтожность» других судите по своим бредовым убеждениям.
Но прагматик также подумает, сколько времени займёт у него эта разработка. И если использование фреймворка существенно сократит время разработки, то почему бы и нет? Тут уже надо смотреть, какая задача стоит — достаточно или 10 капч в секунду, или надо миллионы обрабатывать.
Я говорил про составление алгоритма, его реализацию и оформление реализации.
Эту схему можно хоть блоксхемами сделать, а спроектировать на конкретный язык программирования — не очень сложная задача.
Как пример, немного утрированный, но можно увелить сложность:
умножение на 5 можно сделать двумя способами
1) a = b*5
2) a = b+b+b+b+b
Какой бы язык программирования не был — хороший программист выберет первый вариант.
Или если критична скорость, а умножение для используемого процессора слишком тяжелая операция по сравнению со сложением — тоже второй.
Или же a=(b<<2)+b если оптимизировать и переменные у нас целые положительные числа.
Хотя, это пережитки эпохи 8086. Сейчас процессор сам в состоянии (на уровне микрокода) понять, как ему эффективнее сделать ту или иную операцию. Это вам не 86-й, где команда MUL/IMUL могла занимать больше сотни тактов.
Куча программистов на похапе говорит лишь о том, что у похапе низкий порог вхождения, поэтому среди похапистов высокая концентрация быдлокодеров. Но если конкретный человек знает пыхпых, то это еще ни о чем не говорит.
>таки да
Вообще: мужик сказал — мужик сделал. Может лучше, докажите, если нет — тихо курите в сторонке.
автору +1 за статью, я до сих пор борюсь с одной каптчой, но пока не победил :(
достали с «доставляет»!
ps: «похапе»? это наречие какое-то? ;)
Если человек что-то написал, то это само по себе уже заслуживает оценки, и если это что-то не «чятик на дельфи», то я не вижу никакой причина ее занижать.
Я когда-то на нем писал решение подобной задачи — распознавание образа в каком-то роде и решал ее похожими средствами :)
Кстати, кто-то выше писал про нейронную сеть — система матриц-«отпечатков», предложенная автором — это одна из реализаций нейронной сети и есть :)
почему же оценка не может быть отрицательной?
Можно было бы написать код и на Ассемблере. Но зачем?
Многие бы стали вникать в суть кода? Если здесь самое интересное — понять алгоритм.
К слову, в своё время писал системные службы на Ассемблере, под Windows. К примеру одна из сетевых служб сейчас уже N-й год работает в одном из интернет-центров города, и подсчитывает трафик.
Теперь к Windows`у придерётесь? :)
Ещё раз повторюст, что целью этого поста было показать как можно обойти типичную капчу, проанализировать алгоритм, а не показать какие-то прелести языка.
Просто каждый язык использую в нужном мне месте.
«php — этим всё сказано» — с вами все ясно.
Так забавно =) Напомнило:
«Я так не люблю грубость и грубых людей, что готов набить им морду».
Да уж, судя по числу минусов, заработанным odinом — на хабре так поступает большинство. Добрее надо быть(
вообще конечно знатный велосипед.
думается gocr даже без препроцессинга бы справился.
Т.к. тема интересна — в следующий раз напишу о взломе капчи посложнее, и на каком-нибудь другом языке.
Там вроде шрифт один и тот же, только наклон разный и шум мешается.
От шумов картинку можно почистить медианной фильтрацией.
Потом бинаризовать с помощью k-средних.
Выделяем объекты, чтобы определить наклон вычисляем наклон главной оси объекта, например, с наименьшим эксцентриситетом. В зависимости от угла сдвигаем строчки пикселей на сколько надо (понятно, что нецелое число, ну и фиг с ними с погрешностями округления =)
Ну и потом сравниваем с шаблонами циферок.
Если захотеть, за вечер можно наваять. Про алгоритмы обработки изображений, которые я упомянул, хорошо написано на английской википедии ;)
я один устал от просветленных или есть еще такие?
да, блин, надо просто меньше реагировать на это =) ясно-понятно, что в подобном «просветлении» ключевую роль играет массовая агитация и пропаганда; при этом, «просветлённые», как правило, весьма поверхностно знают и Python, и Ruby (часто — на уровне мануалов «пишем блог за 15 минут»), и, рассуждают в большей мере о фреймворках, противопоставляя (зачем-то? зачем? :)) — язык.
«Просветлённым»: главное, попадая под влияние массовой мэйнстримовой идеи, иметь смелость жить своим умом. И, если уж Вы хотите, показать, рассказать об особенностях и достоинствах Python'a/Ruby'и, то и нужно писать именно об этом, а не о том, как «похапе» (блин, какое забавное наречие :)) «уныл» ;)
Ну, вот этот момент уже спорный. Прогресс имеет направление, связанное со снижением механической работы в созидательном процессе. «Правило генерации (из одной философии): избегайте ручного ввода кода, при любом удобном случае, пишите программы, которые бы писали программы.» Поэтому кодогенерация (и как частные случае — просто автокомплит и сниппеты в редакторе) — это хорошо.
По идее, плагины (в данном случае, руби-джемы) тоже можно представить частями конструктора (уже сгенерированными за человека), из которой кто-то строит свои системы. И это тоже хорошо. Другой вопрос — качество этих плагинов и знание самого Руби (или даже РубиОнРэйлс) — если этот вопрос тоже не столь важен — это тоже хорошо, человек строит свою систему и достигает цели (а цели разные — кому-то хочется глубоко знать язык, кому-то — закрывать побольше заказов — все цели относительно хороши).
спасибо за инвайт!
Сейчас, наверное, самый простой и дешевый способ распознавать капчи в поток — это воспользоваться специализированными сервисами, которые позволяют распознавать 1000 капч за 1$
1) Режем капчу
На этом этапе отрезаем слева и справа по 12px.
Т.к. высота цифры не выше 14px, то снизу и сверху обрезаем лишнее, в зависимости от высоты всей капчи.
2) Идем по полоскам, попиксельно закрашиваячерным, если встречаем резкое (критерий надо вывести) увеличение яркости, то закрашиваем белым.
В результате останутся только цифры на черном фоне
3) дальше распознаем, наложением шаблона.
ЗЫ Так же как вариант, можно применить фильтр — четкие границы (недавно была статья на хабре).
Но! Оказалось, что в редких случаях, пиксели практически сливались с градиентом (получаем нечёткие границы). Конечно во многих случаях капча бы распознавалась успешно, но всё же был бы достаточно высокий % когда капча не распозновалась.
Поэтому и пришлось сначала чистить угловой градиент, и лишь потом обрезать саму капчу.
Обновите. Очень хочется посмотреть.
I = imread('cap.png'); A = imsubtract(I, imopen(I, strel('disk', 2))); imwrite(A, 'cap-new.png');Результат:
Остается диагональная линия, но ее можно не убирать, она не мешает.
Вобще gif это зло для капч, там качество цифр на порядки выше чем у jpg и читать такие капчи легче
Что будет если чуть-чуть изменить алгоритм формирования капчи?! — скрипт перестанет работать.
Но в целом понравилось ;)
Этим постом ты хотел показать, что ходишь на лекции?
зы: на лекции хожу.
Про «корреляционный метод» и «математическую морфологию» слышу впервые.
Так что по-видимому целью моего поста было — показать каким образом, таким смертным как я, не владеющими этими внеземными технологиями — просто разобрать капчу :)
Т.е. 30 капч в 10 секунд = 3 капчи в секунду.
Причем алгоритмы пусть хоть заменяются. И можно к тому же это на поток поставить.
Правда гугл, подлец, требует подтверждения по СМСке, но здесь уже надо привлекать китайцев — их много.
А вааще, если по теме, то афтар — крайне плохой человек, ибо способствует делу спамеров гребаных. Хоть и из праздного любопытства.
А вот это палка о двух концах. Чем больше будет взламываться простых капч, тем больше будет тех, что у Яндекса. Тем сложней будет их взламывать… до поры до времени — пока не научатся )) Закон щита и меча, одним словом.
Сломаете? ;)
Чуть сложнее, не думаете? :)
01011001 01001010 00101010 00000101 10100010 11
Давайте лучше продолжим этот интим в личной конфиденциальной переписке? (шутка)
Кстати, как видим, работает и фильтрует интеллектуальные организмы :)
Похоже что нужен интернет с надежной аутентификацией пользователей, тогда и каптчи будут не нужны. На самом деле достало уже вводить их, пихают везде, а толк от них только на некотрых сайтах.
Иначе можно было бы принять смску и спамить, парсить, досить…
А сам я даже и не задумывался на этим. Думал, что анализ графики — слишком сложное дело и явно не для пхп.
Большое спасибо!
Просто вы видимо самобучались этому языку?)
Работу с изображениями изучал, а вот анализ проспал )))