美文网首页
(新版)正方系统滑动验证码识别

(新版)正方系统滑动验证码识别

作者: dairoot | 来源:发表于2021-06-26 14:03 被阅读0次

    步骤一:点击数据分析

    点击滑动按钮,将发送一个请求到 /zfcaptchaLogin
    请求内容

    "type": "verify"
    "rtk": "6cfab177-afb2-434e-bacf-06840c12e7af"
    "time": "1624611806948"
    "mt": "W3sieCI6OTY1LCJ5IjoxNjksInQiOjE2MjQ2MTE4MDY4Njh9LHsieCI6OTY1LCJ5IjoxNjksInQiOjE2MjQ2MTE4MDY5NDh9XQ=="
    "instanceId": "zfcaptchaLogin"
    "extend": "eyJhcHBOYW1lIjoiTmV0c2NhcGUiLCJ1c2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEwNiBTYWZhcmkvNTM3LjM2IiwiYXBwVmVyc2lvbiI6IjUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEwNiBTYWZhcmkvNTM3LjM2In0="
    

    通过 base64 解密 mtextend 得出解密的数值

    # mt
    [{"x":965,"y":169,"t":1624611806868},{"x":965,"y":169,"t":1624611806948}]
    # extend
    {"appName":"Netscape","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36","appVersion":"5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36"}
    

    mt 为用户的点击行为,x为X轴上的值,y为Y轴上的值,t为时间戳。通过大量点击分析,发现x值最小值为 950,得出950 为 X轴的起点,y值随机无固定值。
    extend 为请求头部内容

    步骤二:滑动验证码图像分析,计算滑动距离x值

    将图像灰度化,通过getpixel可以获取图像某一点的颜色值, 颜色值越高代表图像越浅,所以寻找纵向连续50个像素点均是 getpixel(x+1, y) > getpixel(x, y)(X轴=x 比 X轴=x+1 颜色浅)
    并扫描图像,当x=130、扫描高度=50时,的颜色比x+1时深。

    from PIL import Image
    import matplotlib.pyplot as plt
    import numpy as np
    
    scanf_height= 50 # 扫描的高度
    img = Image.open("zfcaptchaLogin.png")
    
    
    def contrast(imgl, x, y,scanf_height):
        # 黄框颜色值比红框颜色值浅的个数
        count = 0
        for i in range(scanf_height):
            if imgl.getpixel((x+1, y+i)) > imgl.getpixel((x, y+i)):
                count += 1
        # 当 count = scanf_height, 代表黄条区域 整体 红条区域 颜色值浅,则是验证码框位置
        return count
    
    
    def scanf(img):
        imgx, imgy = img.size
        imgl = img.convert('L') # 图像灰度化
        plt.yticks([])
        plt.xticks([i for i in range(0, imgx, 25)])
        plt.imshow(img)
        plt.pause(0.5)
        for y in range(0, imgy-scanf_height, 10):
            plt.pause(0.01)
            plt.clf()
            plt.yticks([])
            plt.xticks([i for i in range(0, imgx, 25)])
            plt.imshow(imgl, cmap=plt.cm.gray)
            for x in range(1, imgx-1, 1):
                plt.pause(0.0001)
                plt.plot([x-1,x-1], [y, y+scanf_height], color='white')
                plt.plot([x,x], [y, y+scanf_height], color='red')
                plt.plot([x+1,x+1], [y, y+scanf_height], color='yellow')
                count = contrast(imgl, x,y, scanf_height)
                plt.title('count: {}'.format(count) )
    
                print("x,y=[{}, {}], 黄条区域值比红条区域颜色值浅的个数:{}".format(x,y, count))
                if count == scanf_height:
                    return
    
    
    scanf(img)
    plt.show()
    

    优化代码计算x,y值


    import json
    import random
    import time
    from io import BytesIO
    
    from PIL import Image
    
    
    class ZfCaptchaRecognit(object):
        def __init__(self, img_path):
            self.img = Image.open(img_path)
    
        def _get_xy(self):
            # 计算 x,y 值
            def _is_dividing_line(img_l, x, y):
                for n in range(50):
                    # 寻找纵向连续50个像素点均是 X=x 比 X=x+1 颜色深
                    if y + n >= img_l.size[1] or x >= img_l.size[0] - 1:
                        return False
                    if img_l.getpixel((x + 1, y + n)) - img_l.getpixel((x, y + n)) < 2:
                        return False
                return True
    
            img_l = self.img.convert("L")
            for x in range(img_l.size[0]):
                for y in range(img_l.size[1]):
                    if _is_dividing_line(img_l, x, y):
                        return (x, y)
    
    
        def show_tag(self):
            # 展示 切分点
            X, Y = self._get_xy()
            img2 = Image.new("RGB", self.img.size, (255, 255, 255))
            for x in range(self.img.size[0]):
                for y in range(self.img.size[1]):
                    pix = self.img.getpixel((x, y))
                    img2.putpixel((x, y), pix)
                    if x == X or y == Y:
                        img2.putpixel((x, y), 225)
    
            img2.save("show_tag.png")
            img2.show()
    
    
    captcha = ZfCaptchaRecognit("zfcaptchaLogin.png")
    captcha.show_tag()
    

    步骤三:生成提交参数

    通过 步骤一得出x值最小为950,y值无规律
    则提交参数mt的大致格式数据是

    [{
        "x":950+ 滑动距离 + 浮动值,  #  浮动值的范围通过分析提交参数得出在10~20内
        "y":random.randint(150, 190),  # 无规律,暂定150到190范围内
        "t":int(time.time() * 1000)},  # 时间戳
     ...]
    

    获取mt 参数

    import json
    import random
    import time
    from io import BytesIO
    
    from PIL import Image
    
    
    class ZfCaptchaRecognit(object):
        def __init__(self, img_stream):
            obj = BytesIO(img_stream)
            self.img = Image.open(obj)
    
        def _get_xy(self):
            ...
    
        def generate_payload(self):
            base_x = 950
            X, Y = self._get_xy()
            payloads = [{"x": base_x + random.randint(5, 20), "y": random.randint(150, 190), "t": int(time.time() * 1000)}]
            for i in range(random.randint(15, 30)):
                # 在上一个参数基础下浮动
                last_payload = payloads[-1].copy()
                payloads[0]["x"] += random.choice([0] * 8 + [1, -1] * 2 + [2, -2])
                last_payload["t"] += random.randint(1, 20)
                last_payload["y"] += random.choice([0] * 8 + [1, -1] * 2 + [2, -2])
                payloads.append(last_payload)
    
            payloads[-1]["x"] = base_x + random.randint(10, 20) + X
            return json.dumps(payloads)
    
    captcha = ZfCaptchaRecognit("zfcaptchaLogin.png")
    captcha. generate_payload()
    

    相关文章

      网友评论

          本文标题:(新版)正方系统滑动验证码识别

          本文链接:https://www.haomeiwen.com/subject/naeoyltx.html