人机检验解决方案之-滑动验证码

使用java + selenium + OpenCV破解腾讯防水墙滑动验证码

**腾讯防水墙:

1
2
3
4
markdown复制代码* 验证码地址:https://007.qq.com/online.html
* 使用OpenCv模板匹配
* 成功率90%左右
* Java + Selenium + OpenCV

产品样例

腾讯防水墙

来吧!展示!

结果展示

注意!!!

1
复制代码· 在模拟滑动时不能按照相同速度或者过快的速度滑动,需要向人滑动时一样先快后慢,这样才不容易被识别。

模拟滑动代码↓↓↓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
java复制代码/**
* 模拟人工移动
* @param driver
* @param element页面滑块
* @param distance需要移动距离
*/
public static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException {
int randomTime = 0;
if (distance > 90) {
randomTime = 250;
} else if (distance > 80 && distance <= 90) {
randomTime = 150;
}
List<Integer> track = getMoveTrack(distance - 2);
int moveY = 1;
try {
Actions actions = new Actions(driver);
actions.clickAndHold(element).perform();
Thread.sleep(200);
for (int i = 0; i < track.size(); i++) {
actions.moveByOffset(track.get(i), moveY).perform();
Thread.sleep(new Random().nextInt(300) + randomTime);
}
Thread.sleep(200);
actions.release(element).perform();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据距离获取滑动轨迹
* @param distance需要移动的距离
* @return
*/
public static List<Integer> getMoveTrack(int distance) {
List<Integer> track = new ArrayList<>();// 移动轨迹
Random random = new Random();
int current = 0;// 已经移动的距离
int mid = (int) distance * 4 / 5;// 减速阈值
int a = 0;
int move = 0;// 每次循环移动的距离
while (true) {
a = random.nextInt(10);
if (current <= mid) {
move += a;// 不断加速
} else {
move -= a;
}
if ((current + move) < distance) {
track.add(move);
} else {
track.add(distance - current);
break;
}
current += move;
}
return track;
}

看操作,no bb,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
java复制代码private final String INDEX_URL = "https://007.qq.com/online.html?ADTAG=index.head";
private void seleniumTest() {
ChromeDriverManager manager = ChromeDriverManager.getInstance();
int status = -1;
try {
WebDriver driver = manager.getDriver();
driver.get(INDEX_URL);
driver.manage().window().maximize(); // 设置浏览器窗口最大化
Thread.sleep(10000);
driver.findElement(By.className("wp-onb-tit")).findElements(By.tagName("a")).get(1).click();
Thread.sleep(500);
// 点击出现滑动图
waitWebElement(driver, By.id("code"), 500).click();
Thread.sleep(100);
// 获取到验证区域
driver.switchTo().frame(waitWebElement(driver, By.id("tcaptcha_iframe"), 500));
Thread.sleep(100);
// 获取滑动按钮
WebElement moveElemet = waitWebElement(driver, By.id("tcaptcha_drag_button"), 500);
Thread.sleep(100);
// 获取带阴影的背景图
String bgUrl = waitWebElement(driver, By.id("slideBg"), 500).getAttribute("src");
Thread.sleep(100);
// 获取带阴影的小图
String sUrl = waitWebElement(driver, By.id("slideBlock"), 500).getAttribute("src");
Thread.sleep(100);
// 获取高度
String topStr = waitWebElement(driver, By.id("slideBlock"), 500).getAttribute("style").substring(32, 36);
int top = Integer.parseInt(topStr.substring(0, topStr.indexOf("p"))) * 2;
Thread.sleep(100);
// 计算移动距离
int distance = (int) Double.parseDouble(getTencentDistance(bgUrl, sUrl, top));
// 滑动
move(driver, moveElemet, distance);
Thread.sleep(5000);

} catch (Exception e) {
e.printStackTrace();
} finally {
manager.closeDriver(status);
}
}

/**
* 获取腾讯验证滑动距离
*
* @return
*/
public static String dllPath = "C://chrome//opencv_java440.dll";

public String getTencentDistance(String bUrl, String sUrl, int top) {
System.load(dllPath);
File bFile = new File("C:/qq_b.jpg");
File sFile = new File("C:/qq_s.jpg");
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());
Point matchLocation = new Point();
MinMaxLocResult mmlr = Core.minMaxLoc(g_result);
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 "" + ((matchLocation.x + s_mat.cols() + 360 - sBI.getWidth() - 46) / 2);
} catch (Throwable e) {
e.printStackTrace();
return null;
} 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);
}
}
}
}
}
// 延时加载
private static WebElement waitWebElement(WebDriver driver, By by, int count) throws Exception {
WebElement webElement = null;
boolean isWait = false;
for (int k = 0; k < count; k++) {
try {
webElement = driver.findElement(by);
if (isWait)
System.out.println(" ok!");
return webElement;
} catch (org.openqa.selenium.NoSuchElementException ex) {
isWait = true;
if (k == 0)
System.out.print("waitWebElement(" + by.toString() + ")");
else
System.out.print(".");
Thread.sleep(50);
}
}
if (isWait)
System.out.println(" outTime!");
return null;
}

五、结果分析

目标:

识别拼图位置,推算出对应滑动距离,模拟滑动。

实现思路:

1.抓取图片

2.灰度化,二值化图像

3.使用opencv模糊匹配算法进行匹配检测

4.通过检测结果推算滑动距离

5.根据推算距离模拟滑动

检测耗时:

15 - 100毫秒

通过率:

=95%

最终测试结果为300条样本结果,这个样本数还是偏少了,不确定在更多的测试条数时还会不会达到这样的效果,应该不会差太远哈。

六、结语

这篇文章到这里就结束了,感谢大佬们驻足观看,大佬们点个关注、点个赞呗~

谢谢大佬~
在这里插入图片描述


作者:香芋味的猫丶

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%