实现一些自动化功能的时候,经常会遇到这样的滑块拼图验证码:
这种新型验证非常烦人,一方面是拼图缺口位置的识别比较有难度,目前也缺乏成功率很高的打码平台;另一方面是模拟人手的拖动动作,需要生成一段加速度移动曲线。
这期教学我就来教大家如何用很短的代码来实现这类验证码的识别。
我们先观察一下这个验证码,可以发现一个明显的特征,就是拼图缺口处,是通过一张半透明的黑色背景标识的,按照Alpha混合公式,当混合一张半透明的黑色图案时,一同混合的图像RGB数值都会降低,说人话就是亮度变低了,所以缺口处,往往是这类验证码最暗的地方。
掌握了这个信息,识别缺口位置就变得很简单了,只需要扫描一行像素,统计每一个像素及四周(拼图块缺口宽度)一定数量的像素亮度均值,亮度最暗的位置,就是拼图缺口位置了。
由于按键精灵很难访问bitmap数据(图片像素数据),因此我们使用C语言实现这个功能,C语言访问bitmap数据很方便,但是访问图像数据却比较麻烦,好在之前我修改了一份bmp访问的功能,使它能够支持多种格式的bmp文件,通过这项功能,我们只需要实现少量代码,就可以搞定滑块拼图验证码的识别了:
- int max3(int a, int b, int c)
- {
- int i = (a > b) ? a : b;
- return (i > c) ? i : c;
- }
- int min3(int a, int b, int c)
- {
- int i = (a < b) ? a : b;
- return (i < c) ? i : c;
- }
- // 识别滑块拼图验证码(返回中心点坐标的位置)
- int CrackSliderVCode(char* sFile, int sw, int sh, int iTop)
- {
- // 滑块至少从 1/ 4 的位置开始,至少从拼图图形一半的地方结束
- ClImage* img = ffbot_LoadImage(sFile);
- int iPosS = sw;
- int iPosE = img->width - (sw / 2);
- int iOffsetY1 = iTop + (0.33 * sh);
- int iOffsetY2 = iTop + (0.67 * sh);
- int light = 255;
- int left = 0;
- for ( int i = iPosS; i < iPosE; i++ ) {
- int l1[43];
- int l2[43];
- int lt[43];
- for ( int p = 0; p < 43; p++ ) {
- int x = i - 21 + p;
- ClRgbQuad c1 = img->bitmapRGBA[ ((img->height - iOffsetY1) * img->width) + x ];
- ClRgbQuad c2 = img->bitmapRGBA[ ((img->height - iOffsetY2) * img->width) + x ];
- l1[p] = (max3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed) + min3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed)) / 2;
- l2[p] = (max3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed) + min3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed)) / 2;
- lt[p] = (l1[p] + l2[p]) / 2;
- }
- int lsum = 0;
- for ( int p = 0; p < 43; p++ ) {
- lsum += lt[p];
- }
- lsum = lsum / 43;
- if (lsum < light) {
- light = lsum;
- left = i;
- }
- //printf("%d, %d\n", i, lsum);
- }
- //printf("最终结果:%d, %d\n", left, light);
- ffbot_FreeBMP(img);
- return left;
- }
复制代码这里讲解这段代码的含义,我们定义了一个函数CrackSliderVCode,它有四个参数,第一个参数是验证码截图,第二个参数是拼图块的宽度(也就是缺口的宽度),第三个参数是拼图块的高度(也就是缺口的高度),第四个参数是拼图块位于截图的纵坐标偏移(可以通过网页元素获取到)。
当然,也有一些最新的拼图验证码,通过元素无法获取到拼图块纵坐标偏移了,这种情况我们暂不考虑(实现起来也不难,之后可以单独开一篇教程)。
为什么需要拼图块的宽度和高度呢?这个实现充分考虑了不同拼图块大小,提供这些信息可以帮助我们更准确的定位,同样可以通过截图工具直接测量,或者获取拼图块网页元素的宽度和高度。
ffbot_LoadImage 这个函数载入了第一个参数传入的 bmp 图片,它会自动把像素格式转换为32位,这样memory对齐访问起来非常方便。
随后我们通过int iOffsetY1 = iTop + (0.33 * sh); 和 int iOffsetY2 = iTop + (0.67 * sh); 两行代码计算出了拼图款的 1/3 位置和 2/3 位置,因为拼图块有四周和中间可能有缺口,这两个位置是最完整的,可以确保统计的数值可信度较高。
接下来我们会遍历这两条线,获取这两个点及其附近的像素数据,通过 max3 和 min3 两个函数,将RGB值转换为 HSL 值(这里只获取了L值,也就是亮度,相关的算法可以百度 RGB转HSL)
当我们发现一个点的亮度比之前的亮度更低,就会保存这个点的坐标,最后把亮度最低的位置返回。
这篇教程的目的是给大家讲解这类验证码的一种解题思路,bitmap 数据里总是能够挖掘出很多玩法,一些看似复杂的命题,当你掌握了bitmap数据的访问能力,就可以用很巧妙的方法解决掉。
当然,也考虑到很多按键精灵用户不具备C语言开发能力,这里也会将源代码编译为DLL,供按键精灵X调用,为什么不提供按键精灵普通版本调用的版本呢?
因为C语言写COM有点麻烦,暂时先这样,文件我会编译后放在2L提供下载地址(编辑这个帖子会导致代码格式丢失……),补充的内容也都会跟帖。