xiaobanni / GeeTest_SlideVerificationCode

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

极验滑动验证码破解

最终代码:

easing.py

run.py

评测结果

6 8 10 12
ease_in_quad 41%/23% 72%/72% 62%/60% 32%/26%
ease_out_quad 72%/96% 88%/92% 77%/80% 50%/72%
ease_in_out_quad 92%/94% 95%/91% 87%/91% 93%/92%
ease_in_cubic 12%/16% 48%/41% 69%/67% 38%/54%
ease_out_cubic 98%/99% 94%/95% 83%/83% 62%/68%
ease_in_out_cubic 99%/98% 98%/66% 85%/87% 64%/50%
ease_in_quart【X】 14%/13% X 43%/53% 52%/59%
ease_out_quart 99%/100% 94%/96% 83%/80% 55%/62%
ease_in_out_quart 92%/94% 94%/98% 85%/80% 76%/75%
ease_in_quint【X】 5%/5% X 27%/28% 22%/23%
ease_out_quint 99%/95% 96%/98% 80%/64% 45%/51%
ease_in_out_quint 98%/88% 87%/93% 87%/81% 28%/41%
ease_in_sine 51%/55% 51%/50% 61%/51% 31%/35%
ease_out_sine 98%/94% 92%/93% 81%/88% 50%/69%
ease_in_out_sine 90%/93% 97%/97% 91%/87% 84%/88%
ease_in_expo【X】 0%/0% X 21%/20% 47%/46%
ease_out_expo 100%/100% 100%/99% 83%/89% 61%/75%
ease_in_out_expo 98%/98% 97%/96% 93%/87% 92%/90%
ease_in_cric【X】 X X X X
ease_out_cric 100%/99% 100%/94% 88%/89% 75%/79%
ease_in_out_cric 97%/100% 99%/99% 90%/93% 96%/97%
ease_in_elastic【X】 X X X X
ease_out_elastic 98%/99% 95%/83% 56%/60% 9%/17%
ease_in_out_elastic 92%/92% 68%/57% 54%/68% 72%/78%
ease_in_back【X】 3%/2% 28%/24% 54%/68% 34%/33%
ease_out_back 96%/98% 94%/93% 73%/74% 59%/62%
ease_in_out_back 95%/94% 87%/89% 81%/81% 85%/82%
ease_in_bounce【X】 46%/48% 27%/25% 29%/20% 23%/28%
ease_out_bounce 100%/99% 67%/76% 39%/42% 91%/92%
ease_in_out_bounce 73%/57% 67%/70% 16%/23% 19%/13%

总体上看,参数为6和8的正确率较10和12高,于是将6和8下的准确率作为评判标准。

标红的是在6和8下准确率超过了85%,甚至能达到95%~100%的轨迹(共16个)

附轨迹图片和轨迹源码网址:https://github.com/gdsmith/jquery.easing/blob/master/jquery.easing.js

1553668216481


# -*- coding: utf-8 -*-
# easing.py

import numpy as np
import math

c1 = 1.70158
c2 = c1 * 1.525
c3 = c1 + 1
c4 = ( 2 * math.pi ) / 3
c5 = ( 2 * math.pi ) / 4.5

def ease_in_quad(x):
    return x * x

def ease_out_quad(x):
    return 1 - (1 - x) * (1 - x)

def ease_in_out_quad(x):
    if(x<0.5):
        return 2*x*x
    else:
        return 1-pow(-2 * x +2 , 2) / 2

#-----------------------------------------------

def ease_in_cubic(x):
    return x * x * x

def ease_out_cubic(x):
    return 1 - pow( 1-x, 3)

def ease_in_out_cubic(x):
    if x < 0.5:
        return 4 * x * x * x
    else:
        return 1 - pow( -2 * x + 2, 3 ) / 2

#-----------------------------------------------

def ease_in_quart(x):
    return x * x * x * x

def ease_out_quart(x):
    return 1 - pow(1 - x, 4)

def ease_in_out_quart(x):
    if x < 0.5:
        return 8 * x * x * x * x
    else:
        return 1 - pow( -2 * x + 2, 4 ) / 2

#-----------------------------------------------

def ease_in_quint(x):
    return  x * x * x * x * x

def ease_out_quint(x):
    return 1 - pow( 1 - x, 5 )

def ease_in_out_quint(x):
    if x < 0.5:
        return 16 * x * x * x * x * x
    else:
        return 1 - pow( -2 * x + 2, 5 ) / 2

#-----------------------------------------------

def ease_in_sine(x):
    return 1 - math.cos( x * math.pi/2 )

def ease_out_sine(x):
    return math.sin( x * math.pi/2 )

def ease_in_out_sine(x):
    return -( math.cos( math.pi * x ) - 1 ) / 2

#------------------------------------------------

def ease_in_expo(x):
    if x==0:
        return 0
    else:
        return pow( 2, 10 * x - 10)

def ease_out_expo(x):
    if x == 1:
        return 1
    else:
        return 1 - pow(2, -10 * x)

def ease_in_out_expo(x):
    if x==0:
        return 0
    else:
        if x==1:
            return 1
        else:
            if x<0.5:
                return pow( 2, 20 * x - 10 ) / 2
            else:
                return ( 2 - pow( 2, -20 * x + 10 ) ) / 2

#---------------------------------------------------------

def ease_in_cric(x):
    return 1 - math.sqrt( 1 - pow( x, 2 ) )

def ease_out_cric(x):
    return math.sqrt( 1 - pow( x - 1, 2 ) )

def ease_in_out_cric(x):
    if x < 0.5:
        return ( 1 - math.sqrt( 1 - pow( 2 * x, 2 ) ) ) / 2
    else:
        return ( math.sqrt( 1 - pow( -2 * x + 2, 2 ) ) + 1 ) / 2

#-----------------------------------------------------------------

def ease_in_bounce(x):
    return 1 - ease_out_bounce( 1-x )

def ease_out_bounce(x):
    n1 = 7.5625
    d1 = 2.75
    if x < 1 / d1 :
        return n1 * x * x
    elif x < 2 / d1:
        x -= 1.5 / d1
        return n1 * x*x + 0.75
    elif x < 2.5 / d1:
        x -= 2.25 / d1
        return n1 * x*x + 0.9375
    else:
        x -= 2.625 / d1
        return n1 * x*x + 0.984375

def ease_in_out_bounce(x):
    if x<0.5:
        return (1 - ease_out_bounce(1- 2 * x ) ) / 2
    else:
        return (1 + ease_out_bounce(2 * x -1 ) ) / 2
#-----------------------------------------------

def ease_in_elastic(x):
    if x==0:
        return 0
    else:
        if x==1:
            return 1
        else:
            return -pow( 2, 10 * x - 10 ) * math.sin( ( x * 10 - 10.75 ) * c4 )

def ease_out_elastic(x):
    if x == 0:
        return 0
    elif x == 1:
        return 1
    else:
        return pow(2, -10 * x) * math.sin((x * 10 - 0.75) * c4) + 1

def ease_in_out_elastic(x):
    if x==0:
        return 0
    else:
        if x==1:
            return 1
        else:
            if x <0.5:
                return -( pow( 2, 20 * x - 10 ) * math.sin( ( 20 * x - 11.125 ) * c5 )) / 2
            else:
                return pow( 2, -20 * x + 10 ) * math.sin( ( 20 * x - 11.125 ) * c5 ) / 2 + 1

#---------------------------------------------------------------

def ease_in_back(x):
    return c3 * x * x * x - c1 * x * x

def ease_out_back(x):
    return 1 + c3 * pow( x - 1, 3 ) + c1 * pow( x - 1, 2 )

def ease_in_out_back(x):
    if x<0.5:
        return ( pow( 2 * x, 2 ) * ( ( c2 + 1 ) * 2 * x - c2 ) ) / 2
    else:
        return ( pow( 2 * x - 2, 2 ) *( ( c2 + 1 ) * ( x * 2 - 2 ) + c2 ) + 2 ) / 2

#-------------------------------------------------------------


def get_tracks(distance, seconds, ease_func):
    tracks = [0]
    offsets = [0]
    for t in np.arange(0.0, seconds, 0.1):
        ease = globals()[ease_func]
        offset = round(ease(t/seconds) * distance)
        tracks.append(offset - offsets[-1])
        offsets.append(offset)

    return offsets, tracks
# -*- coding: utf-8 -*-
# run.py

import random
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
import time
import os
from selenium import webdriver
from PIL import Image #获取图像RGB值
import easing

class CrackGreetest():
    def __init__(self):
        self.url = "https://auth.geetest.com/login/"
        self.browser = webdriver.Chrome(executable_path = 'D:\\2019Download\\ChromeDriver\\chromedriver.exe')
        self.wait = WebDriverWait(self.browser,20)
        self.email = 'youremail@email.com'

    def open(self):
        """
    	打开网页输入用户名密码
    	:return:
    	"""
        self.browser.get(self.url)
        email = self.wait.until(expected_conditions.presence_of_element_located((By.CLASS_NAME,'ivu-input')))
        # password = self.wait.until(expected_conditions.presence_of_element_located((By.ID,'password')))
        email.send_keys(self.email)
        # password.send_keys(self.password)

    def get_geetest_button(self):
        '''
        获取初始验证码按钮
        :return: 按钮对象
        '''
        button = self.wait.until(expected_conditions.element_to_be_clickable((By.CLASS_NAME,'geetest_radar_tip')))
        return button

    def get_unfull_image(self, path):
        """
        获取未完整验证码图片
        :param name:
        :return: 图片对象
        """
        element = self.browser.find_element_by_css_selector(
            "body > div.geetest_fullpage_click.geetest_float.geetest_wind.geetest_slide3 > div.geetest_fullpage_click_wrap > div.geetest_fullpage_click_box > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > div > canvas.geetest_canvas_slice.geetest_absolute")

        unfull_captcha = element.screenshot(path)
        return unfull_captcha

    def get_full_image(self,path):
        '''
        获取完整验证码图片
        :param name:
        :return:
        '''
        # 这里要执行JavaScript脚本才能拿到完整图片的截图
        show_Full_img1 = "document.getElementsByClassName('geetest_canvas_fullbg')[0].style.display='block'"
        self.browser.execute_script(show_Full_img1)
        show_Full_img2 = "document.getElementsByClassName('geetest_canvas_fullbg')[0].style.opacity=1"
        self.browser.execute_script(show_Full_img2)
        # 等待完整图片加载
        time.sleep(2)
        element = self.browser.find_element_by_css_selector(
            "body > div.geetest_fullpage_click.geetest_float.geetest_wind.geetest_slide3 > div.geetest_fullpage_click_wrap > div.geetest_fullpage_click_box > div > div.geetest_wrap > div.geetest_widget > div > a > div.geetest_canvas_img.geetest_absolute > canvas")
        full_captcha = element.screenshot(path)
        return full_captcha

    def get_slider(self):
        """
        获取滑块
        :return: 滑块对象
        """
        slider = self.wait.until(expected_conditions.element_to_be_clickable((By.CLASS_NAME,'geetest_slider_button')))
        return slider

    def get_gap_left(self,image2):
        """
        获取滑块左端位置及滑块大小
        :param image2: 带缺口的图片
        :return: 滑块左端位置,滑块大小
        """
        st_black = 0
        st_yellow=0
        end_black=0
        end_yellow=0
        for i in range(0,image2.size[0]):
            #width
            ans=0
            for j in range(image2.size[1]):
                #height
                rgb = image2.load()[i, j]
                if  ( rgb[0]<120 and rgb[1] < 120 and rgb[2] <120):
                    ans=ans+1
                if(ans>10):
                    st_black = i
                    break
            if st_black>0:
                break

        for i in range(st_black,image2.size[0]):
            ans=0
            for j in range(image2.size[1]):
                #height
                rgb = image2.load()[i, j]
                if  ( rgb[0]>180 and rgb[1] > 180 and rgb[2] <200):
                    ans=ans+1
                if(ans>20):
                    st_yellow=i
                    break
            if st_yellow>0:
                break

        return st_yellow

        #暂时弃置
        for i in range(st_yellow+45,0,-1):
            #width
            ans=0
            for j in range(image2.size[1]):
                #height
                rgb = image2.load()[i, j]
                if  ( rgb[0]<120 and rgb[1] < 120 and rgb[2] <120):
                    ans=ans+1
                if(ans>15):
                    end_black = i
                    break
            if end_black>0:
                break

        for i in range(end_black,0,-1):
            ans=0
            for j in range(image2.size[1]):
                #height
                rgb = image2.load()[i, j]
                if  ( rgb[0]>165 and rgb[1] >165 and rgb[2] <200):
                    ans=ans+1
                if(ans>20):
                    end_yellow = i
                    break
            if end_yellow>0:
                break

        return st_yellow,end_yellow-st_yellow

    def get_gap_right(self,image1,image2):
        """
        获取缺口左端位置
        :param image1: 不带缺口的图片
        :param image2: 带缺口的图片
        :return: 像素是否相同
        """
        # 缺口在滑块右侧,设定遍历初始横坐标left为60
        left = 60
        # 像素对比阈值
        threshold = 50
        for i in range(image1.size[0]-1, left, -1):
            #width
            ans=0
            for j in range(image1.size[1]):
                #height
                rgb1 = image1.load()[i, j]
                rgb2 = image2.load()[i, j]

                res1 = abs(rgb2[0] - rgb1[0])
                res2 = abs(rgb2[1] - rgb1[1])
                res3 = abs(rgb2[2] - rgb1[2])
                if  (res1 > threshold or res2 > threshold or res3 > threshold):
                    ans=ans+1
                if(ans>15):
                    return i-41  # 返回缺口偏移距离,这里需测试几次


    def move_to_grap(self,slider,distance,paring):
        '''
        拖动滑块到缺口处
        :param slider: 滑块
        :param track: 轨迹
        :return:
        '''
        ans=random.randint(3,4)
        offsets, track = easing.get_tracks(distance, ans*2, paring)
        # print(offsets)
        # 调用ActionChains的click_and_hold()方法按住拖动底部滑块,遍历运动轨迹获取每小段位置距离,调用move_by_offset()方法移动此位移,最后调用release()方法松开鼠标
        ActionChains(self.browser).click_and_hold(slider).perform()
        for x in track:
            ActionChains(self.browser).move_by_offset(xoffset=x,yoffset=0).perform()
            time.sleep(0.01)
        time.sleep(0.3)
        ActionChains(self.browser).release().perform()

    def crack(self,paring):
        try:
            # 输入用户名
            self.open()

            # 模拟点击按钮
            button = self.get_geetest_button()
            button.click()
            time.sleep(2)

            tn=time.localtime(time.time())

            # 获取带缺口验证码图片
            unfull_path = 'D:\Documents\Desktop\TempHandle/ans/'+str(tn.tm_mday)+'.'+str(tn.tm_min)+'.'+str(tn.tm_sec)+'pic1.png'
            self.get_unfull_image(unfull_path)
            image1 = Image.open(unfull_path)

            # 获取完整的验证码图片
            full_path = 'D:\Documents\Desktop\TempHandle/ans/'+str(tn.tm_mday)+'.'+str(tn.tm_min)+'.'+str(tn.tm_sec)+'pic2.png'
            self.get_full_image(full_path)
            image2 = Image.open(full_path)
            #
            # 对比两张图片像素点,获取缺口位置,得到偏移距离
            # 获取缺口位置
            st=self.get_gap_left(image1)
            ed=self.get_gap_right(image1,image2)
            distance = ed-st
            # print("缺口距离",distance)

            # # 获取移动轨迹
            # track = self.get_track(distance)
            # print("滑动轨迹",track)

            # 模拟人的行为,拖动滑块,完成验证
            slider = self.get_slider()
            # slider.click()

            # 拖动滑块
            self.move_to_grap(slider, distance, paring)
            time.sleep(1)
            text=self.browser.find_element_by_class_name('geetest_result_title').get_attribute('textContent')

            return
            # if(text=='哇哦~怪物吃了拼图请 3 秒后重试'):
            #     res[1]+=1
            #     return
            # elif(text=='拖动滑块将悬浮图像正确拼合'):
            #     res[2]+=1
            #     return
            # else:
            #     #text=='sec 秒的速度超过 score% 的用户'
            #     res[0]+=1
            #     return
        except:
            return

if __name__ == "__main__":
    crack = CrackGreetest()

    parameter=['ease_in_out_quad','ease_out_cubic','ease_in_out_cubic','ease_out_quart',
               'ease_in_out_quart','ease_out_quint','ease_in_out_quint','ease_out_sine',
               'ease_in_out_sine','ease_out_expo','ease_in_out_expo','ease_out_cric',
               'ease_in_out_cric','ease_out_elastic','ease_out_back','ease_in_out_back']

    while(True):
        ans=random.randint(0,15)
        crack.crack( parameter[ans])

About


Languages

Language:HTML 85.1%Language:Python 14.9%