各位在企业中做 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 命令即可安装完之后需执行:
exportPKG_CONFIG_PATH=/usr/local/lib/pkgconfig
不然在下一步 tesseract 的 configure 脚本会报找不到 Leptonica
将 tesseract 的源码解压后进入到源码主目录下依次执行:
- ./autogen.sh
- ./configure--with-extra-libraries=/usr/local/lib
- make
- make install
即可成功安装
根据项目 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.pydecaptcha.idecaptcha.h 和 depcaptcha.cpp
将以上文件放在同一个临时目录下, 然后执行以下命令进行编译和安装:
python setup.py install
安装时需要调用 swig 命令, 所以系统需要先安装 swig
如果 tesseract 不是安装在默认的路径下, 请参照 setup.py 代码自行修改相关的头文件和库文件的路径即可
安装完之后进入 Python 交互环境试着 import 一下看是否正常:
from decaptcha import Decaptcha
如果报错找不到 libtesseract, 那可能是 tesseract 的库目录 (/usr/local/lib) 没有在 Python 的库搜索目录中这时候可以将 tesseract 的库目录添加到系统的 / etc/ld.so.conf 文件中(加了之后需要执行 ldconfig 命令以生效); 或者每次 import decaptcha 模块之前, 都先执行以下 Python 代码:
- import sys
- sys.path.append("/usr/local/lib")
四安装 Python PIL 库
PIL 的全称是 Python Imaging Library, 是一个强大而易用的图像库在其主页 ( http://pythonware.com/products/pil/ ) 下载最新版 (截止目前是 1.1.7) 源代码进行安装安装之前确保系统已安装了 png/jpeg/tiff 等图像库解压缩之后, 在主目录下执行 python setup.py install 即可
使用很简单, 下面的代码片段从任意格式图片文件创建一个 Image 对象, 进行格式转换, 获得其大小和像素数组, 只需几行代码:
- from PIL import Image
- img = Image.open('test.png') # Your image here!
- img = img.convert("RGBA")
- pixdata = img.load()
- width,height = img.size
- print 'imgsize: %dx %d' % (width, height)
- print'pixel[2,4]:', pixdata[2, 4] #eg,(0xD3,0xD3,0xD3,0xFF)
五实战验证码识别
至此, 进行图形验证码识别的依赖环境都已准备好, 我们可以开干了
识别的流程简单来说如下:
用 Image 加载图像, 转为 RGBA 格式, 然后获取像素数据;
将 RGBA 格式的像素数据转换为 0 和 1 的字节串(其实就是二值化处理);
调用 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) <=1 则为 1, 否则为 0
第三组 cc 系列, 字符和背景都是单色, 但是有不固定位置的点干扰, 干扰点颜色与字符颜色相同, 但是都是离散的这种情况下, 像素是白色的就是背景 0, 否则再判断一下是否离散的点, 可以简单地判断它右边和下边的点是否都是白色背景来判定
第四组 dd 系列, 字符颜色偏紫色, 并且有背景干扰线通过将一些样本图片的每个像素的 RGB 值打印出来, 确赫然发现字符像素的 G 通道值都为 0, 其它情况要么是背景, 要么是干扰线
第五组 ee 系列是最复杂的, 有干扰线, 干扰点, 字符也有变形, 颜色也不固定实际上它来源于一个叫做 securimage 的 php 库所产生, 恐怕不能一两行代码就二值化了但是仔细观察它的模式会发现, 它的大背景干扰线干扰点字符都是用同一种颜色产生所以我们可以以统计数量的方式来找出哪些是背景颜色 (出现次数最多的自然是背景颜色) 另外我们再统计每个字符的颜色与背景颜色的偏差(将 rgb 差值的平方加起来), 找出干扰线字符与背景色的偏差值的阈值范围, 再将其在二值化的时候进行应用, 也可以成功地将其二值化而字符变形的问题则不需要担心, 交给 tesseract 就可以了
在二值化的时候, 我在屏幕上用# 符号打印出了二值化后的图像, 大家可以看下效果:
python decaptcha_demo.py aa aa1.gif
验证码实际是 5648, 识别为 5649, 错了一位
下面来个正确识别的:
python decaptcha_demo.py bb bb1.jpg
第五组的识别情况:
python decaptcha_demo.py ee ee9.png > 1.txt
因图片有点长, 超过了终端的列数, 故输出到 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 就可以了:
- import requests,StringIO
- r =requests.get(imgurl)
- imgbuf = StringIO.StringIO(r.content)
- img =Image.open(imgbuf)
是不是很简单很愉快?
六后记
通过以上的例子和代码, 大家可以初探 tesseract 的能力实际上 tesseract 支持学习, 通过样本训练, 可以大幅提高它的识别准确率, 当然这个过程就有点复杂了, 也不符合本文追求敏捷的宗旨经过多年的技术对抗, 传统的图片验证码已经显得过时了, 但仍有很多企业网站在大量使用, 希望本文能够给大家一些启发和帮助
来源: http://www.tuicool.com/articles/buiqMvY