使用Python和Tesseract來識別圖形驗證碼
*本文原創作者:ipenox,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
各位在企業中做Web漏洞掃描或者滲透測試的朋友,可能會經常遇到需要對圖形驗證碼進行程序識別的需求。很多時候驗證碼明明很簡單(對於非互聯網企業,或者企業內網中的應用來說特別如此),但因為沒有趁手的識別庫,也只能苦哈哈地進行人肉識別,或者無奈地放棄任務。在這裡,我分享一下自己使用Python和開源的tesseract OCR引擎做驗證碼識別的經驗,並提供相關的源代碼和示例供大家借鑒。
一、關於圖形驗證碼識別與tesseractOCR
儘管多數圖型驗證碼只有區區幾個數字或字母,但你可能聽說了,在進行機器識別的過程中,你要收集樣本,對圖片去噪、二值化、提取字元、計算特徵,甚至還要祭出神經網路去訓練數據進行機器學習……還沒開干,退堂鼓早打響三遍了。其實我根本不想去鑽研那麼多高深的理論,只想要寥寥數行Python代碼就搞定它,然後把主要精力投入到更重要的滲透測試中去。在這種情況下,tesseract就能幫上大忙了。
Tesseract的OCR引擎最早是HP實驗室開發的,曾經是 OCR業內最準確的三款識別引擎之一。2005年該引擎交給了Google,作為開源項目發布在Google Project上了。Tesseract提供獨立程序和API兩種形式供用戶使用。純白色背景、字元規整無干擾像素的驗證碼圖片可以直接調用tesseract程序來進行識別。如要更方便靈活地在自己的程序中進行識別,則可以使用tesseract的API。
二、Tesseract的編譯和安裝
Tesseract的項目主頁(https://github.com/tesseract-ocr/tesseract)上wiki中有詳細的編譯安裝步驟,大家可以參考,本文中我們將以3.05.01版本為基礎。我的系統環境是RHEL 7.4,64位版本。首先用yum安裝各種依賴的圖形庫,然後用源碼安裝Leptonica(官方主頁http://www.leptonica.com/download.html,版本需要1.74以上),編譯安裝很簡單,解壓後,以默認參數依次執行configure,make,make install命令即可。安裝完之後需執行:
不然在下一步tesseract的configure腳本會報找不到Leptonica。
將tesseract的源碼解壓後進入到源碼主目錄下依次執行:
即可成功安裝。
根據項目wiki,Data Files節的指南下載相應的數據文件,因為我們只識別英文和數字驗證碼,所以下載3.04/3.05版本的英語文件eng.traineddata即可,下載後放到/usr/local/share/tessdata目錄下。至此,tesseract就安裝完畢了。
三、為Python封裝tesseract API
tesseract提供的是C++ API(介面界面是TessBaseAPI類),最核心的函數就是TessBaseAPI::TesseractRect這個函數。為了能在Python中方便地使用,我將其封裝為Python模塊了,詳細代碼放在github上:https://github.com/penoxcn/Decaptcha。該模塊名為decaptcha,源文件包括以下四個文件:
setup.py、decaptcha.i、decaptcha.h和depcaptcha.cpp。
將以上文件放在同一個臨時目錄下,然後執行以下命令進行編譯和安裝:
安裝時需要調用swig命令,所以系統需要先安裝swig。
如果tesseract不是安裝在默認的路徑下,請參照setup.py代碼自行修改相關的頭文件和庫文件的路徑即可。
安裝完之後進入Python交互環境試著import一下看是否正常:
如果報錯找不到libtesseract,那可能是tesseract的庫目錄(/usr/local/lib)沒有在Python的庫搜索目錄中。這時候可以將tesseract的庫目錄添加到系統的/etc/ld.so.conf文件中(加了之後需要執行ldconfig命令以生效);或者每次import decaptcha模塊之前,都先執行以下Python代碼:
四、安裝Python PIL庫
PIL的全稱是Python Imaging Library,是一個強大而易用的圖像庫。在其主頁(http://pythonware.com/products/pil/)下載最新版(截止目前是1.1.7)源代碼進行安裝。安裝之前確保系統已安裝了png/jpeg/tiff等圖像庫。解壓縮之後,在主目錄下執行python setup.py install即可。
使用很簡單,下面的代碼片段從任意格式圖片文件創建一個Image對象,進行格式轉換,獲得其大小和像素數組,只需幾行代碼:
五、實戰驗證碼識別
至此,進行圖形驗證碼識別的依賴環境都已準備好,我們可以開幹了。
識別的流程簡單來說如下:
1. 用Image載入圖像,轉為RGBA格式,然後獲取像素數據;
2. 將RGBA格式的像素數據轉換為0和1的位元組串(其實就是二值化處理);
3. 調用decaptcha模塊進行圖像識別,獲得驗證碼字元串
實際的代碼也非常簡單,請看我項目Decaptcha目錄下的decaptcha_test.py文件,關鍵代碼也就十幾行。影響代碼長短或複雜性的,就是二值化這一步了。其實很多圖形驗證碼比較簡單,細心分析一下,不難得出二值化的條件。下面以我工作中遇到過的一些驗證碼為例:
有5組,均來自於我公司的不同業務網站。識別代碼請參看我項目目錄下decaptcha_demo.py文件,所有的示例驗證碼放在images目錄下。大家可以用圖片編輯器打開相關的驗證碼文件觀察和分析像素的規律。
第一組aa系列,字元顏色偏白,背景偏黑,所以可試著以像素RGB均值(或總和)大於某個數值為條件進行轉換:r+g+b>=480則為1,否則為0。
第二組bb系列,字元有顏色,背景偏白色,轉換條件考慮為RGB中是否有兩個要素大於0xf0:int(r/240)+int(g/240)+int(b/240)
第三組cc系列,字元和背景都是單色,但是有不固定位置的點干擾,干擾點顏色與字元顏色相同,但是都是離散的。這種情況下,像素是白色的就是背景0,否則再判斷一下是否離散的點,可以簡單地判斷它右邊和下邊的點是否都是白色背景來判定。
第四組dd系列,字元顏色偏紫色,並且有背景干擾線。通過將一些樣本圖片的每個像素的RGB值列印出來,確赫然發現字元像素的G通道值都為0,其它情況要麼是背景,要麼是干擾線。
第五組ee系列是最複雜的,有干擾線,干擾點,字元也有變形,顏色也不固定。實際上它來源於一個叫做securimage的php庫所產生,恐怕不能一兩行代碼就二值化了。但是仔細觀察它的模式會發現,它的大背景、干擾線、干擾點、字元都是用同一種顏色產生。所以我們可以以統計數量的方式來找出哪些是背景顏色(出現次數最多的自然是背景顏色)。另外我們再統計每個字元的顏色與背景顏色的偏差(將rgb差值的平方加起來),找出干擾線、字元與背景色的偏差值的閾值範圍,再將其在二值化的時候進行應用,也可以成功地將其二值化。而字元變形的問題則不需要擔心,交給tesseract就可以了。
在二值化的時候,我在屏幕上用# 符號列印出了二值化後的圖像,大家可以看下效果:
驗證碼實際是5648,識別為5649,錯了一位。
下面來個正確識別的:
第五組的識別情況:
因圖片有點長,超過了終端的列數,故輸出到txt文件後再用notepad++打開的:
驗證碼實際是912065,識別為912085,錯了一位。
再看看總體成績如何:
aa組10個驗證碼,整體正確識別的有5個。
bb組10個驗證碼,整體正確識別的3個。
cc組10個驗證碼,整體正確識別的9個。
dd組10個驗證碼,整體正確識別的3個。
ee組10個驗證碼,整體正確識別的4個。
aa組、cc組和ee組識別得還可以,沒有識別出來的多數僅錯了一個字元。而bb和dd組識別得較差,沒有識別出來的可能都錯了兩三個字元。
這份成績單,咋看之下,有些朋友可能覺得很不好看。但是,請不要太悲觀,要想想投入產出比,大多數情況下,除了固定的模式化代碼外,我們只需要編寫寥寥幾行二值化的代碼就可以收穫輸出了啊,這意味著在測試時,程序自動化就可以搞了啊:不求百分之一百,只求十里有一發。即使只有30%的識別率,連續識別5個圖片,獲得其中一個準確識別的概率也達到了86%,無非多浪費一些Web請求而已。
因為Image庫支持從內存中載入圖片,所以與requests庫的結合也是非常的方便,直接通過HTTP請求下載下來,將內容丟給Image就可以了:
是不是很簡單很愉快?
六、後記
通過以上的例子和代碼,大家可以初探tesseract的能力。實際上tesseract支持學習,通過樣本訓練,可以大幅提高它的識別準確率,當然這個過程就有點複雜了,也不符合本文追求敏捷的宗旨。經過多年的技術對抗,傳統的圖片驗證碼已經顯得過時了,但仍有很多企業網站在大量使用,希望本文能夠給大家一些啟發和幫助。
*本文原創作者:ipenox,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
※Python函數基本講解
※史上最全Python知識點未完待續
TAG:Python |