[Java] 識別圖片驗證碼


現在大多數網站都采用了驗證碼來防止暴力破解或惡意提交。但驗證碼真的就很安全嗎?真的就不能被機器識別??
我先講講我是怎么實現站外提交留言到一個網站的程序。
這個網站的留言版大致如下:




我一看這種簡單的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;


public   class  ImgIdent  {

    
// 數字字符比特表
    private final long[][] NUMERIC = {
        
512104545562436190 },    // '0'
        148931080136348222 },    // '1'
        51197139469273663 },     // '2'
        51197140617045598 },     // '3'
        35168914586948743 },     // '4'
        106548639817045598 },    // '5'
        239208494830871646 },    // '6'
        106562368469239824 },    // '7'
        512104542562436190 },    // '8'
        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;

    

    
public ImgIdent(BufferedImage img) throws IOException {
        
this.img = img;
        init();
    }


    

    
public ImgIdent(File file) throws IOException {
        img 
= ImageIO.read(file);
        init();
    }


    

    
public ImgIdent(URL url) throws IOException {
        img 
= ImageIO.read(url);
        init();
    }


    

    
private void init() {

        
// 得到圖象的長度和寬度
        intImgWith   = img.getWidth();
        IntImgHeight 
= img.getHeight();

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

        
// System.out.println(intBgColor);

        
// 初始化圖象原點座標
        pOrigin = new Point(00);
    }


    

    
private void getBaseInfo() {
        System.out.println(intBgColor 
+ "|" + intCharColor);
        System.out.println(intMinX 
+ "|" + intMinY + "|" + intMaxX + "|" + intMaxY);
    }


    

    
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;

        
return new Point[] { pTopLeft, pBottomRight };
    }


    

    
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
    }


    

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


    

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


    

    
public String getValidatecode(int length) {
        String strNum 
= "";

        
for (int i = 1; i <= length; i++{
            
synchronized (this{
                strNum 
+= String.valueOf(getNum(i));
            }

        }


        
return strNum;
    }


    

    
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;


public   class  MyHttpClient  {

    

    
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...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM