打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
自动识别图形验证码
现在大多数网站都采用了验证码来防止暴力破解或恶意提交。但验证码真的就很安全吗?真的就不能被机器识别??
我先讲讲我是怎么实现站外提交留言到一个网站的程序。
这个网站的留言版大致如下:
我一看这种简单的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...
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
关于http gzip压缩的一点想法
運用Apache HttpClient實作Get與Post動作 - 小嘴冰凉 - ITey...
应用HttpClient来对付各种顽固的WEB服务器(2) [转] - 快乐着飞舞着 - IT博客
HttpClient容易忽视的细节
get/post方式调用http接口
微信开发之如何根据经纬度获取所在的地点信息
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服