python authenticator identification polar sliding authenticator identification

Keywords: Python Selenium

Catalog

1: Introduction to Polar Sliding Authentication Code

_In recent years, there have been some new types of verification codes that do not want the old ones to be unfriendly to human beings, but the difficulty of identifying them has increased several levels.Therefore, other means are needed.

_Identify the required python libraries: selenium and ChromeDriver drivers, which are different for different browsers to download.

Authentication Code Access Website: http://www.geetest.com/

_Polar sliding validation code has reached version 3.0. It is more difficult to identify related graphic validation code. The principle is to drag the picture to the gap, then validate the combined image, which generates three encryption parameters, submit the form to the background, and then validate in the background.

Pyramid also adds machine learning methods to identify malicious programs. It takes only 0.4 seconds to prevent simulation, counterfeiting and violence, and to protect resources from abuse and theft.

_Our programs generally do so as long as they are not crawled maliciously and follow the crawl protocol.Never burden the server.

2. Ideas for Identifying Polar Sliding Verification Codes

_Here we can complete the validation by simulating the browser action, and complete the validation by fully simulating the human behavior with Selenium.

_is mainly divided into three steps

(1) Simulated click validation button

(2) Identifying the position of the sliding notch

(3) Simulated dragging slider

Step (1) is also better to say that step (2) is the key step to identify the location of the interface. Image processing is used to see the location of the interface, and the method of comparison detection with the original image is used to identify the location of the gap.Get two pictures at the same time, set a contrast threshold, and then traverse the two pictures to find the pixels with the same RGB difference exceeding this threshold, then the pixel point location is the location of the gap.

_Step (3) is more difficult because the human movement track is accelerated then decelerated, and methods such as uniform and random movement cannot be verified. To simulate this process well.

3: Polar Verification Code Recognition

1. Authentication code official website: https://auth.geetest.com/login/

The official pictures are:

2. Initialize configuration

# Registered username and password
email = ''
password = ''

class CrackGeetest():
    def __init__(self):
        self.url = 'https://account.geetest.com/login'
        self.browser = webdriver.Chrome()
        self.wait = WebDriverWait(self.browser, 20)
        self.email = email
        self.password = password

3. Analog Click

_The first step in identifying a verification code is to simulate clicking on the initial verification button and obtaining it by waiting explicitly.

def get_geetest_button(self):
        """
        Get Initial Validation Button
        Return value: Button object
        """
        button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
        return button

_Simulate clicks at the call location:

# Click the Verify button
button = self.get_geetest_button()
button.click()

4. Identify gaps

_Next, identify the location of the gap. First, get two pictures and compare them. The different location is the gap.

Get pictures without gaps.Select a picture element with selenium to get a screenshot of the whole web page and clip it. The code is as follows:

def get_screenshot(self):
        """
        //Get Screenshot of Web Page
        :return: Screenshot object
        """
        screenshot = self.browser.get_screenshot_as_png()
        screenshot = Image.open(BytesIO(screenshot))
        return screenshot

    def get_position(self):
        """
        //Get Authentication Code Location
        :return: Authentication code position tuple
        """
        img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
        time.sleep(2)
        location = img.location
        size = img.size
        top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
            'width']
        return (top, bottom, left, right)

    def get_geetest_image(self, name='captcha.png'):
        """
        //Get Authentication Code Picture
        :return: Picture object
        """
        top, bottom, left, right = self.get_position()
        print('Verification Code Location', top, bottom, left, right)
        screenshot = self.get_screenshot()
        captcha = screenshot.crop((left, top, right, bottom))
        captcha.save(name)
        return captcha

_Next, you need to get the second picture, which is a picture with a gap. Just click on the slider below to get the gap. The code is as follows:

def get_slider(self):
        """
        //Get Slider
        :return: Slider object
        """
        slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
        return slider

_click() triggers the click as follows:

# Point by Exhalation Gap
slider = self.get_slider()
slider.click()

_Next, by comparing pictures to get gaps, by traversing each coordinate point on the picture, get RGB data of the corresponding pixel points of two pictures.If within a certain range, it means two pixels are the same, and continues to compare the next pixel point.If the difference exceeds a certain range, it means that the pixel points are different, and the current location is the gap location.This is done by setting a threshold value, which is coded as follows:

def is_pixel_equal(self, image1, image2, x, y):
        """
        //Determine if two pixels are the same
        :param image1: Picture 1
        :param image2: Picture 2
        :param x: position x
        :param y: position y
        :return: Are the pixels the same
        """
        # Pixel points from two pictures
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 60
        if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
                pixel1[2] - pixel2[2]) < threshold:
            return True
        else:
            return False

    def get_gap(self, image1, image2):
        """
        //Get Gap Offset
        :param image1: Pictures without gaps
        :param image2: Picture with gaps
        :return:
        """
        left = 60
        for i in range(left, image1.size[0]):
            for j in range(image1.size[1]):
                if not self.is_pixel_equal(image1, image2, i, j):
                    left = i
                    return left
        return left

5. Simulate dragging

_Simulated dragging is not complicated, but it has more details.Drag the slider to the corresponding position with the relevant function.But if you drag at a uniform speed, you will inevitably recognize that it is a program, not a human operation, because humans cannot drag at a perfectly uniform speed, and it will recognize that it is a machine operation, causing the verification code to fail.

_Through different methods of detection, we found that the validation can be completed by uniformly accelerating the front slider and uniformly decelerating the rear slider.

_Here the acceleration is expressed in a, the current velocity in v, the initial velocity in vo, the displacement in x, and the time in t.

The code is as follows:

def get_track(self, distance):
        """
        //Obtaining a moving track from an offset
        :param distance: Offset
        :return: Moving Track
        """
        # Moving Track
        track = []
        # Current Displacement
        current = 0
        # Deceleration threshold
        mid = distance * 4 / 5
        # Calculate Interval
        t = 0.2
        # Initial velocity
        v = 0
        
        while current < distance:
            if current < mid:
                # Acceleration is positive 2
                a = 2
            else:
                # Acceleration is negative 3
                a = -3
            # Initial velocity v0
            v0 = v
            # Current speed v = v0 + at
            v = v0 + a * t
            # Move distance x = v0t + 1/2 * a * t^2
            move = v0 * t + 1 / 2 * a * t * t
            # Current Displacement
            current += move
            # Join Track
            track.append(round(move))
        return track
    
    def move_to_gap(self, slider, track):
        """
        //Drag slider to notch
        :param slider: slider
        :param track: trajectory
        :return:
        """
        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.5)
        ActionChains(self.browser).release().perform()

6:All Code

import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

EMAIL = 'cqc@cuiqingcai.com'
PASSWORD = ''
BORDER = 6
INIT_LEFT = 60

# Registered username and password
email = ''
password = ''


class CrackGeetest():
    def __init__(self):
        self.url = 'https://account.geetest.com/login'
        self.browser = webdriver.Chrome()
        self.wait = WebDriverWait(self.browser, 20)
        self.email = email
        self.password = password
    
    def __del__(self):
        self.browser.close()
    
    def get_geetest_button(self):
        """
        //Get Initial Validation Button
        //Return value: Button object
        """
        button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
        return button
    
    def get_screenshot(self):
        """
        //Get Screenshot of Web Page
        :return: Screenshot object
        """
        screenshot = self.browser.get_screenshot_as_png()
        screenshot = Image.open(BytesIO(screenshot))
        return screenshot

    def get_position(self):
        """
        //Get Authentication Code Location
        :return: Authentication code position tuple
        """
        img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
        time.sleep(2)
        location = img.location
        size = img.size
        top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
            'width']
        return (top, bottom, left, right)
    
    def get_geetest_image(self, name='captcha.png'):
        """
        //Get Authentication Code Picture
        :return: Picture object
        """
        top, bottom, left, right = self.get_position()
        print('Verification Code Location', top, bottom, left, right)
        screenshot = self.get_screenshot()
        captcha = screenshot.crop((left, top, right, bottom))
        captcha.save(name)
        return captcha

    def get_slider(self):
        """
        //Get Slider
        :return: Slider object
        """
        slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
        return slider
    
    def open(self):
        """
        //Open Web page to enter user name password
        :return: None
        """
        self.browser.get(self.url)
        email = self.wait.until(EC.presence_of_element_located((By.ID, 'email')))
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'password')))
        email.send_keys(self.email)
        password.send_keys(self.password)
    
    def is_pixel_equal(self, image1, image2, x, y):
        """
        //Determine if two pixels are the same
        :param image1: Picture 1
        :param image2: Picture 2
        :param x: position x
        :param y: position y
        :return: Are the pixels the same
        """
        # Pixel points from two pictures
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 60
        if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
                pixel1[2] - pixel2[2]) < threshold:
            return True
        else:
            return False

    def get_gap(self, image1, image2):
        """
        //Get Gap Offset
        :param image1: Pictures without gaps
        :param image2: Picture with gaps
        :return:
        """
        left = 60
        for i in range(left, image1.size[0]):
            for j in range(image1.size[1]):
                if not self.is_pixel_equal(image1, image2, i, j):
                    left = i
                    return left
        return left
    
    def get_track(self, distance):
        """
        //Obtaining a moving track from an offset
        :param distance: Offset
        :return: Moving Track
        """
        # Moving Track
        track = []
        # Current Displacement
        current = 0
        # Deceleration threshold
        mid = distance * 4 / 5
        # Calculate Interval
        t = 0.2
        # Initial velocity
        v = 0
        
        while current < distance:
            if current < mid:
                # Acceleration is positive 2
                a = 2
            else:
                # Acceleration is negative 3
                a = -3
            # Initial velocity v0
            v0 = v
            # Current speed v = v0 + at
            v = v0 + a * t
            # Move distance x = v0t + 1/2 * a * t^2
            move = v0 * t + 1 / 2 * a * t * t
            # Current Displacement
            current += move
            # Join Track
            track.append(round(move))
        return track
    
    def move_to_gap(self, slider, track):
        """
        //Drag slider to notch
        :param slider: slider
        :param track: trajectory
        :return:
        """
        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.5)
        ActionChains(self.browser).release().perform()
    
    def login(self):
        """
        //Sign in
        :return: None
        """
        submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))
        submit.click()
        time.sleep(10)
        print('Login Successful')
    
    def crack(self):
        # Enter User Name Password
        self.open()
        # Click the Verify button
        button = self.get_geetest_button()
        button.click()

        # Get Authentication Code Picture
        image1 = self.get_geetest_image('captcha1.png')
        # Point by Exhalation Gap
        slider = self.get_slider()
        slider.click()
        # Get pictures of authentication codes with gaps
        image2 = self.get_geetest_image('captcha2.png')
        # Get Gap Location
        gap = self.get_gap(image1, image2)
        print('Gap location', gap)
        # Minus notch displacement
        gap -= BORDER
        # Getting a moving track
        track = self.get_track(gap)
        print('Slide Track', track)
        # Drag slider
        self.move_to_gap(slider, track)
        
        success = self.wait.until(
            EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), 'Verification successful'))
        print(success)
        
        # Retry after failure
        if not success:
            self.crack()
        else:
            self.login()


if __name__ == '__main__':
    crack = CrackGeetest()
    crack.crack()

This method is suitable for different polar sliding verification codes. The key lies in the idea of identification, how to identify the location of gaps, how to generate motion tracks and so on.Similar validation codes can be encountered later for this purpose.

Posted by Jay87 on Sat, 23 Nov 2019 01:41:05 -0800