[639]极验验证码破解分析

极验验证码相关文档:http://www.wzdr.cn/article-725.html
极验验证码登录注册地址:http://jiyan.c2567.com/index.php/login/index.html(不能用中文帐号注册)
利用极验接口破解验证码:https://blog.csdn.net/zhangmengran/article/details/82901891


首先介绍一下 极验验证:

1. 抓取极验参数

任何一个网站,如果在登录时网站接入的极验的接口,那么该网站就可以使用极验验证码进行登录,此时极验验证码API就会返回两个极验参数,gt和challenge,这两个参数只跟极验验证码API相关,跟这个网站没有任何关系。

注意:
有的网站是直接调用极验官方提供的验证码接口: 比如:极验的官方后台,https://auth.geetest.com/api/init_captcha?time=1538185553190
有的网站又对极验验证码接口封装了一个API接口: 比如: 魅族登录:https://login.flyme.cn/sec/geetest3?t=1538185653356 工商信息网:http://www.gsxt.gov.cn/SearchItemCaptcha

2. 将获取的极验参数,提交给极验破解网站(http://jiyan.c2567.com)的识别接口,会得到新的返回值:

这里的返回值就相当于你在网站页面中将滑块滑到正确的位置得到的加密结果。

{ 
status: "ok", # 就相当于你的滑块是否滑动到正确位置,正确的话就是OK,错误就是Error 
challenge: "3d033f099597f5ae63e2e2c902301d183z", 
validate: "8f6ebd56291ed6569ac40c1d74780985" 
} 

3. 将上述参数challenge和validate,混合着网站自己提交的参数向网站自己的url发送POST/GET请求即可。

下面写一个小实例:

利用极验破解接口(http://jiyan.c2567.com),模拟极验官方网站后台登录(https://auth.geetest.com/login)

第一个函数要请求的网址以及返回的响应
[639]极验验证码破解分析_第1张图片

第二个函数要请求的网址以及返回的响应
[639]极验验证码破解分析_第2张图片

第三个函数要请求的网址以及返回的响应
[639]极验验证码破解分析_第3张图片

创建一个名字为geetest.py文件,具体代码如下:

import requests, json
from http.cookiejar import LWPCookieJar
 
class JiYanLoginSpider(object):
    def __init__(self):
        # 极验官方登录用户
        self.jiyan_user = ''
        # 极验破解网站登录用户
        self.pojie_user = ''
        self.password = ''
 
        # 初始化session对象,用于保存登录成功后的cookie信息。
        self.session = requests.Session()
        self.session.cookies = LWPCookieJar(filename='jy_cookie.txt')
 
        # 配置session的全局请求头
        self.session.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
        }
 
    def get_jiyan_params(self):
        """
        分析要爬取网站中调用极验验证API的url地址,访问这个url地址,获取gt和challenge参数
        :return:
        """
        try:
            response = self.session.get('https://auth.geetest.com/api/init_captcha')
            return json.loads(response.text).get('data')
        except Exception as e:
            print('极验参数获取失败:',e)
            return None
 
    def jiyan_shibie(self, jiyan_params):
        """
        根据get_jiyan_params()函数返回的值,调用极验破解网站的识别接口,开始识别验证码,验证码识别成功,会返回两个加密数据,challenge和validate。
        :param jiyan_params: 获取的极验参数
        :return:
        """
        pojie_url = 'http://jiyanapi.c2567.com/shibie?gt={}&challenge={}&referer=https://auth.geetest.com&user={}&pass={}&return=json&model=3&format=utf8'.format(jiyan_params.get('gt'), jiyan_params.get('challenge'), self.pojie_user, self.password)
        # 发送请求,开始破解验证码
        try:
            response = self.session.get(pojie_url).text
            result_status = json.loads(response).get('status')
            if result_status == "ok":
                # 破解成功
                return json.loads(response)
            else:
                # 破解失败
                print('破解失败,需要重新获取极验参数,再重新破解。')
                return None
        except Exception as e:
            print('识别请求解析失败,',e)
            return None
 
    def login(self, result_dict):
        """
        根据验证码识别结果,进行模拟登录
        :param result_dict:
        :return:
        """
        loging_url = "https://auth.geetest.com/api/login"
        login_params = {
            # 网站参数
            'email': self.jiyan_user,
            'password': self.password,
            # 验证码识别后的结果参数
            'geetest_challenge': result_dict.get('challenge'),
            'geetest_validate': result_dict.get('validate'),
            'geetest_seccode': result_dict.get('validate') + '|jordan'
        }
        response = self.session.post(loging_url, data=json.dumps(login_params))
        if response.status_code == 200:
            print('登录成功:', response.cookies)
            self.session.cookies.save(ignore_discard=True, ignore_expires=True)
        else:
            print('登录失败:', response.status_code, response.text)
 
 
if __name__ == "__main__":
    jy = JiYanLoginSpider()
    jy_params = jy.get_jiyan_params()
    if jy_params:
        result = jy.jiyan_shibie(jy_params)
        if result:
            jy.login(result)

整个过程的流程如下图:
[639]极验验证码破解分析_第4张图片

识别接口网址所要携带的参数如下图:
[639]极验验证码破解分析_第5张图片

项目环境

大致需要用到以下模块各位看观请提前准备好:
python3.6、selenium、numpy、PIL、chromedriver

分析步骤以及代码编写

  1. 首先分析目标网站(本次主要以geetest官网滑块demo为参考)

[639]极验验证码破解分析_第6张图片

网站大致长这个样子,首先f12打开 开发者工具选择Elements查看节点,发现最新版本的滑块图片是使用画布来进行呈现的,期间查阅大量文档,使用如下代码获得画布中的图片数据,获取到的图片是base64进行编码的

document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png")

[639]极验验证码破解分析_第7张图片

2.通过分析发现这两个画布放的是所需要的背景图和缺口图(其实一眼就看出来的)

image

  1. 接下来就是代码的编写了
    3.1 首先是获得背景图和缺口图的数据
    def get_images(self):
        """
        获取验证码图片
        :return: 图片的location信息
        """
        time.sleep(1)
        self.browser.web_driver_wait_ruishu(10, "class", 'geetest_canvas_slice')
        fullgb = self.browser.execute_js('document.getElementsByClassName("geetest_canvas_bg geetest_'
                                             'absolute")[0].toDataURL("image/png")')["value"]
        bg = self.browser.execute_js('document.getElementsByClassName("geetest_canvas_fullbg geetest_fade'
                                         ' geetest_absolute")[0].toDataURL("image/png")')["value"]
        return bg, fullgb

3.2 对数据进行解码操作并保存图片

    def get_decode_image(self, filename, location_list):
        """
        解码base64数据
        """
        _, img = location_list.split(",")
        img = base64.decodebytes(img.encode())
        new_im: image.Image = image.open(BytesIO(img))
        return new_im

3.3 接下来就是计算缺口位置了(这里使用的PIL中计算两张图片的差值获得缺口位置)

    def compute_gap(self, img1, img2):
        """计算缺口偏移 这种方式成功率很高"""
        # 将图片修改为RGB模式
        img1 = img1.convert("RGB")
        img2 = img2.convert("RGB")
        # 计算差值
        diff = ImageChops.difference(img1, img2)
        # 灰度图
        diff = diff.convert("L")
        # 二值化
        diff = diff.point(self.table, '1')
        left = 43
        # 这里做了优化为减少误差 纵坐标的像素点大于5时才认为是找到
        # 防止缺口有凸起时有误差
        for w in range(left, diff.size[0]):
            lis = []
            for h in range(diff.size[1]):
                if diff.load()[w, h] == 1:
                    lis.append(w)
                if len(lis) > 5:
                    return w

3.4 当滑块的缺口位置找到以后就需要生成滑动轨迹(其中加20是保证在滑动时先超过缺口位置然后在慢慢还原到正确位置)

    def ease_out_quart(self, x):
        return 1 - pow(1 - x, 4)
    def get_tracks_2(self, distance, seconds, ease_func):
        """
        根据轨迹离散分布生成的数学 生成  # 参考文档  https://www.jianshu.com/p/3f968958af5a
        成功率很高 90% 往上
        :param distance: 缺口位置
        :param seconds:  时间
        :param ease_func: 生成函数
        :return: 轨迹数组
        """
        distance += 20
        tracks = [0]
        offsets = [0]
        for t in np.arange(0.0, seconds, 0.1):
            ease = ease_func
            offset = round(ease(t / seconds) * distance)
            tracks.append(offset - offsets[-1])
            offsets.append(offset)
        tracks.extend([-3, -2, -3, -2, -2, -2, -2, -1, -0, -1, -1, -1])
        return tracks

以上来源:https://www.lizenghai.com/archives/4731.html


Python有一个神奇的库叫pyautogui, 可以操作键盘和鼠标。
pip install pyautogui

python selenium2 中的显示等待WebDriverWait与条件判断expected_conditions举例
:https://www.cnblogs.com/yuuwee/p/6635652.html
知乎 极验验证可以被破解吗?:https://www.zhihu.com/question/28833985
Python&按键精灵自动化:https://blog.csdn.net/whiterbear/article/details/50402096
Python调用(运行)外部程序:https://blog.csdn.net/fanhuajames/article/details/8781977

你可能感兴趣的:(爬虫)