尝试绕过验证码

2020-06-16 15:13:20 浏览数 (1)

最近出了点安全事故,有人盗号。而且手段极其简单,就是暴力破解。 为了提高安全性,UI的界面加了验证机制。这也为自动化测试提高了难度。

按照一般的做法,是先截取页面的图,然后通过坐标位置,来定位验证码的位置,然后截取验证码。 效果如下

我首先想到的是,通过mitmproxy拿到图片的url来获取图片,进而来识别图片的文字,发现那url,每次请求都会变化。

于是只能用截图的方式了。

对于web来说,可以通过元素,根据attribute来获取URL,来获取验证码图片,并将图片放大,这样识别的效果更好。

对于App来说,如果不能通过URL来获取,那只能通过截图的方式来碰碰运气了。 以下是App的方式。

代码语言:javascript复制
driver.save_screenshot("login.png")
captcha=driver.find_element_by_xpath("//android.view.View[3]/android.widget.Image")
left = captcha.location['x']  # 区块截图左上角在网页中的x坐标
top = captcha.location['y']  # 区块截图左上角在网页中的y坐标
right = left   captcha.size['width']  # 区块截图右下角在网页中的x坐标
bottom = top   captcha.size['height']  # 区块截图右下角在网页中的y坐标

picture = Image.open(r'login.png')
picture = picture.crop((left, top, right, bottom))  # 二次截图:形成区块截图
imgry = picture.convert('L')  # 图像加强,二值化
sharpness = ImageEnhance.Contrast(imgry)  # 对比度增强
sharp_img = sharpness.enhance(2.0)
sharp_img.save(r'captcha.png')
code = test()
print(code)

图片有了,怎样识别文字能,用OCR来识别,可以去这里下载一个 https://digi.bib.uni-mannheim.de/tesseract/ 然后安装:

代码语言:javascript复制
python -m pip install --upgrade pip
pip3 install tesserocr pillow

运行以下代码来识别图片。

代码语言:javascript复制
def test():


    image = Image.open(image_path)

    text = pytesseract.image_to_string(image)  # 使用简体中文解析图片
    print(str(text))

    return str(text)

可能会出错:

代码语言:javascript复制
pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your path

解决方法: 1.找到python的安装路径下的pytesseract: 例如我的是 E:Python3Libsite-packagespytesseract

2.用文本编辑器打开,查找tesseract_cmd

将原来的 tesseract_cmd = 'tesseract' 改为: tesseract_cmd = 'OCR的安装路径下的tessract.exe'

例如我的是 tesseract_cmd = 'C:Program FilesTesseract-OCRtesseract.exe' 然后运行,就成功了。 等一下,发现运行很多次,有的时候能够完全识别,有的时不能,识别率真的不高。现在还是很简单的情况下,如果更多干扰,那更不行了。

于是在网上搜索提高识别率的,比如增加灰度等,还是没什么用,最后听说搞AI识别,能训练啥的,就用了一个百度AI识别的,一天免费500次。

代码语言:javascript复制
import requests
import json
import base64
import time


class baiduCode(object):

    @classmethod
    def get_token(cls):
        """
        当前函数只用调用一次,用来获取当前账号的token
        :return:
        """
        # 标记当前精准识别是否使用完
        cls.curr_url = ''
        cls.basic_flag = False
        cls.max_num = 100
        cls.login_url = 'https://aip.baidubce.com/oauth/2.0/token'
        cls.login_params = {
            'grant_type': 'client_credentials',
            'client_id': 'wAIXfXOUS8ztLa4FrK3rZex1',
            'client_secret': '3b8nvjSGUZq0LPC18VVAizKYRBbny6Mq'
        }
        cls.headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
        response = requests.post(cls.login_url, params=cls.login_params, headers=cls.headers)
        result = json.loads(response.text)
        cls.token = result['access_token']

    @classmethod
    def get_code(cls, path='', url=''):
        cls.num = 0
        # 普通图片识别的请求链接,正确率50%(测试了500张图片)
        cls.general_basic_url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic'
        # 高精度图片识别的请求链接,正确率80%(测试了500张图片)
        cls.accurate_basic_url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic'
        if path:
            if not cls.basic_flag:
                cls.curr_url = cls.accurate_basic_url
            with open(path, 'rb') as f:
                base64_data = base64.b64encode(f.read())
                base = base64_data.decode()
                cls.basic_params = {
                    'image': base,
                    'access_token': cls.token
                }
        elif url:
            cls.curr_url = cls.general_basic_url
            cls.basic_params = {
                'url': url,
                'access_token': cls.token
            }
        else:
            raise ValueError('当前path和url参数均错误')
        time.sleep(0.5)
        while True:
            try:
                response = requests.post(cls.curr_url, params=cls.basic_params, headers=cls.headers)
                result = json.loads(response.text)
                # 判断当前精准识别是否被使用完
                try:
                    result['words_result']
                except Exception as e:
                    print(e)
                    cls.num  = 1
                    if cls.num > cls.max_num:
                        raise ValueError('当前尝试的错误次数超过%d次,请重新调用'%(cls.max_num))
                    cls.basic_flag = True
                    cls.curr_url = cls.general_basic_url
                    continue
                code = result['words_result'][0]['words']
                return code
                break
            except Exception as e:
                print('error: ', e)
                cls.num  = 1
                if cls.num > cls.max_num:
                    raise ValueError('当前尝试的错误次数超过%d次,请重新调用'%(cls.max_num))


if __name__ == '__main__':
    # 当前获取token的函数只用调用一次
    baiduCode.get_token()
    # 直接传图片的地址就好
    image_path = "C:\work\code\android\captcha.png"
    code = baiduCode.get_code(path=image_path)
    print(code)

用起来效果不好,还不如OCR呢。

看来得想别的方法绕过去了。

0 人点赞