加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

[Java] 识别图片验证码

(2011-01-02 18:14:47)
标签:

杂谈

分类: Java
自动识别图形验证码

 

 


 
现在大多数网站都采用了验证码来防止暴力破解或恶意提交。但验证码真的就很安全吗?真的就不能被机器识别??
我先讲讲我是怎么实现站外提交留言到一个网站的程序。
这个网站的留言版大致如下:

http://www.java3z.com/cwbwebhome/article/article2/img7/liuyanbanLT.jpg识别图片验证码" />


我一看这种简单的4位数字验证码,马上就感觉到有戏了。直觉告诉我让电脑来识别这些图片验证码据对简单o(∩_∩)o...
首先我马上在这个页面用右键菜单看源代码
http://www.java3z.com/cwbwebhome/article/article2/img7/32112.jpg识别图片验证码" />

知道验证码获取页面后 你可以直接用 http://www.XXXX.com/imgchk/validatecode.asp 这样去访问你会发现你打开的就是一个验证码图片。

http://www.java3z.com/cwbwebhome/article/article2/img7/yanzhengma_2022.jpg识别图片验证码" />


对的其实返回的就是图片文件的2进制流而已。接着先用右键保存一张验证码的图片。因为要开始分析这张图片了,什么用什么工具?PhotoShop????不用就一般的画图工具就可以了。我们要搞清楚的是这几个数字分别占几个像素就可以了。

http://www.java3z.com/cwbwebhome/article/article2/img7/kuantu.jpg识别图片验证码" />


可以看出 一个数字5*9  也就是45个像素。恩 这就可以了 另外我们可以看出默认区域就是白色
(姑且说是白色因为我们肉眼看就是白色)
那么我的程序识别原理就是固定去扫描这45个像素点。看每个点的颜色是不是和默认的颜色一致
一致的话就标记为0 ,不一致就标记为1 。
如一个数子是2 那么我的程序扫描出来的图像就应该是:
011110
100001
000001
000001
000010
000100
001000
010000
100000
111111
如果一个数字是7那么扫描出来的图像就是:
111111
100001
000010
000010
000100
000100
001000
001000
010000
010000

恩,就这么简单呵呵。下面给出图像 扫描的java类 (不好意思,在我会的语言里面除开java就剩sql了)



package com.util;

//~--- JDK imports ------------------------------------------------------------

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import java.awt.*;
import java.awt.image.*;

import java.io.*;
import java.io.FileOutputStream;
import java.io.OutputStream;

import java.net.*;

import javax.imageio.*;
import javax.imageio.ImageIO;

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedBlock.gif识别图片验证码" />

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedBlock.gif识别图片验证码" />
public class ImgIdent ...{

    
// 数字字符比特表
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
    private final long[][] NUMERIC = ...{
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
...512104545562436190 }   // '0'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...148931080136348222 }   // '1'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...51197139469273663 }    // '2'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...51197140617045598 }    // '3'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...35168914586948743 }    // '4'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...106548639817045598 }   // '5'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...239208494830871646 }   // '6'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...106562368469239824 }   // '7'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...512104542562436190 }   // '8'
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...512104547486805660 }
    }
                              // '9'

    
// 字框高
    private int intCharHeight = 10;

    
// 字框横向间隙
    private int intCharSpaceH = 5;

    
// 字框纵向间隙
    private int intCharSpaceY = 1;

    
// 字框宽
    private int           intCharWidth = 5;
    
private int           IntImgHeight;
    
private BufferedImage img;
    
private int           intBgColor;
    
private int           intCharColor;
    
private int           intImgWith;
    
private int           intMaxX;
    
private int           intMaxY;
    
private int           intMinX;
    
private int           intMinY;

    
// 座标原点
    private Point  pOrigin;
    
private String strNum;

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public ImgIdent(BufferedImage img) throws IOException ...{
        
this.img = img;
        init();
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public ImgIdent(File file) throws IOException ...{
        img 
= ImageIO.read(file);
        init();
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public ImgIdent(URL url) throws IOException ...{
        img 
= ImageIO.read(url);
        init();
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
private void init() ...{

        
// 得到图象的长度和宽度
        intImgWith   = img.getWidth();
        IntImgHeight 
= img.getHeight();

        
// 得到图象的背景颜色
        intBgColor = img.getRGB(74);

        
// System.out.println(intBgColor);

        
// 初始化图象原点座标
        pOrigin = new Point(00);
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
private void getBaseInfo() ...{
        System.out.println(intBgColor 
+ "|" + intCharColor);
        System.out.println(intMinX 
+ "|" + intMinY + "|" + intMaxX + "|" + intMaxY);
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
private Point[] getCharRange(int intNo) ...{

        
// 左上右下点座标
        Point pTopLeft     = new Point(00);
        Point pBottomRight 
= new Point(00);

        
// 左上点
        pTopLeft.x = pOrigin.x + intCharWidth * (intNo - 1+ intCharSpaceH * (intNo - 1);
        pTopLeft.y 
= pOrigin.y;

        
// 右下点
        pBottomRight.x = 1 + pOrigin.x + intCharWidth * intNo + intCharSpaceH * (intNo - 1- 1;
        pBottomRight.y 
= pOrigin.y + intCharHeight - 1;

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
return new Point[] ...pTopLeft, pBottomRight };
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
private char getBit(int x, int y) ...{
        
int intCurtColor;

        intCurtColor 
= img.getRGB(x, y);

        
//System.out.println("[" "," "]" intCurtColor "==" intBgColor "==>" (Math.abs(intCurtColor) >7308252));
//      return (Math.abs(intCurtColor) >= 5689325)
//              '0'
//              '1';
        return (intCurtColor == intBgColor)
               
? '0'
               
'1';

        
// 5689325    6008535
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
private String getCharString(int intNo) ...{

        
// 本字符的左上右下点座标
        Point[]            = getCharRange(intNo);
        Point   pTopLeft     
= p[0];
        Point   pBottomRight 
= p[1];

        
// 换算边界值
        int intX1, intY1, intX2, intY2;

        intX1 
= pTopLeft.x;
        intY1 
= pTopLeft.y;
        intX2 
= pBottomRight.x;
        intY2 
= pBottomRight.y;

//      System.out.println("intX1=" intX1);
//      System.out.println("intY1=" intY1);
//      System.out.println("intX2=" intX2);
//      System.out.println("intY2=" intY2);

        
// 在边界内循环取象素
        int    i, j;
        String strChar 
= "";

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
for (i = intY1; <= intY2; i++...{
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />             
for (j = intX1; <= intX2; j++...{
                System.out.print(getBit(j, i));
                strChar 
= strChar + getBit(j, i);
            }


            System.out.println();
        }


        System.out.println();

        
return strChar;
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public int getNum(int intNo) ...{

        
// 取得位字符串
        String strChar = getCharString(intNo);

        
// System.out.println(intNo+"=="+strChar);
        
// 取得串高位串和低位串
        String strCharHigh = strChar.substring(0strChar.length() / 2);
        String strCharLow  
= strChar.substring(strChar.length() / 2);

        
// 计算高位和低位值
        long lCharHigh = Long.parseLong(strCharHigh, 2);

        System.out.println(lCharHigh);

        
long lCharLow = Long.parseLong(strCharLow, 2);

        System.out.println(lCharLow);

        
// 在数字中循环比较
        int intNum = '*';

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
for (int = 0<= 9i++...{
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />             
if ((lCharHigh == NUMERIC[i][0]) && (lCharLow == NUMERIC[i][1])) ...{
                intNum 
= i;

                
break;
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />             }
 else ...{
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />                 
if ((lCharHigh == 834533329&& (lCharLow == 242870177)) ...{
                    intNum 
= 6;
                }
    // 834533329 242870177
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
                        else ...{
                    intNum 
= 1;
                }
    // 默认为1   低位为    937393609  937393601
            }

        }


        
return intNum;
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public String getValidatecode(int length) ...{
        String strNum 
= "";

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
for (int = 1<= length; i++...{
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />             
synchronized (this...{
                strNum 
+= String.valueOf(getNum(i));
            }

        }


        
return strNum;
    }


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     
public void saveJPEG(BufferedImage iag, String savePath) throws FileNotFoundException, IOException ...{
        OutputStream     jos     
= new FileOutputStream(savePath);
        JPEGImageEncoder encoder 
= JPEGCodec.createJPEGEncoder(jos);
        JPEGEncodeParam  jpegEP  
= JPEGCodec.getDefaultJPEGEncodeParam(iag);

        jpegEP.setQuality((
float1true);
        encoder.encode(iag, jpegEP);
        jos.flush();
        jos.close();
    }

}



恩这样数字是可以识别出来了,可以我要怎么完成提交那块的工作呢?好在Apache已经为我做完了。我用了
HttpClient这样一个模拟Http协议的小工具包。我只要往那个 Add_MSG.asp里面提交就完了。

package com.util;

//~--- non-JDK imports --------------------------------------------------------

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;

//~--- JDK imports ------------------------------------------------------------

import java.awt.image.BufferedImage;

import java.io.InputStream;

import javax.imageio.ImageIO;


http://www.java3z.com/cwbwebhome/article/article2/img/ContractedBlock.gif识别图片验证码" />
public class MyHttpClient ...{

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />     

    
public synchronized void doSomeThing(String title, String name, String Content, String proIP, int port,
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />             
boolean usePro) ...{

        
// 构造HttpClient的实例
        HttpClient       httpClient   = new HttpClient();
        HttpClientParams clientParams 
= new HttpClientParams();

        
// 隐藏自己请求相关的信息
        clientParams.setParameter("http.useragent""Mozilla/4.0 (compatible; FIREFOX 9.0; IBM AIX 5)");

        
// httpClient.getHttpConnectionManager().getParams().setSoTimeout(30 1000);
        clientParams.setHttpElementCharset("GBK");

        HttpState httpState 
= new HttpState();

        httpClient.setParams(clientParams);
        httpClient.getParams().setParameter(HttpClientParams.HTTP_CONTENT_CHARSET, 
"GBK");
        httpClient.setState(httpState);
        clientParams.setVersion(HttpVersion.HTTP_1_1);

        
// httpClient.getHostConfiguration().setProxy("148.233.159.58", 3128);

        
if (usePro)    // 使用代理
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        ...{
            httpClient.getHostConfiguration().setProxy(proIP, port);
        }


        
// 创建GET方法的实例
        GetMethod getMethod = new GetMethod("http://www.XXXcom/Guestbook/imgchk/validatecode.asp");

        
// 使用系统提供的默认的恢复策略
        getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());

http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         
try ...{

            
// 执行getMethod
            int statusCode = httpClient.executeMethod(getMethod);

            
// System.out.println(statusCode);
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
            if (statusCode != HttpStatus.SC_OK) ...{
                System.err.println(
"Method failed: " + getMethod.getStatusLine());
            }
    // 读取内容

            InputStream inStream 
= getMethod.getResponseBodyAsStream();

            
// 处理内容
            
// System.out.println(new String(responseBody));
            BufferedImage iag      = ImageIO.read(inStream);
            ImgIdent      imgIdent 
= new ImgIdent(iag);

            
// imgIdent.saveJPEG(iag, "C:/ddd.jpg");
            String validate = imgIdent.getValidatecode(4);

            System.out.println(validate);

            PostMethod method  
= new PostMethod("http://www.XXX.com/Guestbook/add_msg.asp");
            String     connect 
= Content;
            String     Title   
= title;

            method.setParameter(
"subject"Title);
            method.setParameter(
"g_name"name);
            method.setParameter(
"companyname""");
            method.setParameter(
"mail""");
            method.setParameter(
"homepageurl""http://");
            method.setParameter(
"pic""p5.gif");
            method.setParameter(
"validatecode"validate);
            method.setParameter(
"content"connect);

//          if (todo) {
            int code = httpClient.executeMethod(method);

            
// String Stringresponse new String(method.getResponseBodyAsString().getBytes("8859_1"));
            
// 打印返回的信息
            
// System.out.println(Stringresponse);
//          }

            method.releaseConnection();

//          System.out.println(iag.getHeight());
//          System.out.println(iag.getWidth());
//          //背景 颜色
//          intBgColor iag.getRGB(38, 0);
//          System.out.println("intBgColor=" intBgColor);
//
//
//          intBgColor iag.getRGB(0, 0);
//          System.out.println("intBgColor=" intBgColor);
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />
        }
 catch (Exception e) ...{

            
// 发生网络异常
            e.printStackTrace();
http://www.java3z.com/cwbwebhome/article/article2/img/ContractedSubBlock.gif识别图片验证码" />         }
 finally ...{}

        
// 释放连接   getMethod.releaseConnection();  }
        getMethod.releaseConnection();
    }

}



恩 就这样了,最后结合SAF整成这样了。什么?为什么不用SWT?想过了SWING才是王道o(∩_∩)o...
http://www.java3z.com/cwbwebhome/article/article2/img7/jieguo.jpg识别图片验证码" />


0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有