抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

原文地址:http://blog.csdn.net/ugg/article/details/3953137
但是原文的介绍比较简单,而且没提到算法的具体实现过程。
所以本文以一个实际例子来演示php识别验证码的过程,并提交验证码到服务器验证。

一、验证码识别介绍

近期研究一些突破验证码方面的知识,记录下来。一方面算是对这几天学习知识的总结帮助自己理解;另一方面希望对研究这方面的技术同学有所帮助;另外也希望引起网站管理者的注意,在提供验证码时多些考虑进去。由于刚刚接触这方面的知识,理解比较浅显,有错误再所难免,欢迎拍砖。

验证码的作用:有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。其实现代的验证码一般是防止机器批量注册的,防止机器批量发帖回复。目前,不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。

所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。

我们最常见的验证码:
1、四位数字,随机的一数字字符串,最原始的验证码,验证作用几乎为零。
2、随机数字图片验证码。图片上的字符比较中规中矩,有的可能加入一些随机干扰素,还有一些是随机字符颜色,验证作用比上一个好。没有基本图形图像学知识的人,不可破!
3、各种图片格式的随机数字+随机大写英文字母+随机干扰像素+随机位置。 
4、汉字是注册目前最新的验证码,随机生成,打起来更难了,影响用户体验,所以,一般应用的比较少。

简单起见,我们这次说明的主要对象是第1种类型的,我们先看几种网上比较常见的验证码图片。
This is a picture without description

这四种样式,基本上能代表2中所提到的验证码类型,初步看起来第一个图片最容易破解,第二个次之,第三个更难,第四个最难。
真实情况呢?其实这三种图片破解难度相同。

第一个图片,最容易,图片背景和数字都使用相同的颜色,字符规整,字符位置统一。本篇文章,就一这种类型验证码为例说明,其它的图片,同学们自己搞。
第二个图片,看似不容易,其实仔细研究会发现其规则,背景色和干扰素无论怎么变化,验证字符字符规整,颜色相同,所以排除干扰素非常容易,只要是非字符色素全部排除即可。
第三个图片,看似更复杂,处理上面提到背景色和干扰素一直变化外,验证字符的颜色也在变化,并且各个字符的颜色也各不相同。
第四个图片,除了第三个图片上提到的特征外,又在文字上加了两条直线干扰率,看似困难其实,很容易去掉。

二、实践例子

下面以万网的“通用网址查询”来说明验证码的识别过程。

打开万网:http://www.net.cn,网站右边侧边栏有一个“通用网址查询”:
This is a picture without description

可以看出,这是第一种验证码,为了让人眼能够识别出数字,所以验证码图片的数字颜色和背景颜色的色差是比较大的,所以其RBG值也相差很大,可以通过判断每个像素的RGB值来区分数字和背景。

三、验证码识别步骤

1、取出字模

识别验证码,毕竟不是专业的OCR识别,并且,由于各个网站的验证码各不相同,所以,最常见的方法就是就是建立这个验证码的特征码库。去字模时,我们需要多下载几张图片,使这些图片中,包括所有的字符,我们这里的图片里只有数字,所以,只要收集到包括0-9的数字图片即可。

(1)、多刷新几次验证码,将验证码图片保存起来,要搜集齐0-9的图片。
This is a picture without description

(2)、用图片处理软件打开图片,我用的是Fireworks,按住ctrl+8可以将图片的视图放大8倍,这样就能很清楚地观察到图片的每个像素。
This is a picture without description

可以发现,每个数字的宽是6px,高是10px,数字的间隔是4px,第一个数字左边偏移了2px,顶部偏移了0px。这些数字后面都是要用到的。

(3)、将每个数字截出来保存为图片,大小为6*10。
This is a picture without description

2、图片二值化

二值化就是把图片上的验证数字上每个象素用数字1表示,其它部分用0表示。把要识别的图片,进行二值化,将数据保存到二维数组里,得到图片特征数组。

(1)、首先要将数字和背景色和干扰色区分开来,用屏幕取色器观察颜色的规律。
This is a picture without description

可以得出一个结论:背景颜色的R、G、B值都是大于200的,而数字的颜色的R、G、B值的某一项有可能小于200,因此可以很容易区分。

(2)、下面的php代码只是为了演示二维数组,为了直观看出数字,所以把1和0改为了0和-:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
echo '<br><img src="v1.jpg"><br><br>';

getHec("v1.jpg");

function getHec($imagePath) {
$res = imagecreatefromjpeg($imagePath);
$size = getimagesize($imagePath);

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'] < 200 || $rgbarray['green']<200 || $rgbarray['blue'] < 200) {
echo "0";
}else{
echo "-";
}
}
echo "<br>";
}
}

结果如下图所示:
This is a picture without description

如果图片的背景颜色比较复杂,处理方法也是一样的,总能找到临界值来区分,具体要靠自己观察了。

3、数字字模二值化

计算出每个数字字模的二值化的数据,记录下这些数据,当作key即可。

(1)、将0-9的数字字模图片进行二值化,逐个取出图片的像个像素的颜色,然后获取每个像素的R、G、B值,再进行判断,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for ($i = 0; $i < 10; $i++) {
echo "'$i'=>'";
echo getHec("$i.jpg")."',<br>";
}

function getHec($imagePath) {
$res = imagecreatefromjpeg($imagePath);
$size = getimagesize($imagePath);

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'] < 200 || $rgbarray['green']<200 || $rgbarray['blue'] < 200) {
echo "1";
}else{
echo "0";
}
}
}
}

输出结果为:

1
2
3
4
5
6
7
8
9
10
'0' => '011110100001100001100001100001100001100001100001100001011110',
'1' => '001000111000001000001000001000001000001000001000001000111110',
'2' => '011110100001100001000001000010000100001000010000110011111111',
'3' => '011110100001100001000010001100000010000001100001100001011110',
'4' => '000100000100001100010100100100100100111111000100001100001111',
'5' => '111111100000100000101110110001000001000001100001100001011110',
'6' => '001110010001100000100000101110110001100001100001100001011110',
'7' => '111111100010100010000100000100001000001000001000001000001000',
'8' => '011110100001100001100001011110010010100001100001100001011110',
'9' => '011100100010100001100001100011011101000001000001100010011100',

4、对照样本

把步骤二中的图片特征码和步骤三中的验证码的字模进行对比,得到验证图片上的数字。

算法过程(代码见附件):
(1)、将图片二值化后的值保存到二维数组里。
(2)、通过循环,求出每一个数字的位置,要用到前面得到的数字的宽、高、间隔、左边偏移、顶部偏移。
例如:第i个数字左边偏移 =(数字宽 + 间隔)* i + 左边偏移。
(3)、知道了数字的偏移位置,就可以计算出数字在二维数组里的位置,通过循环将数字的6*10=60个数据取出来拼接在一起,就形成了与数字字模类似的字符串。
(4)、将字符串与每一个字模的字符串比较,求其相似度,取最高的相似度对应的数字,或者相似度达到95%以上就可以断定是某个数字。
(5)、识别结果如下:
This is a picture without description

使用目前这种方法,对验证码的识别基本上可以做到100%。
通过以上步骤,您可能说了,并没有发现如何取出干扰素啊!其实取出干扰素的方法很简单,干扰素的一个重要特征是,不能影响验证码的显示效果,所以制作干扰素时它的RGB可能低于或者高于某个特定值,比如我给的例子中的图片,干扰素的RGB各项值是不会小于200的,所以,这样我们就很容易去掉干扰素了。

第二部分的内容还是挺多的,所以另开帖子了,详情见 《PHP识别验证码后提交》

四、源码下载

点击下载源码

评论