使用OpenCV 破解QQ登录中的滑动验证码

系列文章目录

第一篇: Java实现QQ登录

第二篇: Selenium QQ自动化登录

第三篇: 使用OpenCV 破解QQ登录中的滑动验证码


 

 

前言

前面几篇文章中介绍了在安全状态下,QQ登录的几种方法,但是只要是,该账号登录在新IP上,或用新账号登录本IP,都会使得登录流程转向非安全状态,即会有图片验证码,如下:


提示:以下是本篇文章正文内容,下面案例可供参考

一、登录流程

如我在第一篇文章中介绍那样:Java实现QQ登录,会多出几个请求,其中我们会看到又两个img的请求:

而这两个请求正是获得验证码图片和图片缺口的接口。

二、验证办法

1.滑动验证码

当我们拖动滑块并停止时,会向后台发送一个cap_union_new_verify的请求,这个request的data里,又这样一个关键的数据,ans

 

经过分析得知,这个ans的值,正式滑动验证码中缺口图片在原图中的位置,那么我们只要能获得这个缺口坐标,即可获得正确request data。

2.获取缺口坐标

我们使用OpenCV来处理图片

2.1 获得两张验证码的url

大图:

https://t.captcha.qq.com/hycdn?index=1&image=937349372142954496?aid=21000501&sess=s0QmzAR9jp0J_P9zQf6-BikkZVSLAm3IZz9JBiMAkRnqiQQ-ehtcopO3OYgJzF9ZLrA1B6phmWOrNtG1YEarWRA9wRp2gHvMW9lzGN5bZgFvIdgvL1ljAEcUq-ASo3WNABJPoMJP-UWSkRsimXX2wetiGFbUg7pMjUYGBPJAUxL8gCiyRnbKExWMrjyzVu4onNqzoxRU4_Ou9jexaVzeERJRo-WahiuPG2NyiohF5390zOVQ1tmDAmDg**&sid=607792806632235317&img_index=1&subsid=9

缺口图:

https://t.captcha.qq.com/hycdn?index=2&image=937349372142954496?aid=21000501&sess=s0QmzAR9jp0J_P9zQf6-BikkZVSLAm3IZz9JBiMAkRnqiQQ-ehtcopO3OYgJzF9ZLrA1B6phmWOrNtG1YEarWRA9wRp2gHvMW9lzGN5bZgFvIdgvL1ljAEcUq-ASo3WNABJPoMJP-UWSkRsimXX2wetiGFbUg7pMjUYGBPJAUxL8gCiyRnbKExWMrjyzVu4onNqzoxRU4_Ou9jexaVzeERJRo-WahiuPG2NyiohF5390zOVQ1tmDAmDg**&sid=607792806632235317&img_index=2&subsid=10

url中最重要的参数sess可从上一个请求:cap_union_prehandle中的response中获得。

验证码的图有时间限制,一段时间后会过期

 

2.2 处理图片获得x坐标

因为滑块是左右滑动的,所以y坐标固定,我们先滑动一下获得他的y坐标,本例中的y坐标是41。

使用下面程序:

    // 加载opencv环境
    public static String dllPath = "D:\\workcode\\txslider\\src\\main\\resources\\opencv\\opencv_java3413.dll";

    /**
     * 获取验证滑动距离
     *
     * @return
     */
    public static int getTencentDistance(String bUrl, String sUrl, int top) {
        System.load(dllPath);
        File bFile = new File("D:\\cap_union_new_getcapbysig.jpg");
        File sFile = new File("D:\\cap_union_new_getcapbysig.png");
        try {
            FileUtils.copyURLToFile(new URL(bUrl), bFile);
            FileUtils.copyURLToFile(new URL(sUrl), sFile);
            BufferedImage bgBI = ImageIO.read(bFile);
            BufferedImage sBI = ImageIO.read(sFile);
            // 裁剪
            bgBI = bgBI.getSubimage(360, top, bgBI.getWidth() - 370, sBI.getHeight());
            ImageIO.write(bgBI, "png", bFile);
            Mat s_mat = Imgcodecs.imread(sFile.getPath());
            Mat b_mat = Imgcodecs.imread(bFile.getPath());
            // 转灰度图像
            Mat s_newMat = new Mat();
            Imgproc.cvtColor(s_mat, s_newMat, Imgproc.COLOR_BGR2GRAY);
            // 二值化图像
            binaryzation(s_newMat);
            Imgcodecs.imwrite(sFile.getPath(), s_newMat);

            int result_rows = b_mat.rows() - s_mat.rows() + 1;
            int result_cols = b_mat.cols() - s_mat.cols() + 1;
            Mat g_result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
            Imgproc.matchTemplate(b_mat, s_mat, g_result, Imgproc.TM_SQDIFF); // 归一化平方差匹配法
            // 归一化相关匹配法
            Core.normalize(g_result, g_result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
            Core.MinMaxLocResult mmlr = Core.minMaxLoc(g_result);
            Point matchLocation = mmlr.maxLoc; // 此处使用maxLoc还是minLoc取决于使用的匹配算法
            Imgproc.rectangle(b_mat, matchLocation,
                    new Point(matchLocation.x + s_mat.cols(), matchLocation.y + s_mat.rows()), new Scalar(0, 0, 0, 0));
            return (int) ((matchLocation.x + s_mat.cols() + 360 - sBI.getWidth()));
        } catch (Throwable e) {
            e.printStackTrace();
            return 0;
        } finally {
            bFile.delete();
            sFile.delete();
        }
    }
    /**
     *
     * @param mat
     *  二值化图像
     */
    public static void binaryzation(Mat mat) {
        int BLACK = 0;
        int WHITE = 255;
        int ucThre = 0, ucThre_new = 127;
        int nBack_count, nData_count;
        int nBack_sum, nData_sum;
        int nValue;
        int i, j;
        int width = mat.width(), height = mat.height();
        // 寻找最佳的阙值
        while (ucThre != ucThre_new) {
            nBack_sum = nData_sum = 0;
            nBack_count = nData_count = 0;

            for (j = 0; j < height; ++j) {
                for (i = 0; i < width; i++) {
                    nValue = (int) mat.get(j, i)[0];

                    if (nValue > ucThre_new) {
                        nBack_sum += nValue;
                        nBack_count++;
                    } else {
                        nData_sum += nValue;
                        nData_count++;
                    }
                }
            }
            nBack_sum = nBack_sum / nBack_count;
            nData_sum = nData_sum / nData_count;
            ucThre = ucThre_new;
            ucThre_new = (nBack_sum + nData_sum) / 2;
        }
        // 二值化处理
        int nBlack = 0;
        int nWhite = 0;
        for (j = 0; j < height; ++j) {
            for (i = 0; i < width; ++i) {
                nValue = (int) mat.get(j, i)[0];
                if (nValue > ucThre_new) {
                    mat.put(j, i, WHITE);
                    nWhite++;
                } else {
                    mat.put(j, i, BLACK);
                    nBlack++;
                }
            }
        }
        // 确保白底黑字
        if (nBlack > nWhite) {
            for (j = 0; j < height; ++j) {
                for (i = 0; i < width; ++i) {
                    nValue = (int) (mat.get(j, i)[0]);
                    if (nValue == 0) {
                        mat.put(j, i, WHITE);
                    } else {
                        mat.put(j, i, BLACK);
                    }
                }
            }
        }
    }

 

主要思想为:根据传入的y坐标和两个图片的url,将网络上的图片先保存在本地,然后对大图进行切图,减少一些计算量,然后二值化两张图,接着使用归一化相关匹配法,计算出切图后的x距离,最后加上原来的x距离,即可得到最终的x坐标。

 

2.3 调用结果

getTencentDistance("https://t.captcha.qq.com/hycdn?index=1&image=937349372142954496?aid=21000501&sess=s0QmzAR9jp0J_P9zQf6-BikkZVSLAm3IZz9JBiMAkRnqiQQ-ehtcopO3OYgJzF9ZLrA1B6phmWOrNtG1YEarWRA9wRp2gHvMW9lzGN5bZgFvIdgvL1ljAEcUq-ASo3WNABJPoMJP-UWSkRsimXX2wetiGFbUg7pMjUYGBPJAUxL8gCiyRnbKExWMrjyzVu4onNqzoxRU4_Ou9jexaVzeERJRo-WahiuPG2NyiohF5390zOVQ1tmDAmDg**&sid=607792806632235317&img_index=1&subsid=9",
                "https://t.captcha.qq.com/hycdn?index=2&image=937349372142954496?aid=21000501&sess=s0QmzAR9jp0J_P9zQf6-BikkZVSLAm3IZz9JBiMAkRnqiQQ-ehtcopO3OYgJzF9ZLrA1B6phmWOrNtG1YEarWRA9wRp2gHvMW9lzGN5bZgFvIdgvL1ljAEcUq-ASo3WNABJPoMJP-UWSkRsimXX2wetiGFbUg7pMjUYGBPJAUxL8gCiyRnbKExWMrjyzVu4onNqzoxRU4_Ou9jexaVzeERJRo-WahiuPG2NyiohF5390zOVQ1tmDAmDg**&sid=607792806632235317&img_index=2&subsid=10",
                41)

调用getTencentDistance方法,传入大图和缺口图的地址,并传入y坐标。

最终结果为:

而实际y坐标为

相差6个像素点,在可接受范围内。


 

总结

本文主要介绍了用opencv,来识别滑动验证码中,缺口图片相对背景图的x坐标,获得了x坐标位置,即可模拟手动滑动,完成验证码的突破,结合前面几篇文章,即可完成有验证码情况下的登录。

 

当然还有更方便的方式,即通过Selenium自动模仿人手动滑动的方法,这种方法将在下篇文章中介绍。

 

 

 

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值