1. 引子

前两天访问学校自助服务器()缴纳网费,登录时发现这系统的验证码也太过“清晰”了,突然脑袋里就蹦出一个想法:如果能够自动识别验证码,然后采用暴力破解的方式,那么密码不是可以轻易被破解吗?

ps:用户名就是学号,可以轻易获得,而密码是系统随机生成的6位数,组合方式仅有 10^6种,假设每次尝试须要50ms,那么大概需要14个小时,如果采用多线程,多个虚拟机(java)同时工作,估计把所有密码过一遍不会超过1个小时,这效率还凑合吧。。。

2. 分析

问题的关键就在于验证码识别,至于如何请求服务器,用java分分钟搞定。学习了一些网友写的关于验证码识别的blog,如:http://blog.csdn.net/problc/article/details/5794460。发现它的基本步骤就是:【去噪】、【分割】、【匹配】,【识别】。

① 去噪

即去除背景和干扰线,并且将背景置为白色,文字置为黑色,便于后面匹配。验证码获取地址:http://202.118.166.244:8080/selfservice/common/web/verifycode.jsp。通过观察会发现,文字部分颜色较深(r,g,b基本小于110),干扰部分颜色较浅。于是可以这样【去噪】:

  1. public static BufferedImage denoising(BufferedImage image) {
  2. for (int x = 0; x < image.getWidth(); x++) {
  3. for (int y = 0; y < image.getHeight(); y++) {
  4. Color color = new Color(image.getRGB(x, y));
  5. int red = color.getRed();
  6. int green = color.getGreen();
  7. int blue = color.getBlue();
  8. if (red > 105 && green > 105 && blue > 105) {
  9. image.setRGB(x, y, Color.WHITE.getRGB());
  10. } else {
  11. image.setRGB(x, y, Color.BLACK.getRGB());
  12. }
  13. }
  14. }
  15. return image;
  16. }

看看效果:

处理前: 处理后: ,效果还是不错的!

② 分割

分割很简单,将验证码按文字等分。

  1. /**
  2. * 分割图片
  3. *
  4. * @param img
  5. * @param splitNum
  6. * @return
  7. * @throws IOException
  8. */
  9. public static List<BufferedImage> splitImage(BufferedImage img, int splitNum) throws IOException {
  10. int width = img.getWidth();
  11. int height = img.getHeight();
  12. int splitWidth = width / splitNum;
  13. List<BufferedImage> bufferedImages = new ArrayList<BufferedImage>();
  14. for (int i = 0; i < splitNum; i++) {
  15. bufferedImages.add(img.getSubimage(i * splitWidth, 0, splitWidth, height));
  16. }
  17. return bufferedImages;
  18. }

③ 匹配

在匹配之前,要利用前面的两个方法得到所有字符的片段,用于匹配。像这样:

然后设计匹配算法,这一步比较关键,匹配算法的好坏将直接导致识别的正确与否。因为观察到文字都没有进行旋转,因此这里采用:用一个集合记录下图片每一纵行所拥有的黑色像素点的个数(没有像素的纵行不记录),将这个集合作为对应图片的指纹。然后分割好的验证码片段与上面的标准片段进行一一比对,最后组合在一起,从而可以识别出验证码。

  1. /**
  2. * 单个字符进行匹配
  3. *
  4. * @param img
  5. * @param regularDataList
  6. * @return
  7. */
  8. public String matchSingleWord(BufferedImage img, List<List<Integer>> regularDataList) {
  9. String result = null;
  10. int maxRank = 0;
  11. List<Integer> matchedData = getFingerprint(img);
  12. for (int i = 0; i < regularDataList.size(); i++) {
  13. int rank = 0;
  14. List<Integer> regularData = regularDataList.get(i);
  15. int minColumn = Math.min(regularData.size(), matchedData.size());
  16. for (int j = 0; j < minColumn; j++) {
  17. if (matchedData.get(j) == regularData.get(j)) {
  18. rank++;
  19. }
  20. }
  21. if (rank > maxRank) {
  22. maxRank = rank;
  23. result = i + "";
  24. }
  25. }
  26. return result;
  27. }
  1. /**
  2. * 获取图像"指纹"
  3. *
  4. * @param image
  5. * @return
  6. */
  7. private static List<Integer> getFingerprint(BufferedImage image) {
  8. List<Integer> list = new ArrayList<Integer>();
  9. for (int x = 0; x < image.getWidth(); x++) {
  10. int count = 0;
  11. for (int y = 0; y < image.getHeight(); y++) {
  12. // System.out.println(image.getRGB(x, y));
  13. if (image.getRGB(x, y) == 0xFF000000) {
  14. count++;
  15. }
  16. }
  17. if (count != 0) {
  18. list.add(count);
  19. }
  20. }
  21. return list;
  22. }
  1. /**
  2. * 加载作为标准的指纹List
  3. *
  4. * @return
  5. * @throws IOException
  6. */
  7. private static List<List<Integer>> loadMatchDataList() throws IOException {
  8. List<List<Integer>> matchData = new ArrayList<List<Integer>>();
  9. File dir = new File("C:\\Users\\Administrator\\Desktop\\verifycode\\match");
  10. File[] files = dir.listFiles();
  11. for (File file : files) {
  12. matchData.add(getFingerprint(ImageIO.read(file)));
  13. }
  14. return matchData;
  15. }

④ 识别

将以上识别出的单个字符组合在一起,就得到验证码啦。

  1. public static void main(String[] args) throws Exception {
  2. BufferedImage image = ImageIO.read(new URL("http://202.118.166.244:8080/selfservice/common/web/verifycode.jsp"));
  3. ImageIO.write(image, "png", new File("C:\\Users\\Administrator\\Desktop\\verifycode\\verifycode_src.png"));
  4. image = denoising(image);
  5. // 注意:最好以png格式输出,否则可能导致图片失真
  6. ImageIO.write(image, "png", new File("C:\\Users\\Administrator\\Desktop\\verifycode\\verifycode.png"));
  7. List<BufferedImage> images = splitImage(image, 4);
  8. List<List<Integer>> regularFingerprintList = loadMatchDataList();
  9. String result = "";
  10. for (BufferedImage bufferedImage : images) {
  11. result += matchSingleWord(bufferedImage, regularFingerprintList);
  12. }
  13. System.out.println("验证码是:" + result);
  14. }

结果: ,完全正确。

3. 总结

总的来说,由于该类型验证码本生较为简单,所以处理起来十分顺利。但不管验证码怎么变化,基于这种识别算法的基本就是以上几部,具体做法根据具体案例实现。

最后随便搞一个账号来测试,用时2个多小时跑出了密码。。。

先写到这里,以后再研究其他识别算法。

验证码识别<1>的更多相关文章

  1. 字符型图片验证码识别完整过程及Python实现

    字符型图片验证码识别完整过程及Python实现 1   摘要 验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的 防火墙 功能,但是随时OCR技术的发展,验证码暴露出来的安全问题也越 ...

  2. 简单的验证码识别(opecv)

    opencv版本: 3.0.0 处理验证码: 纯数字验证码 (颜色不同,有噪音,和带有较多的划痕) 测试时间 :  一天+一晚 效果: 比较挫,可能是由于测试的图片是在太小了的缘故. 原理:  验证码 ...

  3. 利用开源程序(ImageMagick+tesseract-ocr)实现图像验证码识别

    --------------------------------------------------低调的分割线-------------------------------------------- ...

  4. 基于LeNet网络的中文验证码识别

    基于LeNet网络的中文验证码识别 由于公司需要进行了中文验证码的图片识别开发,最近一段时间刚忙完上线,好不容易闲下来就继上篇<基于Windows10 x64+visual Studio2013 ...

  5. Java验证码识别解决方案

    建库,去重,切割,识别. package edu.fzu.ir.test; import java.awt.Color; import java.awt.image.BufferedImage; im ...

  6. 简单验证码识别(matlab)

    简单验证码识别(matlab) 验证码识别, matlab 昨天晚上一个朋友给我发了一些验证码的图片,希望能有一个自动识别的程序. 1474529971027.jpg 我看了看这些样本,发现都是很规则 ...

  7. Python验证码识别处理实例(转载)

    版权声明:本文为博主林炳文Evankaka原创文章,转载请注明出处http://blog.csdn.net/evankaka 一.准备工作与代码实例 1.PIL.pytesser.tesseract ...

  8. 验证码识别--type2

    验证码识别--type2 终于来到了彩色图像,一定有一些特点 这里的干扰项是色彩不是很鲜艳的.灰色的线条,还有单独的干扰点,根据这些特性进行去除 直接ostu的话,有的效果好,有的效果不好   本来是 ...

  9. 验证码识别--type5

    验证码识别--type5 每一种验证码都是由人设计出来.在设计过程中,可能由于多个方面的原因,造成了这样或那样的可以被利用的漏洞.验证码识别,首先需要解决的问题就是发现这些漏洞--然后利用漏洞解决问题 ...

随机推荐

  1. 阿里云直播 C# SDK 如何使用

    阿里云直播SDK的坑 1.直播云没有单独的SDK,直播部分被封装在CDN的相关SDK当中. 2.针对SDK,没有相关Demo. 3.针对SDK,没有相关的文档说明. 4.针对SDK的说明,官网上的说明 ...

  2. Concepts:Request 和 Task

    当SQL Server Engine 接收到Session发出的Request时,SQL Server OS将Request和Task绑定,并为Task分配一个Workder.在TSQL Query执 ...

  3. RestTemplate发送请求并携带header信息

    1.使用restTemplate的postForObject方法 注:目前没有发现发送携带header信息的getForObject方法. HttpHeaders headers = new Http ...

  4. Tesseract-OCR字符识别简介

    OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程.Tesseract:开源的OCR识别引擎,初期Tesseract引擎 ...

  5. 要想提高PHP的编程效率,你必须知道的要点

    1.当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数.此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储 ...

  6. Effective前端2:优化html标签

    div { float: left; } .keyboard > div + div { margin-left: 8px; } --> div{display:table-cell;ve ...

  7. Syscall,API,ABI

    系统调用(Syscall):Linux2.6之前是使用int0x80(中断)来实现系统调用的,在2.6之后的内核是使用sysentry/sysexit(32位机器)指令来实现的系统调用,这两条指令是C ...

  8. PHP设计模式(六)原型模式(Prototype For PHP)

    原型设计模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型设计模式简单的来说,顾名思义, 不去创建新的对象进而保留原型的一种设计模式. 缺点:原型设计模式是的最主要的缺点就 ...

  9. 玩转ajax

    1.什么是ajax? Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的缩写. 2.ajax需要什么基础? HTML 用于建立 Web 表单并确定应 ...

  10. 说说BPM数据表和日志表中几个状态字段的详细解释

    有个客户说需要根据这些字段的值作为判断条件做一些定制化需求,所以需要知道这些字段的名词解释,以及里面存储的值具体代表什么意思 我只好为你们整理奉上这些了! Open Work Sheet  0 Sav ...