前段時(shí)間寫基于俺們學(xué)校教學(xué)管理平臺(tái)的App,需要破解驗(yàn)證碼,模擬登陸,然后抓取數(shù)據(jù),顯示在Android端
驗(yàn)證碼破解的一般思路是下載驗(yàn)證碼,提取出需要的部分,平均拆分成N(N=驗(yàn)證碼字符個(gè)數(shù))份
二值化(轉(zhuǎn)化為黑白色,黑色為1,白色為0),取模,然后保存摸板
俺們學(xué)校的驗(yàn)證碼比較弱,只有0-9 10個(gè)數(shù)字,建好這十個(gè)數(shù)字的模型
在模擬登陸之前先把驗(yàn)證碼下載下來(lái),也是提取出需要的部分,拆分,然后與摸板進(jìn)行比較,這樣驗(yàn)證碼就能破解啦!
步驟總結(jié)如下:
(1)批量下載一部分驗(yàn)證碼圖片
(2)將這部分圖片提取出需要的部分
(3)將提取出來(lái)的部分平均拆分成N(N=驗(yàn)證碼字符個(gè)數(shù))份
(4) 去噪,將圖片灰度化與二值化
(5)提取每一個(gè)字符的特征,,生成特征矢量或特征矩陣
(6)分類與學(xué)習(xí)。將特征矢量或特征矩陣與樣本庫(kù)進(jìn)行比對(duì),,挑選出相似的那類樣本,將這類樣本的值作為輸出結(jié)果,。
下面借助代碼和圖片,具體講解步驟:
(1)批量下載一部分驗(yàn)證碼圖片
這里借助了Apache的http://hc./httpclient-3.x/
這個(gè)比較簡(jiǎn)單,,代碼就不貼了。所做的工作就是從 http://run./Account/GetValidateCode
下載了100張圖片,保存到checkcode文件夾,命名為code_i.jgp
如圖所示:
(2).將這部分圖片提取出需要的部分
用windows自帶的畫圖工具編輯圖片,縮放到最大,如下圖所示
可觀察到周圍有很多空白像素點(diǎn),這些都是不需要的.裁剪出需要的部分,注意:要確保裁剪之后能平均裁剪為4等分
代碼如下:
1 | public static BufferedImage getSingleCode(BufferedImage image) { |
2 | return image.getSubimage( 6 , 5 , 36 , 12 ); |
裁剪之后效果如下:
(3)將提取出來(lái)的部分平均拆分成N(N=驗(yàn)證碼字符個(gè)數(shù))份
我需要破解的驗(yà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的值,,然后根據(jù)黑白化的公式R*R +G*G +B*B < 3*128*128為黑色,,否則為白色,這種方法對(duì)于絕大多數(shù)是有效的,。
還有一種是根據(jù)灰度圖,,然后在根據(jù)灰度來(lái)確定是黑還是白。
像素點(diǎn)灰度的公式:
1.浮點(diǎn)算法:Gray=R*0.3+G*0.59+B*0.11
2.整數(shù)方法: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
可以根據(jù)需要做出微調(diào)
本例采用黑白化公式來(lái)黑白化
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)提取每一個(gè)字符的特征,,生成特征矢量或特征矩陣
代碼如下
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 ); |
經(jīng)過(guò)步驟4和5之后得到如下效果,這就是我們需要的模型,!
(6)分類與學(xué)習(xí),。將特征矢量或特征矩陣與樣本庫(kù)進(jìn)行比對(duì),挑選出相似的那類樣本,,將這類樣本的值作為輸出結(jié)果,。
根據(jù)圖片模型得到數(shù)字模型。我所得到的模型如下:
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 }, } }; |
驗(yàn)證碼識(shí)別代碼:
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(); |
下面是識(shí)別效果我所得到的模型為9*12像素,,包括一些雜點(diǎn),,但是雜點(diǎn)不太影響圖片識(shí)別,,雜點(diǎn)只是少數(shù)幾個(gè)點(diǎn)
取一個(gè)臨界值,,如果能匹配的像素點(diǎn)達(dá)到這個(gè)值,,則代表識(shí)別成功。我取的值為90
親測(cè)識(shí)別率為100%
當(dāng)然,這是因?yàn)槲覀儗W(xué)校教學(xué)管理平臺(tái)的驗(yàn)證碼太過(guò)于規(guī)則,沒(méi)有太多的雜點(diǎn)和扭曲,也沒(méi)有任何的交叉,所以才能破解成功.
本人水平有限,只能破解這樣簡(jiǎn)單的驗(yàn)證碼,大致思路就是這樣的,over!
博文地址 http://veryyoung.sinaapp.com/?p=134
github地址 https://github.com/veryyoung/CrashVeryCode
由最代碼官方編輯于2013-12-30 23:42:58
|