UiBot过滑动验证码检测实例

luo 27天前 1152

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

RPA,RPA机器人_UiBot过滑动验证码检测实例

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


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

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

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

3、滑动滑块到缺陷部分

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

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


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

RPA,RPA机器人_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})
结果:

RPA,RPA机器人_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)

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

RPA,RPA机器人_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", [])



视图:

RPA,RPA机器人_UiBot过滑动验证码检测实例

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

RPA,RPA机器人_UiBot过滑动验证码检测实例

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



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







上传的附件:
最新回复 (6)
  • 99365 24天前
    2
    不错哦, 非常好的一篇文章
  • 曹爽 21天前
    3
    对于我们这种小白,实在太难了,不过这个太实用了,慢慢学。
  • 疯狂、白糖 20天前
    4
    给力哇
  • MAKI 18天前
    5
    罗大多出点干货哦,都巴巴等着呢
  • 沈博文 17天前
    6
    落地的方案
  • 礼拜五 4天前
    7
    很厉害,用按键精灵的明度检测也很容易做得出
返回
发新帖