笔记-滑块验证码

笔记-滑块验证码

 

 

1.      滑块验证码破解

以bilibili为例,尝试破解它的滑块验证。

 

1.1.    思路

基本思路是模拟人的行为,点击按钮,拖动拼图完成验证。

 

主要有以下几个问题:

  1. 怎么请求图片?

目前是通过找到图片url,直接请求,但这非常容易识别然后被ban;

  1. 图片是碎片化的,需要拼接。

纯手工作业,还是可以解决的。

  1. 如何找到验证图片的缺口位置?

比较RGB像素。

  1. 在拖动滑块时如何模拟人的操作

分析人滑动滑块时会出现的动作 

首先,刚开始距离缺口很远,滑动得很快,快要接近缺口了,滑动减慢 

最后还要确定一下是否对齐,所以要在原地停留1秒的样子

 

1.2.    代码

环境:python 3.6.4,selenium,chrome,PIL

页面:https://passport.bilibili.com/login

 

#coding:utf-8

"""
----------------------------------------
description:

author: sss

date:
----------------------------------------
change:
   
----------------------------------------

"""
__author__ = 'sss'

import logging
import time
import random
import re
import requests
from urllib import parse
import pdb
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
from io import BytesIO

class Bilibili(object):
    """
    docstring for bilibili's captcha
    """
   
js = """var keys=document.cookie.match(/[^ =;]+(?=\=)/g);
            if (keys) {
            for (var i = keys.length; i--;)
            document.cookie=keys[i]+'=0;expires=' + new Date( 0).toUTCString()
            } """

   
def __init__(self):
        """构造函数"""
       
super(Bilibili, self).__init__()
        self.browser = webdriver.Chrome()
        self.browser.set_page_load_timeout(20)
        self.browser.implicitly_wait(10)

    def __del__(self):
        """析构函数"""
       
if self.browser is not None:
            self.browser.quit()

    def logging(self, username, password):

        self.browser.get("https://passport.bilibili.com/login")
        dom_input_id = self.browser.find_element_by_id("login-username")
        dom_input_keyword = self.browser.find_element_by_id("login-passwd")
        dom_btn_log = self.browser.find_element_by_xpath('//*[@class="btn-box"]/a[1]')
        # pdb.set_trace()
       
dom_input_id.send_keys(username)
        dom_input_keyword.send_keys(password)

        flag_success = False
        while not
flag_success:
            image_full_bg = self.get_image("gt_cut_fullbg_slice")
            # 下载完整的验证图
           
image_bg = self.get_image("gt_cut_bg_slice")
            # 下载有缺口的验证图
           
diff_x = self.get_diff_x(image_full_bg, image_bg)
            # pdb.set_trace()
           
track = self.get_track(diff_x)
            result = self.simulate_drag(track)
            print(result)

            if u'验证通过' in result:
                flag_success = True
            elif
u'出现错误:' in result:
                self.browser.execute_script('location.reload()')
            elif u'再' in result:
                time.sleep(4)
                continue
            elif
u'吃' in result:
                time.sleep(5)
            else:
                break

        if
flag_success:
            time.sleep(random.uniform(1.5, 2))
            self.browser.execute_script(self.js)

    def get_image(self, class_name):
        """
       
下载并还原极验的验证图
        Args:
            class_name: 验证图所在的html标签的class name
        Returns:
            返回验证图
        Errors:
            IndexError: list index out of range. ajax超时未加载完成,导致image_slices为空
        """
       
image_slices = self.browser.find_elements_by_class_name(class_name)
        # pdb.set_trace()
       
if len(image_slices) == 0:
            print('No such a class')
        div_style = image_slices[0].get_attribute('style')
        # pdb.set_trace()
       
image_url = re.findall("background-image: url\(\"(.*)\"\); background-position: (.*)px (.*)px;", div_style)[0][0]
        image_url = image_url.replace("webp", "jpg")
        #image_filename = urlparse.urlsplit(image_url).path.split('/')[-1]
        #image_filename = parse.urlsplit(image_url)

       
location_list = list()
        for image_slice in image_slices:
            location = dict()
            location['x'] = int(re.findall("background-position: (.*)px (.*)px;",
                                           image_slice.get_attribute('style'))[0][0])
            location['y'] = int(re.findall("background-position: (.*)px (.*)px;",
                                           image_slice.get_attribute('style'))[0][1])
            location_list.append(location)

        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"}
        response = requests.get(image_url, headers=headers)

        image = Image.open(BytesIO(response.content))
        image = self.recover_image(image, location_list)
        return image

    def recover_image(self, image, location_list):
        """
       
还原验证图像
        Args:
            image: 打乱的验证图像(PIL.Image数据类型)
            location_list: 验证图像每个碎片的位置
        Returns:
           还原过后的图像
        """
       
new_im = Image.new('RGB', (260, 116))
        im_list_upper = []
        im_list_down = []
        for location in location_list:
            if location['y'] == -58:
                im_list_upper.append(image.crop((abs(location['x']), 58, abs(location['x']) + 10, 116)))
            if location['y'] == 0:
                im_list_down.append(image.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)))

        x_offset = 0
        for im in im_list_upper:
            new_im.paste(im, (x_offset, 0))
            x_offset += im.size[0]

        x_offset = 0
        for im in im_list_down:
            new_im.paste(im, (x_offset, 58))
            x_offset += im.size[0]

        return new_im

    def get_diff_x(self, image1, image2):
        """
      
计算验证图的缺口位置(x轴)
       两张原始图的大小都是相同的260*116,那就通过两个for循环依次对比每个像素点的RGB值,
       如果RGB三元素中有一个相差超过50则就认为找到了缺口的位置
       Args:
           image1: 图像1
           image2: 图像2
       Returns:
           x_offset
       """

       
for x in range(0, 260):
            for y in range(0, 116):
                if not self.__is_similar(image1, image2, x, y):
                    return x

    def __is_similar(self, image1, image2, x_offset, y_offset):
        """
       
判断image1, image2的[x, y]这一像素是否相似,如果该像素的RGB值相差都在50以内,则认为相似。
        Args:
            image1: 图像1
            image2: 图像2
            x_offset: x坐标
            y_offset: y坐标
        Returns:
           boolean
        """
       
pixel1 = image1.getpixel((x_offset, y_offset))
        pixel2 = image2.getpixel((x_offset, y_offset))
        for i in range(0, 3):
            if abs(pixel1[i] - pixel2[i]) >= 50:
                return False
        return True

    def
get_track(self, x_offset):

        track = list()
        length = x_offset - 6
        x = random.randint(1, 5)
        while length - x > 4:
            track.append([x, 0, 0])
            length = length - x
            x = random.randint(1, 15)

        for i in range(length):
            if x_offset > 47:
                track.append([1, 0, random.randint(10, 12) / 100.0])
            else:
                track.append([1, 0, random.randint(13, 14) / 100.0])
        return track

    def simulate_drag(self, track):

        dom_div_slider = self.browser.find_element_by_xpath('//*[@id="gc-box"]/div/div[3]/div[2]')

        ActionChains(self.browser).click_and_hold(on_element=dom_div_slider).perform()

        for x, y, z in track:
            ActionChains(self.browser).move_to_element_with_offset(
                to_element=dom_div_slider,
                xoffset=x + 22,
                yoffset=y + 22).perform()
            time.sleep(z)

        time.sleep(0.9)
        ActionChains(self.browser).release(on_element=dom_div_slider).perform()
        time.sleep(1)
        dom_div_gt_info = self.browser.find_element_by_class_name('gt_info_type')
        return dom_div_gt_info.text


if __name__ == '__main__':
    bilibili = Bilibili()
    bilibili.logging('username', 'password')

 

1.3.    总结

前几次很容易成功,但抓包发现有时只会请求2个图片,一张带缺口的大图,一张小图;有点不解

另外一点是滑块移动模拟的参数需要不断变化,如果始终使用一个移动模式,很快会被封。

 

1.4.    问题

selenium获取源码

# 执行js得到整个HTML

    html = driver.execute_script("return document.documentElement.outerHTML")

获得整个文档的HTML

    html = driver.find_element_by_xpath("//*").get_attribute("outerHTML")
    # 不要用 driver.page_source,那样得到的页面源码不标准

获取单个元素具体的HTML源文件

    webElement.getAttribute("outerHTML")

 

2.      行为式验证

滑块验证是通过识别鼠标行为特征来区分人和机器,属于行为式验证的一种。

行为式验证主要包括两种,拖动式和点触式。

 

鼠标行为特征

  利用鼠标消息钩子截获到的数据样本内容如下表所示,表中每一行表示所截获到的一个鼠标输入数据点信息。

 

消息类型     X轴坐标      Y轴坐标      时间戳

512 359 295 419556140

512 358 295 419556171

513 358 295 419556281

514 358 295 419556390

512 358 295 419556562

  通过数据样本可知采集的鼠标输入数据都是按时间顺序排列的4 维数据集合:InputData={(typei, xi, yi, timei)},i表示第i个数据点。

 

typei表示鼠标消息类型,描述了鼠标操作事件类型,如左键按下,右键弹起,滚动,移动等。

xi和yi分别表示鼠标输入数据点在屏幕坐标系中的绝对坐标,取值范围和屏幕分辨率对应。

timei为输入数据点的高精度时间信息。

  由此可以提取鼠标 移动速度、移动加速度、移动偏移量、移动持续时间、点击时间间隔的统计量这 5 类特征。

 

移动速度

  在人机交互过程中,鼠标移动的轨迹通常不是直线,鼠标在移动方向和垂直移动方向都有位移。单点的速度方向并不总是和移动方向一致,有时甚至偏差很大。将单点的速度v分解为移动方向的速度和垂直移动方向的速度。对于水平移动,计算水平方向上的速度, 第j个数据点的速度定义为当前数据点和前一数据点水平坐标之差除以它们的时间戳之差。类似地,可以计算出垂直速度。

 

移动加速度

  加速度的计算使用已经计算出来的速度值,加速度方向和速度方向一致。第 j 个数据点 的加速度定义为当前数据点和前一数据点速度之差除以它们的时间戳之差。

 

移动偏移量

  对于水平移动,计算垂直方向上的偏移量,第j段移动的偏移量定义为移动轨迹中,每相邻数据点的垂直坐标之差绝对值的求和。类似地,对于垂直移动,计算水平偏移量。对于斜线方向移动的处理也需要先进行坐标变换。

 

移动持续时间

  一次移动的持续时间是一次移动中首尾两个数据点的时间之差。

 

点击时间间隔统计特征

  一次人机交互会话中的点击时间间隔统计特征包括鼠标单、双击时间间隔(包括单击持续时间、双击中两次单击持续时间,间隔时间和双击持续时间)的均值和方差。

 

  根据以上的鼠标行为特征就能进行计算机用户的身份识别,同时也就能区分出到底是人还是恶意程序在进行鼠标的操作,当然简单的依靠这些特征来还无法直接检测用户身份,需要经过一些特殊处理,比如利用特征的距离度量等手段来进行判断。

 

回到正题

在极验的验证过程中可以发现一个请求:

GET /ajax.php?gt=1aa3ab3b7f07681cb310696cd27932d1&challenge=8ff8261434c2dcdbbdf58bd107001a4dla&w=eJxOJlUJ41cZHXmpFYING7uQEEPcX9rnqWfDHls7aqh0YYHRKA(9SnJjzXTd0Tae44zB260R(fULZOcBtb3uqeKrN5I6vLvN1d)4(UnBqlP3IYyYFxPNL(E2GmulZnI19N9Er8Z62a8nMUiSBCkYeWgRc)4NTdWIL(rQm0zj)dMohkR0ffvwG5URRFu)D8t7qSUMM4GE3TegfE6szUj7pPs2(QqpSL7oG(k0qapSd(wlP8dN7n6mO9Mwn55E0smkTdZsvsJJeIOFJRrkZ8BT85KZMRACJ48C)eM3olmS3lCp7W

收到的应答:

geetest_1553953267049({"score": "3", "message": "success", "validate": "1f1b8d960671d9bae0ceac9587f6e703", "success": 1})

 

基本上判断它就是极验的验证请求了。

https://static.geetest.com/static/js/geetest.6.0.9.js

具体的js解析太。。。。

还是模拟操作比较简单粗暴,性价比高一些。

 

另外附一个js实现拖拉式验证码,可以做为鼠标行为模式识别的简单参考方案。

https://blog.csdn.net/tel13259437538/article/details/79823112

 

2.1.    问题

 

html

svg:可缩放矢量图形scalable vector graphics, SVG 是使用 XML 来描述二维图形和绘图程序的语言;

symbol

path

svg语言中的元素

 

css

.bili .nav-menu .kjiwe {}

css的类选择器

 

 

2.2.    参考文档

https://blog.csdn.net/ieternite/article/details/51483491

 

转载于:https://www.cnblogs.com/wodeboke-y/p/10629394.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值