您的位置:首页 > 其它

自动识别图形验证码

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



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



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



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



可以看出 一个数字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;




/**


 *   登陆验证图片转换为数字


 *


 *


 * @version    1.0, 08/04/20


 * @author     张健滢


 */


public class ImgIdent {




    // 数字字符比特表


    private final long[][] NUMERIC = {


        { 512104545, 562436190 },    // '0'


        { 148931080, 136348222 },    // '1'


        { 511971394, 69273663 },     // '2'


        { 511971406, 17045598 },     // '3'


        { 35168914, 586948743 },     // '4'


        { 1065486398, 17045598 },    // '5'


        { 239208494, 830871646 },    // '6'


        { 1065623684, 69239824 },    // '7'


        { 512104542, 562436190 },    // '8'


        { 512104547, 486805660 }


    };                               // '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;




    /**


     * Constructs ...


     *


     *


     * @param img


     *


     * @throws IOException


     */


    public ImgIdent(BufferedImage img) throws IOException {


        this.img = img;


        init();


    }




    /**


     *   构造函数


     *   @param   file     本地文件


     *   @throws   IOException


     */


    public ImgIdent(File file) throws IOException {


        img = ImageIO.read(file);


        init();


    }




    /**


     *   构造函数


     *   @param   url    远程文件


     *   @throws   IOException


     */


    public ImgIdent(URL url) throws IOException {


        img = ImageIO.read(url);


        init();


    }




    /**


     *   类初始工作


     */


    private void init() {




        // 得到图象的长度和宽度


        intImgWith   = img.getWidth();


        IntImgHeight = img.getHeight();




        // 得到图象的背景颜色


        intBgColor = img.getRGB(7, 4);




        // System.out.println(intBgColor);




        // 初始化图象原点座标


        pOrigin = new Point(0, 0);


    }




    /**


     * Method description


     *


     */


    private void getBaseInfo() {


        System.out.println(intBgColor + "|" + intCharColor);


        System.out.println(intMinX + "|" + intMinY + "|" + intMaxX + "|" + intMaxY);


    }




    /**


     *   得到字符的左上右下点座标


     *   @param   intNo   int                                   第n个字符


     *   @return   int[]


     */


    private Point[] getCharRange(int intNo) {




        // 左上右下点座标


        Point pTopLeft     = new Point(0, 0);


        Point pBottomRight = new Point(0, 0);




        // 左上点


        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;




        return new Point[] { pTopLeft, pBottomRight };


    }




    /**


     *   与背景颜色比较返回相应的字符


     *   @param   x   int                                           横座标


     *   @param   y   int                                           纵座标


     *   @return   char                                           返回字符


     */


    private char getBit(int x, int y) {


        int intCurtColor;




        intCurtColor = img.getRGB(x, y);




        // System.out.println("[" + x + "," + y + "]" + intCurtColor + "==" + intBgColor + "==>" + (Math.abs(intCurtColor) >7308252));


//      return (Math.abs(intCurtColor) >= 5689325)


//              ? '0'


//              : '1';


        return (intCurtColor == intBgColor)


               ? '0'


               : '1';




        // 5689325    6008535


    }




    /**


     *   得到第n个字符对应的字符串


     *   @param   intNo   int                                   第n个字符


     *   @return   String                                       代表字符位的串


     */


    private String getCharString(int intNo) {




        // 本字符的左上右下点座标


        Point[] p            = 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 = "";




        for (i = intY1; i <= intY2; i++) {


            for (j = intX1; j <= intX2; j++) {


                System.out.print(getBit(j, i));


                strChar = strChar + getBit(j, i);


            }




            System.out.println();


        }




        System.out.println();




        return strChar;


    }




    /**


     *   得到第n个字符对应数值


     *   @param   intNo   int                                   第n个字符


     *   @return   int                                             对应数值


     */


    public int getNum(int intNo) {




        // 取得位字符串


        String strChar = getCharString(intNo);




        // System.out.println(intNo+"=="+strChar);


        // 取得串高位串和低位串


        String strCharHigh = strChar.substring(0, strChar.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 = '*';




        for (int i = 0; i <= 9; i++) {


            if ((lCharHigh == NUMERIC[i][0]) && (lCharLow == NUMERIC[i][1])) {


                intNum = i;




                break;


            } else {


                if ((lCharHigh == 834533329) && (lCharLow == 242870177)) {


                    intNum = 6;


                }    // 834533329 242870177


                        else {


                    intNum = 1;


                }    // 默认为1   低位为    937393609  937393601


            }


        }




        return intNum;


    }




    /**


     * 保存图片


     *


     *


     * @param length


     *


     * @return


     */


    public String getValidatecode(int length) {


        String strNum = "";




        for (int i = 1; i <= length; i++) {


            synchronized (this) {


                strNum += String.valueOf(getNum(i));


            }


        }




        return strNum;


    }




    /**


     * Method description


     *


     *


     * @param iag


     * @param savePath


     *


     * @throws FileNotFoundException


     * @throws IOException


     */


    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((float) 1, true);


        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;






public class MyHttpClient {




    /**


     * Method description


     *


     *


     * @param title 留言标题


     * @param name 留言者


     * @param Content 内容


     * @param proIP 代理IP


     * @param port  代理端口


     * @param usePro 是否使用代理


     */


    public synchronized void doSomeThing(String title, String name, String Content, String proIP, int port,


            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)    // 使用代理


        {


            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());




        try {




            // 执行getMethod


            int statusCode = httpClient.executeMethod(getMethod);




            // System.out.println(statusCode);


            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);


        } catch (Exception e) {




            // 发生网络异常


            e.printStackTrace();


        } finally {}




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


        getMethod.releaseConnection();


    }


}



恩 就这样了,最后结合SAF整成这样了。什么?为什么不用SWT?想过了SWING才是王道o(∩_∩)o...

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: