利用JavaScript破解驗(yàn)證碼
原創(chuàng)【51CTO.com 獨(dú)家翻譯】
原文:http://ejohn.org/blog/dom-insertadjacenthtml/
近日,網(wǎng)上驚現(xiàn)可以破解驗(yàn)證碼的JavaScript腳本——GreaseMonkey!由“Shaun Friedle”開發(fā)的這段腳本可以輕松搞定Megaupload站點(diǎn)的CAPTCHA。如果您不相信的話,可以到http://herecomethelizards.co.uk/mu_captcha/親自嘗試一下!
現(xiàn)在,Megaupload站點(diǎn)提供的CAPTCHA在上述代碼面前已經(jīng)敗下陣來,說實(shí)話,這里的驗(yàn)證碼設(shè)計(jì)的不不太好,下面給出一些例子:
![]() |
圖1 |
但是,更有趣的是:
1.HTML 5中的Canvas應(yīng)用程序接口getImageData可以用來從驗(yàn)證碼圖像中取得像素?cái)?shù)據(jù)。利用Canvas,我們不僅可以將一個(gè)圖像嵌入一個(gè)畫布中,而且之后還可以再?gòu)闹兄匦绿崛〕鰜怼?/P>
2.上述的腳本中包含一個(gè)完全使用JavaScript實(shí)現(xiàn)的神經(jīng)網(wǎng)絡(luò)。
3.使用Canvas從圖像中提取出像素?cái)?shù)據(jù)后,將其送入神經(jīng)網(wǎng)絡(luò),通過一種簡(jiǎn)單的光學(xué)字符識(shí)別技術(shù)來推測(cè)驗(yàn)證碼中到底使用了哪些字符。
通過閱讀源代碼,我們不僅可以更好地理解其工作原理,也可以領(lǐng)會(huì)這個(gè)驗(yàn)證碼究竟是如何實(shí)現(xiàn)的。就像前面看到的那樣,這里使用的驗(yàn)證碼不是很復(fù)雜——每個(gè)驗(yàn)證碼有三個(gè)字符組成,每個(gè)字符使用一種不同的顏色,并且只使用26個(gè)字母中的字符,而所有字符都使用同一種字體。
第一步的用意很明顯,那就是把驗(yàn)證碼拷貝到畫布上,并且把它轉(zhuǎn)化為灰度圖。
function 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;
}
}
}
然后,將畫布分成三個(gè)單獨(dú)的像素矩陣,每個(gè)矩陣包含一個(gè)字符。這一步實(shí)現(xiàn)起來非常容易,因?yàn)槊總€(gè)字符都使用一種單獨(dú)的顏色,所以通過顏色就可以將其區(qū)分開來。
filter(image_data[0], 105);
filter(image_data[1], 120);
filter(image_data[2], 135);
function filter(image_data, colour){
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;
// Turn all the pixels of the certain colour to white
if (image_data.data[i] == colour) {
image_data.data[i] = 255;
image_data.data[i+1] = 255;
image_data.data[i+2] = 255;
// Everything else to black
} else {
image_data.data[i] = 0;
image_data.data[i+1] = 0;
image_data.data[i+2] = 0;
}
}
}
}
最終,所有無(wú)關(guān)的干擾像素都被剔除出去。為此,可以先查找那些前面或者后面被黑色(未匹配的)像素圍繞的白色(匹配過的)像素,然后將匹配過的像素刪除即可。
var i = x*4+y*4*image_data.width;
var above = x*4+(y-1)*4*image_data.width;
var below = 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;
}
現(xiàn)在我們已經(jīng)得到了字符的大約圖形,但在將其載入神經(jīng)網(wǎng)絡(luò)之前,腳本還會(huì)進(jìn)一步對(duì)它進(jìn)行必要的邊緣檢測(cè)。腳本會(huì)尋找圖形最左、右、上、下方的像素,并將其轉(zhuǎn)化為一個(gè)矩形,接著把矩形重新轉(zhuǎn)換為一個(gè)20*25像素的矩陣。
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],
edges[2]-edges[0], edges[3]-edges[1], 0, 0,
edges[2]-edges[0], edges[3]-edges[1]);
image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
cropped_canvas.width, cropped_canvas.height);
經(jīng)過上面的處理,我們得到了什么呢? 一個(gè)20*25的矩陣,其中包含單個(gè)矩形,其中填由黑白色。真是太好了!
然后,會(huì)對(duì)這個(gè)矩形做進(jìn)一步的簡(jiǎn)化。我們策略性地從矩陣中提取一些點(diǎn),作為“光感受器”,這些光感受器將輸送到神經(jīng)網(wǎng)絡(luò)。舉例而言,某個(gè)光感受器具體對(duì)應(yīng)的可能是位于9*6位置像素,有像素或者沒有像素。腳本會(huì)提取一系列這樣的狀態(tài)(遠(yuǎn)少于對(duì) 20*25矩陣整個(gè)計(jì)算的次數(shù)——只提取64種狀態(tài)),并將這些狀態(tài)送入神經(jīng)網(wǎng)絡(luò)。
您可能要問,為什么不直接對(duì)像素進(jìn)行比較?有必要使用神經(jīng)網(wǎng)絡(luò)嗎?問題的關(guān)鍵在于,我們要去掉那些模棱兩可的情況。如果您試過前面的演示就會(huì)發(fā)現(xiàn),直接進(jìn)行像素比較比通過神經(jīng)網(wǎng)絡(luò)比較,更容易出錯(cuò),盡管出錯(cuò)的時(shí)候不多。但我們必須承認(rèn),對(duì)于大部分用戶來說,直接的像素比較應(yīng)該已經(jīng)夠用了。
下一步就是嘗試猜字母了。神經(jīng)網(wǎng)絡(luò)中導(dǎo)入了64個(gè)布爾值(由其中的一個(gè)字符圖像獲取而來),同時(shí)包含一系列預(yù)先計(jì)算好的數(shù)據(jù)。神經(jīng)網(wǎng)絡(luò)的理念之一,就是我們希望得的結(jié)果事先就是知道的,所以我們可以針對(duì)結(jié)果對(duì)神經(jīng)網(wǎng)絡(luò)進(jìn)行相關(guān)的訓(xùn)練。腳本作者可以多次運(yùn)行腳本,并收集了一系列最佳評(píng)分,這些評(píng)分能幫助倒推出產(chǎn)生它們的那些值,從而幫神經(jīng)網(wǎng)絡(luò)猜出答案,除此之外,這些評(píng)分沒有任何特殊意義。
當(dāng)神經(jīng)網(wǎng)絡(luò)對(duì)驗(yàn)證碼中一個(gè)字母對(duì)應(yīng)的64個(gè)布爾值進(jìn)行計(jì)算以后,和一個(gè)預(yù)先計(jì)算好的字母表相比較,然后為和每個(gè)字母的匹配都給出一個(gè)分?jǐn)?shù)。(最后的結(jié)果可能類似:98%的可能是字母A,36%的可能是字母B等。)
當(dāng)對(duì)驗(yàn)證碼中的三個(gè)字母都經(jīng)過處理以后,最終的結(jié)果也就出來了。需要注意的是,該腳本無(wú)法達(dá)到100%正確性(不知道如果在開始的時(shí)候不將字母轉(zhuǎn)換成矩形,是不是可以提高評(píng)分的精度),但這已經(jīng)相當(dāng)好了,至少對(duì)于當(dāng)前的用途來說是這樣。而且所有的操作都是在基于標(biāo)準(zhǔn)的客戶端技術(shù)實(shí)現(xiàn)的瀏覽器中完成的!
補(bǔ)充說明一下,這個(gè)腳本應(yīng)該算是一個(gè)特例吧,這項(xiàng)技術(shù)可能會(huì)很好的工作在在其它簡(jiǎn)陋的驗(yàn)證碼上,但對(duì)于復(fù)雜的驗(yàn)證碼來說,就有點(diǎn)鞭長(zhǎng)莫及了(尤其是這種基于客戶端的分析)。但愿有更多人能從這個(gè)項(xiàng)目中受到啟發(fā)而開發(fā)出更奇妙的東西來,因?yàn)樗臐摿?shí)在是太大了?!?1CTO.COM 獨(dú)家翻譯,轉(zhuǎn)載請(qǐng)注明出處及譯者!】
【編輯推薦】