Utiliser JavaScript pour déchiffrer les codes de vérification
Récemment, un script JavaScript capable de déchiffrer les codes de vérification est apparu sur Internet - GreaseMonkey ! Ce script développé par "Shaun Friedle" peut facilement résoudre le CAPTCHA du site Megaupload. Si vous n'y croyez pas, vous pouvez l'essayer vous-même sur http://herecomethelizards.co.uk/mu_captcha/ !
Maintenant, le CAPTCHA fourni par le site Megaupload a été vaincu par le code ci-dessus. le code ici est conçu Non, pas très bon. Mais ce qui est plus intéressant, c'est :
1. L'interface de l'application Canvas getImageData en HTML 5 peut être utilisée pour obtenir des données de pixels à partir de l'image du code de vérification. En utilisant Canvas, nous pouvons non seulement intégrer une image dans un canevas, mais également la réextraire ultérieurement.
2. Le script ci-dessus contient un réseau de neurones entièrement implémenté en JavaScript.
3. Après avoir utilisé Canvas pour extraire les données de pixels de l'image, envoyez-les au réseau neuronal et utilisez une technologie simple de reconnaissance optique de caractères pour déduire quels caractères sont utilisés dans le code de vérification.
En lisant le code source, nous pouvons non seulement mieux comprendre son fonctionnement, mais également comprendre comment ce code de vérification est implémenté. Comme vous l'avez vu précédemment, les CAPTCHA utilisés ici ne sont pas très complexes : chaque CAPTCHA se compose de trois caractères, chaque caractère utilise une couleur différente et utilise uniquement des caractères de l'alphabet de 26 lettres, tandis que tous les caractères utilisent tous la même police.
Le but de la première étape est évident : copier le code de vérification sur le canevas et le convertir en une image en niveaux de gris.
fonction convert_grey(image_data){
pour (var x = 0; x < image_data.width; x++){
pour (var y = 0; y < image_data.height; y++){
var je = 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] = luminance;
image_data.data[i+2] = luminance;
image_data.data[i+3] = 255 ;
}
}
}
Ensuite, divisez le canevas en trois matrices de pixels distinctes, chacune contenant un caractère. Cette étape est très simple à réaliser car chaque caractère utilise une couleur distincte, il peut donc être distingué par couleur.
filtre(image_data[0], 105);
filtre(image_data[1], 120);
filtre(image_data[2], 135);
filtre de fonction (image_data, couleur) {
pour (var x = 0; x < image_data.width; x++){
pour (var y = 0; y < image_data.height; y++){
var je = x*4+y*4*image_data.width;
// Transforme tous les pixels d'une certaine couleur en blanc
if (image_data.data[i] == couleur) {
image_data.data[i] = 255 ;
image_data.data[i+1] = 255 ;
image_data.data[i+2] = 255 ;
// Tout le reste en noir
} autre {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
}
}
}
Enfin, tous les pixels parasites non pertinents sont éliminés. Pour ce faire, vous pouvez d'abord rechercher les pixels blancs (correspondants) qui sont entourés de pixels noirs (sans correspondance) devant ou derrière, puis supprimer les pixels correspondants.
var je = x*4+y*4*image_data.width;
var ci-dessus = x*4+(y-1)*4*image_data.width;
var ci-dessous = x*4+(y+1)*4*image_data.width;
si (image_data.data[i] == 255 &&
image_data.data[ci-dessus] == 0 &&
image_data.data[ci-dessous] == 0) {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
Maintenant que nous avons une forme approximative du personnage, le script effectue en outre la détection des contours nécessaire avant de le charger dans le réseau neuronal. Le script recherchera les pixels les plus à gauche, à droite, en haut et en bas du graphique, les convertira en rectangle, puis reconvertira le rectangle en une matrice de 20*25 pixels.
cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
var bords = find_edges(image_data[i]);
cropped_canvas.getContext("2d").drawImage(canvas, bords[0], bords[1],
bords[2]-bords[0], bords[3]-bords[1], 0, 0,
bords[2]-bords[0], bords[3]-bords[1]);
image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
cropped_canvas.width, cropped_canvas.height);
Après le traitement ci-dessus, qu'obtenons-nous ? Une matrice 20*25 contenant un seul rectangle rempli de noir et de blanc. C'est super.
Ensuite, nous simplifierons encore plus le rectangle. Nous extrayons stratégiquement des points de la matrice en tant que « photorécepteurs » qui seront introduits dans le réseau neuronal. Par exemple, un certain photorécepteur peut correspondre à un pixel situé en 9*6, avec ou sans pixels. Le script extrait une série de ces états (beaucoup moins que l'ensemble du calcul matriciel 20x25 - seuls 64 états sont extraits) et alimente ces états dans le réseau neuronal.
Vous pourriez vous demander pourquoi ne pas simplement comparer les pixels directement ? Est-il nécessaire d’utiliser un réseau neuronal ? Le fait est que nous voulons nous débarrasser de ces situations ambiguës ? Si vous avez essayé la démo précédente, vous remarquerez que la comparaison directe des pixels est plus sujette aux erreurs que la comparaison via un réseau neuronal, même si cela n'arrive pas souvent. Mais il faut admettre que pour la plupart des utilisateurs, une comparaison directe des pixels devrait suffire.
L'étape suivante consiste à essayer de deviner les lettres. 64 valeurs booléennes (obtenues à partir de l'une des images de personnages) sont importées dans le réseau neuronal, ainsi qu'une série de données pré-calculées. L'un des concepts des réseaux de neurones est que les résultats que nous souhaitons obtenir sont connus à l'avance, afin que nous puissions entraîner le réseau de neurones en conséquence sur la base des résultats. L'auteur du script peut exécuter le script plusieurs fois et collecter une série de meilleurs scores qui peuvent aider le réseau neuronal à deviner la réponse en revenant sur les valeurs qui les ont produits, mais ces scores n'ont aucune signification particulière.
Lorsque le réseau de neurones calcule les 64 valeurs booléennes correspondant à une lettre du code de vérification, il les compare avec un alphabet pré-calculé, puis attribue un score pour la correspondance avec chaque lettre. (Le résultat final peut être similaire : 98 % peuvent être la lettre A, 36 % peuvent être la lettre B, etc.)
Une fois les trois lettres du code de vérification traitées, le résultat final sera publié. Il convient de noter que ce script n'est pas correct à 100% (je me demande si la précision de la notation peut être améliorée si les lettres ne sont pas converties en rectangles au début), mais il est plutôt bon, du moins pour le but actuel. Dis-le. Et toutes les opérations sont effectuées dans le navigateur sur la base d'une technologie client standard !
En complément, ce script doit être considéré comme un cas particulier. Cette technologie peut bien fonctionner sur d'autres codes de vérification simples, mais pour les codes de vérification complexes, c'est un peu le cas. hors de portée (en particulier ce type d’analyse basée sur le client). J'espère que davantage de personnes pourront s'inspirer de ce projet et développer des choses plus merveilleuses, car son potentiel est si grand.