Solange ein Captcha nicht sehr aufwändig generiert wurde, ist er auch entsprechend leicht zu knacken. Ich habe hier mal ein Beispiel für einen schlechten Captcha:
Was ist an diesem Captcha schlecht? Der auszulesende Inhalt ist nicht abwechselnd genug (immer nur Zahlenfolgen), nicht verzerrt, nicht farblich variierend und hebt sich zu gut vom Hintergrund ab. Das alles werden wir nun ausnutzen um den Captcha zu knacken.
Zuerst müssen wir „Zahlendefinitionen“ schreiben. Das heißt wir legen für jede Zahl einige eindeutige Fixpunkte fest, mit denen die Zahl eindeutig identifizierbar ist.
Beispiel an der Zahl „1“:
OO OOO OOOO OO OO OO OO OO OO OOOOOO
Die Definition:
function is_one($pix, $x, $y) { $c = 0; $c += check($pix, $x, $y, 2, -2); // 5 / 5 $c += check($pix, $x, $y, 3, -2); // 6 / 5 $c += check($pix, $x, $y, 1, -1); // 4 / 6 $c += check($pix, $x, $y, 2, -1); // 5 / 6 $c += check($pix, $x, $y, 3, -1); // 6 / 6 $c += check($pix, $x, $y, 0, 0); // 3 / 7 $c += check($pix, $x, $y, 1, 0); // 4 / 7 $c += check($pix, $x, $y, 2, 0); // 5 / 7 $c += check($pix, $x, $y, 3, 0); // 6 / 7 $c += check($pix, $x, $y, 2, 1); // 5 / 8 $c += check($pix, $x, $y, 3, 1); // 6 / 8 $c += check($pix, $x, $y, 2, 2); // 5 / 9 $c += check($pix, $x, $y, 3, 2); // 6 / 9 $c += check($pix, $x, $y, 2, 3); // 5 / 10 $c += check($pix, $x, $y, 3, 3); // 6 / 10 $c += check($pix, $x, $y, 2, 4); // 5 / 11 $c += check($pix, $x, $y, 3, 4); // 6 / 11 $c += check($pix, $x, $y, 2, 5); // 5 / 12 $c += check($pix, $x, $y, 3, 5); // 6 / 12 $c += check($pix, $x, $y, 2, 6); // 5 / 13 $c += check($pix, $x, $y, 3, 6); // 6 / 13 $c += check($pix, $x, $y, 0, 7); // 3 / 14 $c += check($pix, $x, $y, 1, 7); // 4 / 14 $c += check($pix, $x, $y, 2, 7); // 5 / 14 $c += check($pix, $x, $y, 3, 7); // 6 / 14 $c += check($pix, $x, $y, 4, 7); // 7 / 14 $c += check($pix, $x, $y, 5, 7); // 8 / 14 if ($c == 27) { return true; } return false; }
Dort sind nun alle Fixpunkte relativ zum „ersten“ Fixpunkt gespeichert. Der „erste“ Fixpunkt ist bei der eins der Punkt ganz unten rechts. Nachdem wir solche Definitionen für alle Zahlen von 0-9 erstellt haben geht’s nun an die Wiedererkennung der Zahlen. Dazu muss zuerst der Hintergrund herausgefiltert werden und die Grafik in ein besser (für unser Programm) lesbares Format gebracht werden. Dies mache ich wie folgt:
function read($path) { $im = ImageCreateFromPNG($path); $pix = array(); $sy = ImageSy($im); $sx = ImageSx($im); for ($y = 0;$y<$sy;$y++) { for ($x = 0;$x<$sx;$x++) { $col = imagecolorat($im, $x, $y); $rgb = imagecolorsforindex($im, $col); if ($rgb["red"] <= 150) { $pix[$x][$y] = "O"; } else { $pix[$x][$y] = "W"; } } } // ... }
Nun haben wir den Array $pix, der bei [x][y] entweder W (für weiß) oder O (für schwarz) enthält. Jetzt müssen wir nur noch durch den Array durchgehen und nach unseren Fixpunkt-Mustern suchen:
// .... $no = 0; $complete_string = ""; $found_at = array(); for ($x = 0;$x<$sx;$x++) { for ($y = 0;$y<$sy;$y++) { if ($pix[$x][$y] == "O" && !in_array("$x|$y", $found_at)) { $no = 0; if (is_one($pix, $x, $y)) { $no = 1; } if (is_two($pix, $x, $y)) { $no = 2; } if (is_three($pix, $x, $y)) { $no = 3; } if (is_four($pix, $x, $y)) { $no = 4; } if (is_five($pix, $x, $y)) { $no = 5; } if (is_six($pix, $x, $y)) { $no = 6; } if (is_seven($pix, $x, $y)) { $no = 7; } if (is_eight($pix, $x, $y)) { $no = 8; } if (is_nine($pix, $x, $y)) { $no = 9; } if ($no != 0) { $found_at[] = "$x|$y"; $complete_string .= $no; } } } } //...
Die Variable $complete_string enthält nun den ausgelesenen String. Simple as that 😉
Hier der gesamte Code inklusive der Hilfsfunktion check(). Der Code ist schon etwas älter, also nicht über schlechten Code-Stil wundern – er soll legendlich das Prinzip erklären: http://agrafix.net.pastebin.com/b6exg0SW
Das war’s schon – viel Vergnügen damit!