您的位置:首页 > 其它

自动识别图形验证码

2012-10-16 22:10 459 查看
现在大多数网站都采用了验证码来防止暴力破解或恶意提交。但验证码真的就很安全吗?真的就不能被机器识别??

我先讲讲我是怎么实现站外提交留言到一个网站的程序。

这个网站的留言版大致如下:



我一看这种简单的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...

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