1. 程式人生 > >圖形驗證碼的生成和破解

圖形驗證碼的生成和破解

驗證碼的功能一般是防止使用程式惡意註冊、暴力破解或批量發帖而設定的。所謂驗證碼,就是將一串隨機產生的數字或符號,生成一幅圖片,圖片里加上一些干擾象素(防止OCR),由使用者肉眼識別其中的驗證碼資訊,輸入表單提交網站驗證,驗證成功後才能使用某項功能。學習驗證碼的破解/識別技術,不僅可以知道驗證碼的原理,而且可以讓你知道怎樣才能防止驗證碼被破解。

最常見的驗證碼主要有以下幾種:

  1. 四位數字,隨機的一數字字串,最原始的驗證碼,驗證作用幾乎為零。
  2. 隨機數字圖片驗證碼。圖片上的字元比較中規中矩,有的可能加入一些隨機干擾素,還有一些是隨機字元顏色,驗證作用比上一個好。沒有基本圖形影象學知識的人,不可破!
  3. 各種圖片格式的隨機數字+隨機大寫英文字母+隨機干擾畫素+隨機位置。
  4. 漢字是註冊目前最新的驗證碼,隨機生成,打起來更難了,影響使用者體驗,所以,一般應用的比較少。

為簡單起見,破解說明主要針對是第2種類型的,先來看看網上常見的這種驗證碼的圖片:

  • 第一種,最容易,圖片背景和數字都使用相同的顏色,字元規整,字元位置統一。
  • 第二種,看似不容易,其實仔細研究會發現其規則,背景色和干擾素無論怎麼變化,驗證字元字元規整,顏色相同,所以排除干擾素非常容易,只要是非字元色素全部排除即可。
  • 第三種,看似更復雜,處理上面提到背景色和干擾素一直變化外,驗證字元的顏色也在變化,並且各個字元的顏色也各不相同。
  • 第四種,除了第三個圖片上提到的特徵外,又在文字上加了兩條直線干擾率,看似困難其實,很容易去掉。

驗證碼識別一般分為以下幾個步驟:

  1. 取出字模 識別驗證碼,畢竟不是專業的OCR識別,並且,由於各個網站的驗證碼各不相同,所以,最常見的方法就是就是建立這個驗證碼的特徵碼庫。去字模時,我們需要多下載幾張圖片,使這些圖片中,包括所有的字元,我們這裡的字母只有圖片,所以,只要收集到包括0-9的圖片即可。
  2. 二值化 二值化就是把圖片上的驗證數字上每個象素用一種數字表示1,其他部分用0表示。這樣就可以計算出每個數字字模,記錄下這些字模來,當作key即可。
  3. 計算特徵 把要識別的圖片,進行二值化,得到圖片特徵。
  4. 對照樣本 把步驟3種的圖片特徵碼和驗證碼的字模進行對比,得到驗證圖片上的數字。

使用目前這種方法,對驗證碼的識別基本上可以做到100%。

通過以上步驟,您可能說了,並沒有發現如何取出干擾素啊!其實取出干擾素的方法很簡單,干擾素的一個重要特徵是,不能影響驗證碼的顯示效果,所以製作干擾素時它的RGB可能低於或者高於某個特定值,比如我給的例子中的圖片,干擾素的RGB各項值是不會超過125的,所以,這樣我們就很容易去掉干擾素了。

簡單的驗證碼只有數字和字母組成,格式統一,每次出現位置固定。下面繼續深入研究識別驗證碼,這次需要識別的目標是:驗證碼有字元和數字組成,驗證碼存在旋轉(可能左右都旋轉),位置不固定,存在字元與字元之間的粘連,且驗證碼有更強的干擾素。

我們以下圖為例進行講解。

第一步:二值化。把驗證碼的部分用 1 表示,背景部分用 0 表示出來,識別方法很簡單,我們打印出驗證碼整張圖片的 RGB ,然後分析其規律即可,通過 RGB 碼,我們很容易分辨出上面這張圖片的 R 值大於 120 , G 和 B 的值小於 80 ,所以依據這個規則我們很容易把上面的圖片二值化。

再來看看上面的第三種驗證碼圖片

剛看上去,感覺很複雜。驗證碼的圖片每次背景色都不相同,且不是單色,各個驗證碼數字的顏色每次也各不相同。貌似很難二值化,其實我們打印出其 RGB 值很容易就發現。無論驗證數字顏色如何變化,該數字的 RGB 值總有一個值小於 125 ,所以通過如下判斷 $rgbarray['red'] < 125 || $rgbarray['green']<125|| $rgbarray['blue'] < 125 我們就很容易分辨出哪裡是數字,哪裡是背景。

我們能夠找到這些規律的原因是,在製作驗證碼的干擾素時,為了使干擾素不影響數字的顯示效果,必須使用干擾素的 RGB 和數字 RGB 相互獨立,互不干擾。只要懂得這個規律,我們就很容易實現二值化。

我們找到的 120 , 80 , 125 等閾值,可能和實際的 RGB 有出入,所以,有時二值化後,會有部分地方出現 1 ,對於驗證碼上固定位置顯示數字,這種干擾沒有太大意義。但是對於驗證碼位置不確定的圖片來說,在我們切割字元時,很可能造成干擾。所以,在二值化後要進行去噪處理。

第二部:去噪處理。去噪的原理很簡單,就是把孤立的有效的值去掉,如果噪點比較高,要求的效率也比較高的話,這裡面也有很多工作要做。幸好這裡我們不要求這麼高深,我們使用最簡單的方法就可以,如果一個點為 1 則判斷這個點的上下左右上左上右下左下右 8 個方位上數字是否為 1 ,如果不為 1 ,就認為是一個燥點,直接設定為 1 即可。

如上圖所示,我們使用此方法很容易發現紅色方框部分的 1 為燥點,直接設定為 1 即可。在判斷時我們使用了一個技巧,有時候的噪點可能是兩個連續的 1 ,所以我們計算這個點的 8 個方向上的值之和,最後我們判斷他們的和是否小於特定的閾值。

第三部:切割字元。切割字元的方法有很多種,這裡採用最簡單的一種,先垂直方向切割成為字元,然後在水平方向去掉多於的 0000 ,如下圖

第一步切割紅線部分,第二步切割藍線部分,這樣就可以得到獨立的字元了。但是像下面這種情況

按上面的方法會把 dw 字元切割成一個字元,這是錯誤的切割,所以這裡我們涉及到粘連字元的切割。

第四步:粘連字元切割。製作驗證碼時,規則字元的粘連很容易分割開,如果字元本身有縮放,變形就很難處理,經過分析,我們可以發現,上面的字元粘連屬於很簡單的方式,只是規則字元的粘連,所以處理這種情況,我們也使用很簡單的處理方式。當完成分割操作後,我們不能馬上確定分割的部分就為一個字元,要進行驗證,驗證的關鍵因素就是,切割下來的字元的寬是否大於閾值,這個閾值的取捨標準是,一個字元無論怎麼旋轉變形都不會大於這個閾值,所以,如果我們切割的塊大於這個閾值,就可以認為這是一個粘連字元;如果大於兩個閾值之和,就認為是三個字元粘連,以此類推。知道這個規則後,切割粘連字元也就很簡單了。如果我們發現是粘連字元塊,直接平分這個塊為兩個或者多個新的塊就可以。當然為了更好的還原字元,我一般都採用平分 +1 , -1 對字元塊的部分進行適當的補充。

第五步:匹配字元。對於旋轉字元的特徵碼建立,有很多種方法,這裡就不做深入研究了。我這裡使用的最簡單的方式,為所有字元的所有情況建立匹配庫,所以在我提供的程式碼種增加了 study 操作,其目的就是,先有人手工識別圖片的驗證碼,然後通過 study 方法,寫入特徵碼庫。這樣寫入的圖片資料越多,驗證識別的準確行也就越高。

經過以上步驟,我們基本上可以識別現在網際網路上大部分的驗證碼,這裡我們都是使用的最簡單的方法,沒有使用任何 OCR 知識。

另外製作驗證碼的一些建議:

對於識別驗證碼的程式來說,最難得部分是驗證字元的切割和特徵碼的建立,而國內很多程式設計師只做驗證碼時,總是喜歡在驗證碼加很多幹擾素,干擾線,影響效果不說,還達不到很好的效果;所以,要想使自己驗證碼難於本識別,只做下面兩點就夠了

  1. 字元粘連,最好所有的字元都有粘連的部分;
  2. 不要使用規格字元,驗證碼的各個部分使用不同比例的縮放或者旋轉。

只要做到這兩點,或者這兩點的變形,識別程式就很難識別。