近期研究一些突破驗證碼方面的知識,記錄下來。一方面算是對這幾天學習知識的總結幫助自己理解;另一方面希望對研究這方面的技術同學有所幫助;另外也希望引起網站管理者的注意,在提供驗證碼時多些考慮進去。由於剛剛接觸這方面的知識,理解比較淺顯,有錯誤再所難免,歡迎拍磚。
驗證碼的作用: 有效防止某個黑客對某一個特定注冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試。其實現代的驗證碼一般是防止機器批量注冊的,防止機器批量發帖回復。目前,不少網站為了防止用戶利用機器人自動注冊、登錄、灌水,都采用了驗證碼技術。
所謂驗證碼,就是將一串隨機產生的數字或符號,生成一幅圖片,圖片裡加上一些干擾象素(防止OCR),由用戶肉眼識別其中的驗證碼信息,輸入表單提交網站驗證,驗證成功後才能使用某項功能。
我們最常見的驗證碼
1,四位數字,隨機的一數字字符串,最原始的驗證碼,驗證作用幾乎為零。
2,隨機數字圖片驗證碼。圖片上的字符比較中規中矩,有的可能加入一些隨機干擾素,還有一些是隨機字符顏色,驗證作用比上一個好。沒有基本圖形圖像學知識的人,不可破!
3,各種圖片格式的隨機數字+隨機大寫英文字母+隨機干擾像素+隨機位置。
4,漢字是注冊目前最新的驗證碼,隨機生成,打起來更難了,影響用戶體驗,所以,一般應用的比較少。
簡單起見,我們這次說明的主要對象是第2種類型的,我們先看幾種網上比較常見的這種驗證碼的圖片.
(不知道怎麼搞的,CSDN又不能上傳圖片了,我把這四種圖片放到下載包中了,可以下載下來對比察看)
這四種樣式,基本上能代表2中所提到的驗證碼類型,初步看起來第一個圖片最容易破解,第二個次之,第三個更難,第四個最難。
真實情況那?其實這三種圖片破解難度相同。
第一個圖片,最容易,圖片背景和數字都使用相同的顏色,字符規整,字符位置統一。
第二個圖片,看似不容易,其實仔細研究會發現其規則,背景色和干擾素無論怎麼變化,驗證字符字符規整,顏色相同,所以排除干擾素非常容易,只要是非字符色素全部排除即可。
第三個圖片,看似更復雜,處理上面提到背景色和干擾素一直變化外,驗證字符的顏色也在變化,並且各個字符的顏色也各不相同。看似無法突破這個驗證碼,本篇文章,就一這種類型驗證碼為例說明,第四個圖片,同學們自己搞。
第四個圖片,除了第三個圖片上提到的特征外,又在文字上加了兩條直線干擾率,看似困難其實,很容易去掉。
驗證碼識別一般分為以下幾個步驟:
1. 取出字模
2. 二值化
3. 計算特征
4. 對照樣本
1:取出字模
識別驗證碼,畢竟不是專業的OCR識別,並且,由於各個網站的驗證碼各不相同,所以,最常見的方法就是就是建立這個驗證碼的特征碼庫。去字模時,我們需要多下載幾張圖片,使這些圖片中,包括所有的字符,我們這裡的字母只有圖片,所以,只要收集到包括0-9的圖片即可。
2:二值化
二值化就是把圖片上的驗證數字上每個象素用一種數字表示1,其他部分用0表示。這樣就可以計算出每個數字字模,記錄下這些字模來,當作key即可。
3:計算特征
把要識別的圖片,進行二值化,得到圖片特征。
4:對照樣本
把步驟3種的圖片特征碼和驗證碼的字模進行對比,得到驗證圖片上的數字。
使用目前這種方法,對驗證碼的識別基本上可以做到100%。
通過以上步驟,您可能說了,並沒有發現如何取出干擾素啊!其實取出干擾素的方法很簡單,干擾素的一個重要特征是,不能影響驗證碼的顯示效果,所以制作干擾素時它的RGB可能低於或者高於某個特定值,比如我給的例子中的圖片,干擾素的RGB各項值是不會超過125的,所以,這樣我們就很容易去掉干擾素了。
php代碼
[php]
<?php
define('WORD_WIDTH',9);
define('WORD_HIGHT',13);
define('OFFSET_X',7);
define('OFFSET_Y',3);
define('WORD_SPACING',4);
class valite
{
public function setImage($Image)
{
$this->ImagePath = $Image;
}
public function getData()
{
return $data;
}
public function getResult()
{
return $DataArray;
}
public function getHec()
{
$res = imagecreatefromjpeg($this->ImagePath);
$size = getimagesize($this->ImagePath);
$data = array();
for($i=0; $i < $size[1]; ++$i)
{
for($j=0; $j < $size[0]; ++$j)
{
$rgb = imagecolorat($res,$j,$i);
$rgbarray = imagecolorsforindex($res, $rgb);
if($rgbarray['red'] < 125 || $rgbarray['green']<125
|| $rgbarray['blue'] < 125)
{
$data[$i][$j]=1;
}else{
$data[$i][$j]=0;
}
}
}
$this->DataArray = $data;
$this->ImageSize = $size;
}
public function run()
{
$result="";
// 查找4個數字
$data = array("","","","");
for($i=0;$i<4;++$i)
{
$x = ($i*(WORD_WIDTH+WORD_SPACING))+OFFSET_X;
$y = OFFSET_Y;
for($h = $y; $h < (OFFSET_Y+WORD_HIGHT); ++ $h)
{
for($w = $x; $w < ($x+WORD_WIDTH); ++$w)
{
$data[$i].=$this->DataArray[$h][$w];
}
}
}
// 進行關鍵字匹配
foreach($data as $numKey => $numString)
{
$max=0.0;
$num = 0;
foreach($this->Keys as $key => $value)
{
$percent=0.0;
similar_text($value, $numString,$percent);
if(intval($percent) > $max)
{
$max = $percent;
$num = $key;
if(intval($percent) > 95)
break;
}
}
$result.=$num;
}
$this->data = $result;
// 查找最佳匹配數字
return $result;
}
public function Draw()
{
for($i=0; $i<$this->ImageSize[1]; ++$i)
{
for($j=0; $j<$this->ImageSize[0]; ++$j)
{
echo $this->DataArray[$i][$j];
}
echo "/n";
}
}
public function __construct()
{
$this->Keys = array(
'0'=>'000111000011111110011000110110000011110000011110000011110000011110000011110000011110000011011000110011111110000111000',
'1'=>'000111000011111000011111000000011000000011000000011000000011000000011000000011000000011000000011000011111111011111111',
'2'=>'011111000111111100100000110000000111000000110000001100000011000000110000001100000011000000110000000011111110111111110',
'3'=>'011111000111111110100000110000000110000001100011111000011111100000001110000000111000000110100001110111111100011111000',
'4'=>'000001100000011100000011100000111100001101100001101100011001100011001100111111111111111111000001100000001100000001100',
'5'=>
'111111110111111110110000000110000000110000000111110000111111100000001110000000111000000110100001110111111100011111000',
'6'=>'000111100001111110011000010011000000110000000110111100111111110111000111110000011110000011011000111011111110000111100',
'7'=>'011111111011111111000000011000000010000000110000001100000001000000011000000010000000110000000110000001100000001100000',
'8'=>'001111100011111110011000110011000110011101110001111100001111100011101110110000011110000011111000111011111110001111100',
'9'=>'001111000011111110111000111110000011110000011111000111011111111001111011000000011000000110010000110011111100001111000',
);
}
protected $ImagePath;
protected $DataArray;
protected $ImageSize;
protected $data;
protected $Keys;
protected $NumStringArray;
}
?>
我做了一個例子,你可以從這裡下載http://www.BkJia.com/uploadfile/2012/0316/20120316110154186.rar
破解完成上面的驗證碼,我們就可以使用snoopy(比curl要輕量,所以我喜歡)來模擬浏覽器器,訪問網站了。