Mit PHP einen simplen Captcha cracken

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:

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

Noch ein Screenshot:
Geknackter Captcha

Das war’s schon – viel Vergnügen damit!

Zippyshare Downloads Automatisieren *update*

Kurz nachdem ich den letzten Beitrag über zippyshare veröffentlicht habe, hat die Seite wieder umgestellt auf einen anderen “Schutz” der Downloadlinks. Diesmal ist es aber ziemlich einfach an diese zu geraten, man muss nur nach documents.getElementById(‘dlbutton’).href = “…” suchen. Dort findet sich in Klammern ein einfacher mathematischer Ausdruck, der in fasst jeder Programmiersprache mit einem Einzeiler ausgeführt werden kann.

Zippyshare Downloads automatisieren

Zur Zeit arbeite ich an einem Downloadmanager, der alle gängigen Download-Seiten unterstützen soll. Neulich hat zippyshare.com etwas eingebaut um genau diesen “automatischen” Downloads einen Riegel vorzuschieben: Der Download-Link wird jetzt per JavaScript “verschlüsselt”. Ich werde jetzt hier keinen Beispiel-Zippyshare-Downloadlink posten, das findet man mit etwas googlen schnell selbst ;-)

Nun möchte ich erläutern, wie ich den Download-Link automatisch auslese:
Zuerst entnehme ich der Seite per Regulärem Ausdruck die benötigten JavaScript Variablen:
xHTML-Code der Stelle

<script type="text/javascript">
var zipdata = eval(eval(eval(((function() {var parzDiXdet= (((function() {var hutByDojpZ= eval(379);var BvLCmMYDEQB= ((function() {var qefrKDjxbJ= eval(51);var DKbCbnceoJN= ((((18+(3*3)))+((function() {var kzCDpmQUwq= eval(eval(43));var UoRHKeZvyaH= 23;return (kzCDpmQUwq+UoRHKeZvyaH)})())));return (qefrKDjxbJ+DKbCbnceoJN)})());return (hutByDojpZ+BvLCmMYDEQB)})())*eval((19*eval(29)+((eval((eval(3)*3))+(((function() {var fhYCresaSD= 0;var XRIVTZgfbzF= eval(2);return (fhYCresaSD+XRIVTZgfbzF)})())*eval(3)+1)))))+468);var MpIWcCCkdZo= (((function() {var ciqbBKFBYW= eval(80);var aAZRNKwkocE= 242;return (ciqbBKFBYW+aAZRNKwkocE)})())*(3*(8*eval(32)+(1*((function() {var yMgeUqevwX= ((function() {var CIechCiWBN= 0;var GoKsZmkLAMw= ((function() {var JYpKjwyxxu= 0;var YfDpdwhpscp= 2;return (JYpKjwyxxu+YfDpdwhpscp)})());return (CIechCiWBN+GoKsZmkLAMw)})());var FklyvoQTPho= eval(eval(2));return (yMgeUqevwX+FklyvoQTPho)})())))+eval(eval((1*((0+eval(2)))))))+261);return (parzDiXdet+MpIWcCCkdZo)})()))));
var fulllink = 'http://www123.zippyshare.com/d/123456789/'+zipdata+'/Alarmclock.mp3';
document.getElementById('dlbutton').href = fulllink;
</script>

Regexp zum auslesen

$aFind = StringRegExp($ZippySrc, "<script type=""text/javascript"">\s*(var zipdata = .*;)\s*var fulllink = '(.*)';\s*document", 1)

Nun muss die Variable “zipdata” evaluiert werden. Dies mache ich wie folgt:

;
;
; Evaluate zippyshare.com JavaScript
;
; Coded by www.agrafix.net
;
;
Func _JSParse_Parse($sJavaScript)
	For $_x = 0 To 50
 
		If StringRegExp($sJavaScript, "var zipdata = ([0-9]*);") Then
			ExitLoop
		EndIf
 
		$sJavaScript = _JSParse_NumericVariableDeclarations($sJavaScript)
		$sJavaScript = _JSParse_EvaluateEasyMath($sJavaScript)
		$sJavaScript = _JSParse_SimpleEvals($sJavaScript)
		$sJavaScript = _JSParse_SimpleFunctions($sJavaScript)
	Next
 
	$m = StringRegExp($sJavaScript, "var zipdata = ([0-9]*);", 1)
 
	Return $m[0]
 
EndFunc
 
Func _JSParse_SourceReplace($aArray, $sJavaScript)
	For $i = 0 To UBound($aArray) - 1 Step 2
		$sJavaScript = StringReplace($sJavaScript, $aArray[$i], $aArray[$i+1])
	Next
 
	Return $sJavaScript
EndFunc
 
Func _JSParse_NumericVariableDeclarations($sJavaScript)
	$aNumDeclarations = StringRegExp($sJavaScript, "var ([a-zA-Z0-9_-]*) ?= ([0-9]*);", 3)
 
	$sJavaScript = StringRegExpReplace($sJavaScript, "var ([a-zA-Z0-9_-]*) ?= ([0-9]*);", "")
 
	$sJavaScript = _JSParse_SourceReplace($aNumDeclarations, $sJavaScript)
 
	Return $sJavaScript
EndFunc
 
Func _JSParse_EvaluateEasyMath($sJavaScript)
 
	While StringRegExp($sJavaScript, "\(([0-9]*?[+\-*/]?[0-9]*[+\-*/][0-9]*)\)")
 
		$aMatches = StringRegExp($sJavaScript, "\(([0-9]*?[+\-*/]?[0-9]*[+\-*/][0-9]*)\)", 3)
 
		For $i = 0 To UBound($aMatches) - 1
			$Result = Execute($aMatches[$i])
 
			$sJavaScript = StringReplace($sJavaScript, "(" & $aMatches[$i] & ")", $Result)
		Next
 
	WEnd
 
	Return $sJavaScript
 
EndFunc
 
Func _JSParse_SimpleEvals($sJavaScript)
 
	While StringRegExp($sJavaScript, "eval\(([0-9]*)\)")
 
		$aMatches = StringRegExp($sJavaScript, "eval\(([0-9]*)\)", 3)
 
		For $i = 0 To UBound($aMatches) - 1
			$sJavaScript = StringReplace($sJavaScript, "eval(" & $aMatches[$i] & ")", $aMatches[$i])
		Next
 
	WEnd
 
	$sJavaScript = StringRegExpReplace($sJavaScript, "\(([0-9]{1,11})\)", "$1")
 
	Return $sJavaScript
EndFunc
 
Func _JSParse_SimpleFunctions($sJavaScript)
 
	$sJavaScript = StringRegExpReplace($sJavaScript, "\(function\(\) {return ([0-9]*)}\)\(\)", "$1")
 
	Return $sJavaScript
EndFunc

Nun muss ich die Funktion nur noch mit den obigen Werten aus meiner RegExp aufrufen, die JavaScript Variable “fulllink” anpassen und aufrufen:

$url = StringReplace($aFind[1], "'+zipdata+'", _JSParse_Parse($aFind[0]))
INetGet($url, "Alarm.mp3")

Das war’s auch schon. Alle Source-Codes sind in AutoIT. Natürlich ist _JSParse kein funktionierender JavaScript Parser, sondern einfach nur eine Funktionssammlung die eben diesen Zippyshare-JavaScript-Haufen evaluieren kann ;)

Der obige Source-Code darf natürlich verwendet, verändert und verteilt werden, ich bitte legendlich um einen kleinen Hinweis auf den Ursprung (http://www.agrafix.net).

jQuery in Greasemonkey

Wer kennt das nicht? Man möchte ein Greasemonkey-Script schreiben und würde dabei gerne auf jQuery zurückgreifen. Außerdem verwendet die betreffende Seite bereits jQuery, das heißt man müsste dieses Script eigentlich gar nicht mehr nachladen. Doch wie kann man auf die bereits vorhandene jQuery-Instanz zugreifen? Ganz einfach:

var $ = unsafeWindow.jQuery;

Das steht zu beginn des Scripts und nun kann man wie immer per $ auf alle jQuery Funktionen und Elemente der Seite zugreifen.

In diesem Zuge möchte ich gleich noch zwei neue UserScripts für das Browserspiel Die-Stämme vorstellen, die ich heute veröffentlicht habe und welche ebenfalls auf die genannte Technik zurückgreifen:

[DS] Save Attack

Ermöglicht es im Versammlungsplatz die gewählen Einheiten zu speichern um bei einem weiteren Angriff mit gleichen Truppen anzugreifen

Download & Install

[DS] Menu Resort

Dieses Script sortiert die ingame Die-Stämme Menüleiste so um wie sie früher war (vor Version 7.x)

Download & Install