現在大多數網站都采用了驗證碼來防止暴力破解或惡意提交。但驗證碼真的就很安全嗎?真的就不能被機器識別??
我先講講我是怎么實現站外提交留言到一個網站的程序。
這個網站的留言版大致如下:
![[Java] <wbr>識別圖片驗證碼](/image/aHR0cDovL3d3dy5qYXZhM3ouY29tL2N3YndlYmhvbWUvYXJ0aWNsZS9hcnRpY2xlMi9pbWc3L2xpdXlhbmJhbkxULmpwZw==.png)
我一看這種簡單的4位數字驗證碼,馬上就感覺到有戲了。直覺告訴我讓電腦來識別這些圖片驗證碼據對簡單o(∩_∩)o...
首先我馬上在這個頁面用右鍵菜單看源代碼
![[Java] <wbr>識別圖片驗證碼](/image/aHR0cDovL3d3dy5qYXZhM3ouY29tL2N3YndlYmhvbWUvYXJ0aWNsZS9hcnRpY2xlMi9pbWc3LzMyMTEyLmpwZw==.png)
知道驗證碼獲取頁面后 你可以直接用 http://www.XXXX.com/imgchk/validatecode.asp 這樣去訪問你會發現你打開的就是一個驗證碼圖片。
![[Java] <wbr>識別圖片驗證碼](/image/aHR0cDovL3d3dy5qYXZhM3ouY29tL2N3YndlYmhvbWUvYXJ0aWNsZS9hcnRpY2xlMi9pbWc3L3lhbnpoZW5nbWFfMjAyMi5qcGc=.png)
對的其實返回的就是圖片文件的2進制流而已。接着先用右鍵保存一張驗證碼的圖片。因為要開始分析這張圖片了,什么用什么工具?PhotoShop????不用就一般的畫圖工具就可以了。我們要搞清楚的是這幾個數字分別占幾個像素就可以了。
![[Java] <wbr>識別圖片驗證碼](/image/aHR0cDovL3d3dy5qYXZhM3ouY29tL2N3YndlYmhvbWUvYXJ0aWNsZS9hcnRpY2xlMi9pbWc3L2t1YW50dS5qcGc=.png)
可以看出 一個數字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 = { { 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; 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(7, 4); // System.out.println(intBgColor); // 初始化圖象原點座標 pOrigin = new Point(0, 0); } private void getBaseInfo() { System.out.println(intBgColor + "|" + intCharColor); System.out.println(intMinX + "|" + intMinY + "|" + intMaxX + "|" + intMaxY); } 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 }; } 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((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 { 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... ![]() |

![[Java] <wbr>識別圖片驗證碼](/image/aHR0cDovL3d3dy5qYXZhM3ouY29tL2N3YndlYmhvbWUvYXJ0aWNsZS9hcnRpY2xlMi9pbWc3L2ppZWd1by5qcGc=.png)