Verwendung von JavaScript zum Knacken von Bestätigungscodes
Kürzlich ist im Internet ein JavaScript-Skript aufgetaucht, das Verifizierungscodes knacken kann – GreaseMonkey! Dieses von „Shaun Friedle“ entwickelte Skript kann das CAPTCHA der Megaupload-Site leicht lösen. Wenn Sie es nicht glauben, können Sie es selbst unter http://herecomethelizards.co.uk/mu_captcha/ versuchen.
Um ehrlich zu sein, wurde das von der Megaupload-Site bereitgestellte CAPTCHA durch den oben genannten Code besiegt Der Code hier ist Nein, nicht sehr gut. Aber was noch interessanter ist:
1. Die Canvas-Anwendungsschnittstelle getImageData in HTML 5 kann verwendet werden, um Pixeldaten aus dem Verifizierungscode-Bild zu erhalten. Mit Canvas können wir ein Bild nicht nur in eine Leinwand einbetten, sondern es später auch wieder daraus extrahieren.
2. Das obige Skript enthält ein vollständig in JavaScript implementiertes neuronales Netzwerk.
3. Nachdem Sie Canvas verwendet haben, um Pixeldaten aus dem Bild zu extrahieren, senden Sie sie an das neuronale Netzwerk und verwenden Sie eine einfache optische Zeichenerkennungstechnologie, um abzuleiten, welche Zeichen im Bestätigungscode verwendet werden.
Durch das Lesen des Quellcodes können wir nicht nur seine Funktionsweise besser verstehen, sondern auch verstehen, wie dieser Verifizierungscode implementiert wird. Wie Sie bereits gesehen haben, sind die hier verwendeten CAPTCHAs nicht sehr komplex – jedes CAPTCHA besteht aus drei Zeichen, jedes Zeichen verwendet eine andere Farbe und verwendet nur Zeichen aus dem 26-Buchstaben-Alphabet, während alle Zeichen alle dieselbe Schriftart verwenden.
Der Zweck des ersten Schritts liegt auf der Hand: Er kopiert den Bestätigungscode auf die Leinwand und konvertiert ihn in ein Graustufenbild.
Funktion „convert_grey(image_data)“
for (var x = 0; x < image_data.width; x++){
for (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*image_data.width;
var luma = Math.floor(image_data.data[i] * 299/1000 +
image_data.data[i+1] * 587/1000 +
image_data.data[i+2] * 114/1000);
image_data.data[i] = luma;
image_data.data[i+1] = luma;
image_data.data[i+2] = luma;
image_data.data[i+3] = 255;
}
}
}
Teilen Sie dann die Leinwand in drei separate Pixelmatrizen auf, die jeweils ein Zeichen enthalten. Dieser Schritt ist sehr einfach durchzuführen, da jedes Zeichen eine eigene Farbe verwendet und somit farblich unterschieden werden kann.
filter(image_data[0], 105);
filter(image_data[1], 120);
filter(image_data[2], 135);
Funktionsfilter(image_data, color){
for (var x = 0; x < image_data.width; x++){
for (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*image_data.width;
// Alle Pixel der bestimmten Farbe in Weiß verwandeln
if (image_data.data[i] == color) {
image_data.data[i] = 255;
image_data.data[i+1] = 255;
image_data.data[i+2] = 255;
// Alles andere zu schwarz
} anders {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
}
}
}
Schließlich werden alle irrelevanten Störpixel eliminiert. Dazu können Sie zunächst die weißen (übereinstimmenden) Pixel finden, die vorne oder hinten von schwarzen (nicht übereinstimmenden) Pixeln umgeben sind, und dann die übereinstimmenden Pixel löschen.
var i = x*4+y*4*image_data.width;
var oben = x*4+(y-1)*4*image_data.width;
var unten = x*4+(y+1)*4*image_data.width;
if (image_data.data[i] == 255 &&
image_data.data[above] == 0 &&
image_data.data[below] == 0) {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
Nachdem wir nun eine ungefähre Form des Zeichens haben, führt das Skript die erforderliche Kantenerkennung durch, bevor es in das neuronale Netzwerk geladen wird. Das Skript sucht nach den Pixeln ganz links, rechts, oben und unten in der Grafik, wandelt sie in ein Rechteck um und wandelt das Rechteck dann wieder in eine 20*25-Pixel-Matrix um.
cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
var Edges = find_edges(image_data[i]);
cropped_canvas.getContext("2d").drawImage(canvas, Edges[0], Edges[1],
Kanten[2]-Kanten[0], Kanten[3]-Kanten[1], 0, 0,
Kanten[2]-Kanten[0], Kanten[3]-Kanten[1]);
image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
cropped_canvas.width, cropped_canvas.height);
Was erhalten wir nach der obigen Verarbeitung? Eine 20*25-Matrix, die ein einzelnes Rechteck enthält, das mit Schwarz und Weiß gefüllt ist. Das ist großartig!
Dann vereinfachen wir das Rechteck noch weiter. Wir extrahieren strategisch Punkte aus der Matrix als „Photorezeptoren“, die in das neuronale Netzwerk eingespeist werden. Beispielsweise kann ein bestimmter Fotorezeptor einem Pixel bei 9*6 entsprechen, mit oder ohne Pixel. Das Skript extrahiert eine Reihe solcher Zustände (weitaus weniger als die gesamte 20x25-Matrixberechnung – es werden nur 64 Zustände extrahiert) und speist diese Zustände in das neuronale Netzwerk ein.
Sie fragen sich vielleicht: Warum nicht einfach Pixel vergleichen? Ist es notwendig, ein neuronales Netzwerk zu verwenden? Der Punkt ist, dass wir diese mehrdeutigen Situationen beseitigen wollen. Wenn Sie die vorherige Demo ausprobiert haben, werden Sie feststellen, dass der direkte Vergleich von Pixeln fehleranfälliger ist als der Vergleich über ein neuronales Netzwerk, obwohl dies nicht häufig vorkommt. Wir müssen jedoch zugeben, dass für die meisten Benutzer ein direkter Pixelvergleich ausreichen dürfte.
Der nächste Schritt besteht darin, zu versuchen, die Buchstaben zu erraten. 64 Boolesche Werte (erhalten aus einem der Charakterbilder) werden in das neuronale Netzwerk importiert, sowie eine Reihe vorberechneter Daten. Eines der Konzepte neuronaler Netze besteht darin, dass die Ergebnisse, die wir erhalten möchten, im Voraus bekannt sind, sodass wir das neuronale Netz basierend auf den Ergebnissen entsprechend trainieren können. Der Skriptautor kann das Skript mehrmals ausführen und eine Reihe der besten Ergebnisse sammeln, die dem neuronalen Netzwerk dabei helfen können, die Antwort zu erraten, indem er von den Werten aus rückwärts arbeitet, die sie erzeugt haben. Diese Ergebnisse haben jedoch keine besondere Bedeutung.
Wenn das neuronale Netzwerk die 64 booleschen Werte berechnet, die einem Buchstaben im Bestätigungscode entsprechen, vergleicht es diese mit einem vorberechneten Alphabet und gibt dann eine Bewertung für die Übereinstimmung mit jedem Buchstaben ab. (Das Endergebnis kann ähnlich sein: 98 % können der Buchstabe A sein, 36 % können der Buchstabe B usw. sein.)
Nachdem alle drei Buchstaben im Bestätigungscode verarbeitet wurden, wird das Endergebnis angezeigt. Es sollte beachtet werden, dass dieses Skript nicht 100 % korrekt ist (ich frage mich, ob die Genauigkeit der Wertung verbessert werden kann, wenn die Buchstaben am Anfang nicht in Rechtecke umgewandelt werden), aber es ist ziemlich gut, zumindest für den aktuellen Zweck. Sag es. Und alle Vorgänge werden im Browser basierend auf der Standard-Client-Technologie ausgeführt.
Als Ergänzung sollte diese Technologie bei anderen einfachen Verifizierungscodes gut funktionieren, bei komplexen Verifizierungscodes jedoch nicht außerhalb der Reichweite (insbesondere dieser Art der kundenbasierten Analyse). Ich hoffe, dass noch mehr Menschen von diesem Projekt inspiriert werden und noch mehr wunderbare Dinge entwickeln können, denn sein Potenzial ist so groß