前段时间写基于俺们学校教学管理平台的App,需要破解验证码,模拟登陆,然后抓取数据,显示在Android端
验证码破解的一般思路是下载验证码,提取出需要的部分,平均拆分成N(N=验证码字符个数)份
二值化(转化为黑白色,黑色为1,白色为0),取模,然后保存摸板
俺们学校的验证码比较弱,只有0-9 10个数字,建好这十个数字的模型
在模拟登陆之前先把验证码下载下来,也是提取出需要的部分,拆分,然后与摸板进行比较,这样验证码就能破解啦!
步骤总结如下:
(1)批量下载一部分验证码图片
(2)将这部分图片提取出需要的部分
(3)将提取出来的部分平均拆分成N(N=验证码字符个数)份
(4) 去噪,将图片灰度化与二值化
(5)提取每一个字符的特征,生成特征矢量或特征矩阵
(6)分类与学习。将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。
下面借助代码和图片,具体讲解步骤:
(1)批量下载一部分验证码图片
这里借助了Apache的http://hc./httpclient-3.x/
这个比较简单,代码就不贴了。所做的工作就是从 http://run./Account/GetValidateCode
下载了100张图片,保存到checkcode文件夹,命名为code_i.jgp
如图所示:

(2).将这部分图片提取出需要的部分
用windows自带的画图工具编辑图片,缩放到最大,如下图所示

可观察到周围有很多空白像素点,这些都是不需要的.裁剪出需要的部分,注意:要确保裁剪之后能平均裁剪为4等分
代码如下:
1 | public static BufferedImage getSingleCode(BufferedImage image) { |
2 | return image.getSubimage( 6 , 5 , 36 , 12 ); |
裁剪之后效果如下:

(3)将提取出来的部分平均拆分成N(N=验证码字符个数)份
我需要破解的验证码为4位,所以需平均裁剪为4等份
代码如下:
01 | public static BufferedImage[] getCheckCodes(BufferedImage image) { |
02 | BufferedImage checkCode[] = new BufferedImage[ 4 ]; |
03 | int height = image.getHeight(); |
04 | int width = image.getWidth(); |
05 | int x = 0 * (width / checkCode.length); |
07 | int w = width / checkCode.length; |
09 | checkCode[ 0 ] = image.getSubimage(x, y, w, h); |
10 | checkCode[ 1 ] = image.getSubimage( 1 * (width / checkCode.length), 0 , |
11 | width / checkCode.length, height); |
12 | checkCode[ 2 ] = image.getSubimage( 2 * (width / checkCode.length), 0 , |
13 | width / checkCode.length, height); |
14 | checkCode[ 3 ] = image.getSubimage( 3 * (width / checkCode.length), 0 , |
15 | width / checkCode.length, height); |
效果如下
(4) 去噪,将图片灰度化与二值化
图片黑白化原理:
获取到R、G、B的值,然后根据黑白化的公式R*R +G*G +B*B < 3*128*128为黑色,否则为白色,这种方法对于绝大多数是有效的。
还有一种是根据灰度图,然后在根据灰度来确定是黑还是白。
像素点灰度的公式:
1.浮点算法:Gray=R*0.3+G*0.59+B*0.11
2.整数方法:Gray=(R*30+G*59+B*11)/100
3.移位方法:Gray =(R*28+G*151+B*77)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿色:Gray=G;
参考:http://baike.baidu.com/view/1184366.html
可以根据需要做出微调
本例采用黑白化公式来黑白化
01 | public static int pixelConvert( int pixel) { |
04 | int r = (pixel >> 16 ) & 0xff ; |
05 | int g = (pixel >> 8 ) & 0xff ; |
06 | int b = (pixel) & 0xff ; |
09 | int tmp = r * r + g * g + b * b; |
10 | if (tmp > 3 * 128 * 128 ) { |
public class Filter {(5)提取每一个字符的特征,生成特征矢量或特征矩阵
代码如下
01 | public static void blackAndWhiteFilter(BufferedImage image) { |
06 | for ( int i = 0 ; i < image.getHeight(); i++) { |
07 | for ( int j = 0 ; j < image.getWidth(); j++) { |
08 | image.setRGB(j, i, Tools.pixelConvert(image.getRGB(j, i))); |
13 | public static void dotFilter(BufferedImage image) { |
18 | for ( int i = 0 ; i < image.getHeight(); i++) { |
19 | for ( int j = 0 ; j < image.getWidth(); j++) { |
20 | if (i > 0 && j > 0 && i < (image.getHeight() - 1 ) |
21 | && j < (image.getWidth() - 1 )) { |
22 | if (image.getRGB(j, i) == 0xff000000 ) { |
23 | if (image.getRGB(j - 1 , i) == 0xffffffff |
24 | && image.getRGB(j - 1 , i - 1 ) == 0xffffffff |
25 | && image.getRGB(j, i - 1 ) == 0xffffffff |
26 | && image.getRGB(j + 1 , i) == 0xffffffff |
27 | && image.getRGB(j + 1 , i + 1 ) == 0xffffffff |
28 | && image.getRGB(j, i + 1 ) == 0xffffffff ) { |
29 | image.setRGB(j, i, 0xffffffff ); |
经过步骤4和5之后得到如下效果,这就是我们需要的模型!
(6)分类与学习。将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。
根据图片模型得到数字模型。我所得到的模型如下:
01 | private static final int [][][] model = { { // 0 |
02 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
03 | { 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 0 , 0 , 1 , 1 }, |
04 | { 1 , 1 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, |
05 | { 1 , 0 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 }, |
06 | { 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 0 }, |
07 | { 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 }, |
08 | { 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 }, |
09 | { 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
10 | { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 }, }, { // 1 |
11 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
12 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
13 | { 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
14 | { 1 , 1 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
15 | { 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 }, |
16 | { 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 }, |
17 | { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 }, |
18 | { 0 , 1 , 0 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 }, |
19 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 2 |
20 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
21 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 }, |
22 | { 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 }, |
23 | { 1 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 }, |
24 | { 1 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 }, |
25 | { 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 }, |
26 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 1 , 0 , 0 }, |
27 | { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 }, |
28 | { 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 3 |
29 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 }, |
30 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 }, |
31 | { 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 }, |
32 | { 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 }, |
33 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 }, |
34 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 }, |
35 | { 0 , 0 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 1 }, |
36 | { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
37 | { 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 4 |
38 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 }, |
39 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 }, |
40 | { 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 }, |
41 | { 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 0 , 0 , 1 , 1 }, |
42 | { 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 }, |
43 | { 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 }, |
44 | { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, |
45 | { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 }, |
46 | { 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 }, }, { // 5 |
47 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
48 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 }, |
49 | { 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 0 }, |
50 | { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 }, |
51 | { 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 }, |
52 | { 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 }, |
53 | { 0 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 }, |
54 | { 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
55 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 }, }, { // 6 |
56 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
57 | { 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
58 | { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 }, |
59 | { 1 , 0 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 }, |
60 | { 0 , 0 , 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 }, |
61 | { 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 1 , 0 }, |
62 | { 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 }, |
63 | { 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
64 | { 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 7 |
65 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
66 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
67 | { 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 }, |
68 | { 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 }, |
69 | { 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 }, |
70 | { 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 }, |
71 | { 0 , 0 , 1 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 }, |
72 | { 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
73 | { 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 8 |
74 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
75 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 0 , 1 }, |
76 | { 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }, |
77 | { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 }, |
78 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 }, |
79 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 0 }, |
80 | { 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 }, |
81 | { 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 1 }, |
82 | { 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, }, { // 9 |
83 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }, |
84 | { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 }, |
85 | { 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 }, |
86 | { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 }, |
87 | { 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 }, |
88 | { 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 0 , 0 , 0 }, |
89 | { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 0 , 0 , 0 , 1 }, |
90 | { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 }, |
91 | { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 }, } }; |
验证码识别代码:
01 | public static String compare(BufferedImage image) { |
02 | BufferedImage checkCode[] = Tools.getCheckCodes(image); |
03 | StringBuffer code = new StringBuffer(); |
04 | for ( int t = 0 ; t < 4 ; t++) { |
05 | int [] result = new int [ 10 ]; |
06 | boolean ckFlg = false ; |
08 | for ( int i = 0 ; i < 10 ; i++) { |
11 | for ( int x = 0 ; x < checkCode[t].getWidth(); x++) { |
12 | for ( int y = 0 ; y < checkCode[t].getHeight(); y++) { |
13 | int expRGB = Tools.pixelConvert(checkCode[t].getRGB(x, |
15 | int cmpRGB = model[i][x][y]; |
16 | if (expRGB == cmpRGB) { |
36 | return code.toString(); |
下面是识别效果我所得到的模型为9*12像素,包括一些杂点,但是杂点不太影响图片识别,杂点只是少数几个点
取一个临界值,如果能匹配的像素点达到这个值,则代表识别成功。我取的值为90

亲测识别率为100%
当然,这是因为我们学校教学管理平台的验证码太过于规则,没有太多的杂点和扭曲,也没有任何的交叉,所以才能破解成功.
本人水平有限,只能破解这样简单的验证码,大致思路就是这样的,over!
博文地址 http://veryyoung.sinaapp.com/?p=134
github地址 https://github.com/veryyoung/CrashVeryCode
由最代码官方编辑于2013-12-30 23:42:58
|