Menggunakan JavaScript untuk memecahkan kode verifikasi
Baru-baru ini, skrip JavaScript yang dapat memecahkan kode verifikasi telah muncul di Internet - GreaseMonkey! Skrip yang dikembangkan oleh "Shaun Friedle" ini dapat dengan mudah memecahkan CAPTCHA situs Megaupload. Kalau tidak percaya, kamu bisa mencobanya sendiri di http://herecomethelizards.co.uk/mu_captcha/ !
Nah, CAPTCHA yang disediakan situs Megaupload sudah dikalahkan oleh kode di atas kode di sini dirancang Tidak, tidak terlalu bagus. Namun yang lebih menariknya adalah:
1. Antarmuka aplikasi Canvas getImageData dalam HTML 5 dapat digunakan untuk memperoleh data piksel dari gambar kode verifikasi. Dengan menggunakan Canvas, kita tidak hanya dapat menyematkan gambar ke dalam kanvas, tetapi juga mengekstraknya kembali nanti.
2. Skrip di atas berisi jaringan saraf yang diimplementasikan seluruhnya dalam JavaScript.
3. Setelah menggunakan Canvas untuk mengekstrak data piksel dari gambar, mengirimkannya ke jaringan saraf, dan menggunakan teknologi pengenalan karakter optik sederhana untuk menyimpulkan karakter mana yang digunakan dalam kode verifikasi.
Dengan membaca kode sumbernya, kita tidak hanya dapat lebih memahami cara kerjanya, tetapi juga memahami bagaimana kode verifikasi ini diterapkan. Seperti yang Anda lihat sebelumnya, CAPTCHA yang digunakan di sini tidak terlalu rumit - setiap CAPTCHA terdiri dari tiga karakter, setiap karakter menggunakan warna berbeda, dan hanya menggunakan karakter dari 26 huruf alfabet, sedangkan semua karakter Semua menggunakan font yang sama.
Tujuan dari langkah pertama sudah jelas, yaitu menyalin kode verifikasi ke kanvas dan mengubahnya menjadi gambar skala abu-abu.
fungsi convert_grey(data_gambar){
untuk (var x = 0; x < gambar_data.lebar; x++){
untuk (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*data_gambar.lebar;
var luma = Math.floor(image_data.data[i] * 299/1000 +
gambar_data.data[i+1] * 587/1000 +
gambar_data.data[i+2] * 114/1000);
image_data.data[i] = luma;
image_data.data[i+1] = luma;
image_data.data[i+2] = luma;
gambar_data.data[i+3] = 255;
}
}
}
Kemudian, bagi kanvas menjadi tiga matriks piksel terpisah, masing-masing berisi satu karakter. Langkah ini sangat mudah dilakukan karena setiap karakter menggunakan warna tersendiri sehingga dapat dibedakan berdasarkan warnanya.
filter(data_gambar[0], 105);
filter(data_gambar[1], 120);
filter(data_gambar[2], 135);
filter fungsi(data_gambar, warna){
untuk (var x = 0; x < gambar_data.lebar; x++){
untuk (var y = 0; y < image_data.height; y++){
var i = x*4+y*4*data_gambar.lebar;
// Ubah semua piksel dengan warna tertentu menjadi putih
if (data_gambar.data[i] == warna) {
gambar_data.data[i] = 255;
gambar_data.data[i+1] = 255;
gambar_data.data[i+2] = 255;
// Segalanya menjadi hitam
} kalau tidak {
gambar_data.data[i] = 0;
gambar_data.data[i+1] = 0;
gambar_data.data[i+2] = 0;
}
}
}
}
Akhirnya, semua piksel pengganggu yang tidak relevan dihilangkan. Untuk melakukannya, pertama-tama Anda dapat menemukan piksel putih (yang cocok) yang dikelilingi oleh piksel hitam (tidak cocok) di depan atau belakang, lalu menghapus piksel yang cocok tersebut.
var i = x*4+y*4*data_gambar.lebar;
var di atas = x*4+(y-1)*4*image_data.width;
var di bawah = x*4+(y+1)*4*image_data.width;
if (gambar_data.data[i] == 255 &&
image_data.data[di atas] == 0 &&
image_data.data[di bawah] == 0) {
gambar_data.data[i] = 0;
gambar_data.data[i+1] = 0;
gambar_data.data[i+2] = 0;
}
Sekarang kita memiliki perkiraan bentuk karakter, skrip selanjutnya melakukan deteksi tepi yang diperlukan sebelum memuatnya ke jaringan saraf. Skrip akan mencari piksel paling kiri, kanan, atas, dan bawah dari grafik, mengubahnya menjadi persegi panjang, lalu mengubah ulang persegi panjang tersebut menjadi matriks 20*25 piksel.
cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
var edge = find_edges(data_gambar[i]);
cropped_canvas.getContext("2d").drawImage(kanvas, tepian[0], tepian[1],
tepian[2]-tepian[0], tepian[3]-tepian[1], 0, 0,
tepian[2]-tepian[0], tepian[3]-tepian[1]);
image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
cropped_canvas.lebar, cropped_canvas.height);
Setelah pemrosesan di atas, apa yang kita dapatkan? Matriks berukuran 20*25 berisi sebuah persegi panjang yang diisi dengan warna hitam dan putih. Bagus sekali!
Lalu, kita akan menyederhanakan persegi panjang lebih jauh lagi. Kami secara strategis mengekstrak titik-titik dari matriks sebagai "fotoreseptor" yang akan dimasukkan ke dalam jaringan saraf. Misalnya, fotoreseptor tertentu mungkin berhubungan dengan piksel yang terletak di 9*6, dengan atau tanpa piksel. Skrip mengekstrak serangkaian status tersebut (jauh lebih sedikit dari keseluruhan penghitungan matriks 20x25 - hanya 64 status yang diekstraksi) dan memasukkan status tersebut ke dalam jaringan saraf.
Anda mungkin bertanya, mengapa tidak membandingkan piksel secara langsung saja? Apakah perlu menggunakan jaringan saraf? Intinya, kita ingin menghilangkan situasi ambigu tersebut. Jika Anda sudah mencoba demo sebelumnya, Anda akan melihat bahwa membandingkan piksel secara langsung lebih rawan kesalahan dibandingkan membandingkan melalui jaringan saraf, meskipun hal itu tidak banyak terjadi. Namun harus kita akui bahwa bagi sebagian besar pengguna, perbandingan piksel langsung sudah cukup.
Langkah selanjutnya adalah mencoba menebak hurufnya. 64 Nilai Boolean (diperoleh dari salah satu gambar karakter) diimpor ke jaringan saraf, serta serangkaian data yang telah dihitung sebelumnya. Salah satu konsep jaringan saraf tiruan adalah hasil yang ingin kita peroleh sudah diketahui sebelumnya, sehingga kita dapat melatih jaringan saraf tersebut berdasarkan hasil tersebut. Penulis skrip dapat menjalankan skrip beberapa kali dan mengumpulkan serangkaian skor terbaik yang dapat membantu jaringan saraf menebak jawabannya dengan bekerja mundur dari nilai yang menghasilkannya, namun skor tersebut tidak memiliki arti khusus.
Ketika jaringan saraf menghitung 64 nilai Boolean yang sesuai dengan sebuah huruf dalam kode verifikasi, membandingkannya dengan alfabet yang telah dihitung sebelumnya, dan kemudian memberikan skor untuk kecocokan dengan setiap huruf. (Hasil akhirnya mungkin serupa: 98% mungkin huruf A, 36% mungkin huruf B, dst.)
Setelah ketiga huruf pada kode verifikasi diproses, maka akan keluar hasil akhir. Perlu dicatat bahwa skrip ini tidak 100% benar (saya ingin tahu apakah keakuratan penilaian dapat ditingkatkan jika huruf-hurufnya tidak diubah menjadi persegi panjang di awal), tetapi cukup bagus, setidaknya untuk tujuan saat ini. Katakan begitu. Dan semua operasi diselesaikan di browser berdasarkan teknologi klien standar!
Sebagai tambahan, skrip ini harus dianggap sebagai kasus khusus. Teknologi ini mungkin berfungsi dengan baik pada kode verifikasi sederhana lainnya, tetapi untuk kode verifikasi yang rumit, ini sedikit di luar jangkauan (terutama analisis berbasis klien semacam ini). Saya berharap lebih banyak orang dapat terinspirasi oleh proyek ini dan mengembangkan hal-hal yang lebih menakjubkan, karena potensinya sangat besar