UiBot过滑动验证码检测实例

luo 2019-7-27 31041

我们在做一些自动化登录的过程中,经常会遇到各种各样的图灵检测,如下图所例:

_UiBot过滑动验证码检测实例

因为验证码样式多变,而且需要较多命令组合才能完成验证,所以UiBot暂时没有将验证码做为一个命令组件进行预制。难到这样就没办法了吗?显然不是,我们可以利用RPA连接外部的特性,使用部分coding来解决此问题(使用coding也算一种连接嘛)


回到上例,一般来说这样的验证码验证有如下流程,我们把他拆分一下可得:

1、展示完整原图,说明验证方式

2、鼠标按住滑块,展现出缺陷图

3、滑动滑块到缺陷部分

其中,1、2两部我们用眼睛识别,用来测算距离,第3步用鼠标移动用来填补距离,基于这个逻辑,我们同样把整个验证的实现拆分成2个大步,那么第一步是算滑动距离,第二步就是根据滑动距离进行滑动了,下面我们根据实例来一步步进行分解实现。

实例地址:https://www.chinapay.com/index.jsp(银联的登录地址,用户名密码随便输入可触发滑动验证)


要得到完整距离,首先我们要得到无缺口图和有缺口图2张图片,其中有缺口的图片需要将鼠标移到滑块上并按住才会出现,当然这也很好解决,我们可以直接使用UiBot获取这两张图,获取的方式如下:

_UiBot过滑动验证码检测实例

代码:

UiElement.ScreenShot({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","aaname":"加载中...","css-selector":"body>div>div>div>div>div>div"},"index":0}},"1.png","content",{"bContinueOnError":false,"iDelayAfter":300,"iDelayBefore":200})
Mouse.Hover({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","isleaf":"1","css-selector":"body>div>div>div>div>div"},"index":3}},10000,{"bContinueOnError":false,"iDelayAfter":0,"iDelayBefore":0,"bSetForeground":true,"sCursorPosition":"Center","iCursorOffsetX":0,"iCursorOffsetY":0,"sKeyModifiers":[],"sSimulate":"simulate"})
Mouse.Click("left", "down", [])
Delay(600)
UiElement.ScreenShot({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","aaname":"加载中...","css-selector":"body>div>div>div>div>div>div"},"index":0}},"2.png","content",{"bContinueOnError":false,"iDelayAfter":300,"iDelayBefore":200})
结果:

_UiBot过滑动验证码检测实例

得到两张图后我们需要测算滑动距离,如何测算呢?我们可以比对上图,发现除了缺口以外其他部分基本和原图一致(有部分干扰块,但是问题不大,因为色深不够而且水平距离不一致,我们可以通过后面的算法把他滤过),所以实际上我们只需要抓住起始点和缺陷部分的特征既可算出两者直接的距离,这里我们可以通过python进行图像处理实现,具体实现如下:


from PIL import Image  
import PIL.ImageChops as imagechops
from PIL import Image, ImageDraw,ImageFont
import random
def CalcDistance(srcImg,distImg):
    im1 = Image.open(srcImg)
    im2 = Image.open(distImg)
    #得出两图不一致的地方
    diff=imagechops.difference(im1, im2)
    draw =ImageDraw.Draw(diff)
    #通过颜色处理清除干扰块
    for x in range(0,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor[0]>=100 or pixelColor[1]>=100 or pixelColor[2]>=100:
                draw.line((x, y, x, y),(255,255,255,255))
            else:
                draw.line((x, y, x, y),(0,0,0,0))
    # #清理完可以show一下,查看清理完之后的黑白化效果
    # diff.show()
    #找第一个块中的参照点
    firsetPoint=[0,0]
    for x in range(0,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor!=(0,0,0,0):
                firsetPoint=[x,y]
                break
        if firsetPoint!=[0,0]:
            break
    # 往后跳50找第二个块中的参照点,50是矩形宽度
    secondPoint=[0,0]
    for x in range(firsetPoint[0]+50,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor!=(0,0,0,0):
                secondPoint=[x,y]
                break
        if secondPoint!=[0,0]:
            break
    # #画两条线看看位置是否标注正确,仅用于调试
    # draw.line((firsetPoint[0], firsetPoint[1], firsetPoint[0]+20, firsetPoint[1]),(6,255,9,0))
    # draw.line((secondPoint[0], secondPoint[1], secondPoint[0]+20, secondPoint[1]),(9,8,255,0))
    # diff.show()
    diffPixel=secondPoint[0]-firsetPoint[0]
    print(diffPixel)

经过上述处理后,前文的图会被处理为下图的样子,并算出两块直接的一个距离

_UiBot过滑动验证码检测实例


得到距离,接下来我们需要驱动鼠标进行滑动,这些在UiBot里面都有相应的命令可以直接使用,在第一次处理这个验证码时,我天真的以为到算出距离就已经结束,没想到实际上滑动验证码的难点恰恰是滑动轨迹的模拟。滑动验证码在滑动轨迹上同样加入了人机测验,就算本例的解决方式也只有60%的成功率(两块相隔距离较长的情况下成功率更高)。话不多说,下面直接放轨迹的实现。

def GetStacks(distance):
    distance += 20
    '''
    匀加速\减速运行
        v = v0 + a * t
    位移:
    s = v * t + 0.5 * a * (t**2)
    '''
    # 初速度
    v0 = 0
    # 加减速度列表
    a_list = [3, 4, 5]
    # 时间
    t = 0.2
    # 初始位置
    s = 0
    # 向前滑动轨迹
    forward_stacks = []
    mid = distance * 3 / 5
    while s < distance:
        if s < mid:
            a = a_list[random.randint(0, 2)]
        else:
            a = -a_list[random.randint(0, 2)]
        v = v0
        stack = v * t + 0.5 * a * (t ** 2)
        # 每次拿到的位移
        stack = round(stack)
        s += stack
        v0 = v + a * t
        forward_stacks.append(stack)
    back_stacks = [-1, -1, -2, -3, -2, -3, -2, -2, -3, -1]
    return {'forward_stacks': forward_stacks, 'back_stacks': back_stacks

上述轨迹实现,逻辑是模拟人以加速度的形式进行增速滑动,为了骗过人机检测,特意还有个回滑的操作,而后将轨迹以相对位移数组的形式进行返回,结合我们之前距离的测算,可以得到一个完整的python文件。

from PIL import Image  
import PIL.ImageChops as imagechops
from PIL import Image, ImageDraw,ImageFont
import random
def CalcDistance(srcImg,distImg):
    im1 = Image.open(srcImg)
    im2 = Image.open(distImg)
    #得出两图不一致的地方
    diff=imagechops.difference(im1, im2)
    draw =ImageDraw.Draw(diff)
    #通过颜色处理清除干扰块
    for x in range(0,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor[0]>=100 or pixelColor[1]>=100 or pixelColor[2]>=100:
                draw.line((x, y, x, y),(255,255,255,255))
            else:
                draw.line((x, y, x, y),(0,0,0,0))
    # #清理完可以show一下,查看清理完之后的黑白化效果
    # diff.show()
    #找第一个块中的参照点
    firsetPoint=[0,0]
    for x in range(0,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor!=(0,0,0,0):
                firsetPoint=[x,y]
                break
        if firsetPoint!=[0,0]:
            break
    # 往后跳50找第二个块中的参照点,50是矩形宽度
    secondPoint=[0,0]
    for x in range(firsetPoint[0]+50,260):
        for y in range(0,116):
            pixelColor=diff.getpixel((x,y))
            if pixelColor!=(0,0,0,0):
                secondPoint=[x,y]
                break
        if secondPoint!=[0,0]:
            break
    # #画两条线看看位置是否标注正确,仅用于调试
    # draw.line((firsetPoint[0], firsetPoint[1], firsetPoint[0]+20, firsetPoint[1]),(6,255,9,0))
    # draw.line((secondPoint[0], secondPoint[1], secondPoint[0]+20, secondPoint[1]),(9,8,255,0))
    # diff.show()
    diffPixel=secondPoint[0]-firsetPoint[0]
    return GetStacks(diffPixel)
    
def GetStacks(distance):
    distance += 20
    '''
    匀加速\减速运行
        v = v0 + a * t
    位移:
    s = v * t + 0.5 * a * (t**2)
    '''
    # 初速度
    v0 = 0
    # 加减速度列表
    a_list = [3, 4, 5]
    # 时间
    t = 0.2
    # 初始位置
    s = 0
    # 向前滑动轨迹
    forward_stacks = []
    mid = distance * 3 / 5
    while s < distance:
        if s < mid:
            a = a_list[random.randint(0, 2)]
        else:
            a = -a_list[random.randint(0, 2)]
        v = v0
        stack = v * t + 0.5 * a * (t ** 2)
        # 每次拿到的位移
        stack = round(stack)
        s += stack
        v0 = v + a * t
        forward_stacks.append(stack)
    back_stacks = [-1, -1, -2, -3, -2, -3, -2, -2, -3, -1]
    return {'forward_stacks': forward_stacks, 'back_stacks': back_stacks}

现在剩下的工作是将这个文件给UiBot进行使用。我们将上面的文件保存成CrackGEE.py,放到UiBot Creator的安装目录下的 extend\python下面(可参考:https://forum.uibot.com.cn/thread-51.htm),然后在UiBot里面添加如下代码:

Import CrackGEE
#icon("@res:dp3vobi1-353k-q9mp-q5oh-nicth66fv4ag.png")
UiElement.ScreenShot({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","aaname":"加载中...","css-selector":"body>div>div>div>div>div>div"},"index":0}},"1.png","content",{"bContinueOnError":false,"iDelayAfter":300,"iDelayBefore":200})
#icon("@res:sod4iqf0-l79e-a7hu-n4rm-hom2l8pgvv4k.png")
Mouse.Hover({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","isleaf":"1","css-selector":"body>div>div>div>div>div"},"index":3}},10000,{"bContinueOnError":false,"iDelayAfter":0,"iDelayBefore":0,"bSetForeground":true,"sCursorPosition":"Center","iCursorOffsetX":0,"iCursorOffsetY":0,"sKeyModifiers":[],"sSimulate":"simulate"})
Mouse.Click("left", "down", [])
Delay(600)
#icon("@res:dp3vobi1-353k-q9mp-q5oh-nicth66fv4ag.png")
UiElement.ScreenShot({"wnd":[{"cls":"Chrome_WidgetWin_1","title":"*","app":"chrome"},{"cls":"Chrome_RenderWidgetHostHWND","title":"Chrome Legacy Window"}],"html":{"tagName":"DIV","attrMap":{"tag":"DIV","aaname":"加载中...","css-selector":"body>div>div>div>div>div>div"},"index":0}},"2.png","content",{"bContinueOnError":false,"iDelayAfter":300,"iDelayBefore":200})
dim 点列表=[]
点列表=CrackGEE.CalcDistance("1.png","2.png")
TracePrint(点列表)
Dim 正向移动=点列表["forward_stacks"]
Dim 回移=点列表["back_stacks"]
For Each value In 正向移动
 Mouse.Move(value, 0, true)
Delay(12)
Next
For Each value In 回移
 Mouse.Move(value, 0, true)
Delay(34)
Next
Delay(200)
Mouse.Click("left", "up", [])



视图:

_UiBot过滑动验证码检测实例

在使用chrome打开银联网站,随便输入用户名密码,呼出验证,停留在如下图所示地方:

_UiBot过滑动验证码检测实例

点击UiBot Creator的运行,即可看到效果。



本例权当抛砖引玉,实际上在短距离的比对计算还有滑动轨迹上面还有不小的提升空间,有兴趣的大神可以进一步进行扩展。







上传的附件:
最新回复 (43)
  • 99365 2019-7-29
    2
    不错哦, 非常好的一篇文章
  • 歪大爷 2019-8-1
    3
    对于我们这种小白,实在太难了,不过这个太实用了,慢慢学。
  • 疯狂、白糖 2019-8-2
    4
    给力哇
  • MAKI 2019-8-5
    5
    罗大多出点干货哦,都巴巴等着呢
  • 沈博文 2019-8-5
    6
    落地的方案
  • 礼拜五 2019-8-19
    7
    很厉害,用按键精灵的明度检测也很容易做得出
  • 诸葛毛豆 2019-9-7
    8
    厉害
  • 紫罗袍 2019-9-9
    9
    思路真的好
  • 李明_62004 2019-9-15
    10
    非常厉害
  • SUM 2019-9-15
    11
    收藏,慢慢看0.0
  • Coony 2019-10-16
    12
    我说怎么看着图标那么眼熟,然后是罗总发的帖子.
  • zeror 2019-10-25
    13
    收藏ing
  • 木槿向南 2019-10-29
    14
    大佬 为什么我移动的距离都是一样的
  • 黄同学 2019-10-30
    15


    这咋整

  • 双口吕 2019-10-31
    16
    收藏标记
  • pande 2019-11-8
    17
    牛比
  • str-liang 2019-11-8
    18
    PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。

    由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。
    摘自廖雪峰...我也不会= =||,还在尝试如何将python的Pillow应用到本案例中..
  • Mar 2019-12-4
    19
    为啥 uibot自带的图像识别超级慢(苦笑)
  • 胡石 2019-12-11
    20
    厉害
  • 我心还在 2019-12-17
    21
    看不懂咋整
  • smithgao 2019-12-25
    22
    厉害
  • FastFast 2020-5-7
    23
    导入插件加载不成功会是什么原因?
  • 赵玮祺 2020-5-14
    24
    如果没有原图怎么来实现找出两张图片的不同藏而实现滑动验证呢????
  • dfewfe 2020-5-28
    25
    这个正需要啊
  • 龙游小宾 2020-6-25
    26
    学习了
  • 刘恒_319495 2020-7-28
    27
    从头到位只有一张图,怎么解????
  • 刘恒_319495 2020-7-28
    28
    只有老的滑动验证是你这样的 新的都只有一张图 


  • 夜行 2020-8-2
    29
    刘恒_319495 从头到位只有一张图,怎么解????
    参考这个帖子看看可以吗
    https://forum.uibot.com.cn/thread-6350.htm
  • 新人小牛 2020-8-10
    30
    滑块每次更新页面都会改变上下位置,但是滑块最终目标位置与滑块首次出现的位置距离是一致的,这种滑块验证码怎么解决?是不是可以先通过鼠标识别滑块位置,然后捕获鼠标位置,然后滑动滑块到最终位置
  • Klose 2020-9-30
    31
    不需要那么复杂,实际上图片灰度识别就能搞定。。亲测成功率90%以上,加个try catch完美运行。
  • 新人小牛 2020-11-22
    32
    滑块移动位置纵坐标不变,横坐标做个循环
  • qiuhui 2020-12-23
    33
    怎么拿到原图呢,图片是动态生成的
  • 雷湘泉 2020-12-24
    34
    厉害!厉害!收藏了
  • andy_23052 2021-4-15
    35
    在excel中已经排版好格式的文本,通过设置元素文本输出后,变成一个个字母以打字的方式输出,没有了任何排版的格式,有办法解决吗,版本为5.5.0,谢谢大家
  • 黄翌青 2021-9-6
    36
    模块 crackGEE.CalcDistance 命令出错:image index out of range    咱回事,老大些
  • 李哥_490733 2021-9-7
    37
    学习
  • Zhang1115 2021-10-28
    38
    如果它只能鼠标点击的时候才显出图片,怎么才能截到图片呢
  • 39

    模块 crackGEE.CalcDistance 这个模块在那个类目里面找

  • 肖阳_593450 2021-12-16
    40

    已经通过查看源代码获取到了原图,但是不知道怎么替换luo大神脚本里面的原图,不懂代码真心学得很累,请大神指导一下,获取到的图片地址的JPg格式的,变量为sRet22。调试结果为--
    获取滑动验证码原图.task 第28行:"https://auth.zwfw.hunan.gov.cn/oauth2/images/ver-8.jpg"

  • 开怀 2023-8-22
    41
    大佬现在的高级题,x轴y轴都会变,div把图片分成很多小块的https://sim-academy.laiye.com/ly-chacha
    这种情况通用吗
  • 42
    求问各位大神    无缺口图 要怎么选取到呢???
  • zhou_178536 6月前
    43
    求问各位大神    无缺口图 要怎么选取到呢
  • 99网络 6月前
    44
    zhou_178536 求问各位大神 无缺口图 要怎么选取到呢
    F12分析代码,直到图片所在位置后,再用ui分析器来定位到对应元素,然后通过获取元素属性的方式获取到图片链接
返回
发新帖