pkulaw滑块还原和验证

案例地址:gov.pkulaw.cn
案例内容:对gov.pkulaw的滑块进行分析还原并通过协议完成验证。
案例说明:本文内容仅限参考和学习,如有侵权请联系作者进行删除。


接口分析

注册接口和验证接口是同一个。
在这里插入图片描述

验证参数中,包括缺口距离,操作时间,明文轨迹(起始坐标,时间戳)。

在这里插入图片描述


参数分析

通过控制台打印出result,发现有图中几个参数组成。
在这里插入图片描述

y和array暂时未知。array是0到20的数字。

图片信息 imgx是宽300,imgy是高200。

small和normal分别是滑块图片和背景图片。

small:
在这里插入图片描述
normal:
在这里插入图片描述


图片还原

继续阅读源码。
在这里插入图片描述
把图片以 宽/10,高/2进行分割,分割为20张小图片,然后进行偏移。

接下来看源码中是如何进行还原的。
在这里插入图片描述
首先把array转为数组,然后计算每张小图片的宽高。 通过遍历数组,进行偏移和还原。

比如arrayList如下:
在这里插入图片描述
第一副图位于混淆前图片的位置18。第二幅图位于混淆前图片的位置12。

把代码拿到本地运行一下。

result = "复制的result"
result = JSON.parse(result)

var yvalue = result['y'],
    small = result['small'],
    array = result['array'],
    normal = result['normal'];
    __imgx = result['imgx'];
    __imgy = result['imgy'];

function indexOf(arr, str) {
    if (arr && arr.indexOf) return arr.indexOf(str);
    var len = arr.length;
    for (var i = 0; i < len; i++) { if (arr[i] === str) return i; } return -1;
}

var bgarray = array.split(',');
//还原图片
var _cutX = __imgx / 10;
var _cutY = __imgy / 2;
for (var i = 0; i < bgarray.length; i++) {
    var num = indexOf(bgarray, i.toString()); //第i张图相对于混淆图片的位置为num
    var x = 0, y = 0;
    //还原前偏移
    y = i > 9 ? -_cutY : 0;
    x = i > 9 ? (i - 10) * -_cutX : i * -_cutX;
    //当前y轴偏移量
    if (num > 9 && i < 10) y = y - _cutY;
    if (i > 9 && num < 10) y = y + _cutY;
    //当前x轴偏移量
    x = x + (num - i) * -_cutX;
    //显示第i张图片
    console.log("小图",i+1,"的background-position:", x + "px " + y + "px")
    //$("#bb" + i).css("background-position", x + "px " + y + "px");
}

打印结果:
在这里插入图片描述

这块比较简单,可以直接转为Python处理

def indexOf(arr,str_):
    if arr and arr.index:
        return arr.index(str_)
    lenarr = len(arr)
    for i in range(lenarr):
        if arr[i] == str_:
            return i
    return -1

def bg_huanyuan(bgarray):
    __imgx = 300
    __imgy = 200
    _cutX = __imgx / 10
    _cutY = __imgy / 2
    for i in range(len(bgarray)):
        num = indexOf(bgarray,i)
        x = 0
        y = 0
        # 还原前偏移
        if i > 9:
            y = -_cutY
        else:
            y = 0
        if i > 9:
            x = (i-10) * -_cutX
        else:
            x = i * -_cutX
        # 当前y轴偏移量
        if num>9 and i<10:
            y = y-_cutY
        if i>9 and num<10:
            y = y+_cutY
        # 当前x轴偏移量
        x = x+ ((num-i) * -_cutX)
        print(x,y)

# 测试结果和js一致
bgarray = [18,12,15,1,10,3,4,19,9,13,5,17,16,8,14,6,0,11,7,2]
bg_huanyuan(bgarray)

既然有了图片偏移的坐标,那么就可以开始着手还原图片。

还原的原理: 把原图的小块扣出来,然后根据偏移值贴到一副新的图片中。

在这里插入图片描述

但是要注意,偏移值和真实图片的区别,偏移值是基于前端页面的postion,如果我们想在本地用的话,需要重新计算,根据array中给出的图片顺序来偏移。

Python还原验证图的代码逻辑可能有点绕,大家多读几遍,不明白可以在群里@我。

# -*- coding: utf-8 -*-
# author :lx

import io
import json
from pathlib import Path
from PIL import Image
import base64

def bg_captcha_0(img, save_path, bgarray: list):
    """
    滑块背景图还原
    """
    if isinstance(img, (str, Path)):
        _img = Image.open(img)
    elif isinstance(img, bytes):
        _img = Image.open(io.BytesIO(img))
    else:
        return

    # 图片还原顺序
    __imgx = 300
    __imgy = 200
    _cutX = int(__imgx / 10)
    _cutY = int(__imgy / 2)

    # 创建一个新图
    new_img = Image.new('RGB', (__imgx, __imgy))

    for i in range(len(bgarray)):
        # 从背景图中裁剪出对应位置的小块  Image.crop(左,上,右,下)坐标的4元组
        if i < 10:
            crop_tuple = (int(i * _cutX), 0, int((i + 1) * _cutX), _cutY)
        else:
            crop_tuple = (int((i - 10) * _cutX), _cutY, int((i - 10 + 1) * _cutX), __imgy)
        img_cut = _img.crop(crop_tuple)

        # 将小块拼接到新图中,按bgarray数组给的位置来写坐标。
        if bgarray[i] < 10:
            new_img.paste(img_cut, (bgarray[i] * _cutX, 0))
        else:
            new_img.paste(img_cut, ((bgarray[i] - 10) * _cutX, _cutY))

    save_path = Path(save_path).resolve().__str__()
    new_img.save(save_path)
    new_img.show()
    return new_img

def indexOf(arr, str_):
    if arr and arr.index:
        return arr.index(str_)
    lenarr = len(arr)
    for i in range(lenarr):
        if arr[i] == str_:
            return i
    return -1

data = {'errcode': 0, 'y': 114,
        'array': '18,12,15,1,10,3,4,19,9,13,5,17,16,8,14,6,0,11,7,2', 'imgx': 300, 'imgy': 200,
        'small': '自己改一下',
        'normal': '自己改一下'}

bgarray = [int(i) for i in data['array'].split(',')]

# 分割字符串
res = data['normal'].split(',')[1]

# base64解码
b64decode = base64.b64decode(res)
bg_captcha_0(b64decode, save_path='s2.png', bgarray=bgarray)

还原之后,如下图所示:
在这里插入图片描述

已经变成一个正常的滑块验证图片啦。


缺口识别

这个目前对大家来说应该挺简单了,拿opencv的代码跑一跑就出来了。

对原理感兴趣的可以查看博主之前写的opencv相关博客。

import cv2

class SlideCrack(object):
    def __init__(self, front, bg, out=None):
        """
        init code
        :param front: 缺口图片
        :param bg: 背景图片
        :param out: 输出图片
        """
        self.front = front
        self.bg = bg
        self.out = out

    @staticmethod
    def clear_white(img):
        # 清除图片的空白区域,这里主要清除滑块的空白
        img = cv2.imread(img)
        rows, cols, channel = img.shape
        min_x = 255
        min_y = 255
        max_x = 0
        max_y = 0
        for x in range(1, rows):
            for y in range(1, cols):
                t = set(img[x, y])
                if len(t) >= 2:
                    if x <= min_x:
                        min_x = x
                    elif x >= max_x:
                        max_x = x
                    if y <= min_y:
                        min_y = y
                    elif y >= max_y:
                        max_y = y
        img1 = img[min_x:max_x, min_y: max_y]
        return img1

    def template_match(self, tpl, target):
        th, tw = tpl.shape[:2]
        result = cv2.matchTemplate(target, tpl, cv2.TM_CCOEFF_NORMED)
        # 寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        tl = max_loc
        br = (tl[0] + tw, tl[1] + th)
        # 绘制矩形边框,将匹配区域标注出来
        # target:目标图像
        # tl:矩形定点
        # br:矩形的宽高
        # (0,0,255):矩形边框颜色
        # 1:矩形边框大小
        cv2.rectangle(target, tl, br, (0, 0, 255), 2)
        return tl[0]

    @staticmethod
    def image_edge_detection(img):
        edges = cv2.Canny(img, 100, 200)
        return edges

    def discern(self):
        img1 = self.clear_white(self.front)
        img1 = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY)
        slide = self.image_edge_detection(img1)

        back = cv2.imread(self.bg, 0)
        back = self.image_edge_detection(back)

        slide_pic = cv2.cvtColor(slide, cv2.COLOR_GRAY2RGB)
        back_pic = cv2.cvtColor(back, cv2.COLOR_GRAY2RGB)
        x = self.template_match(slide_pic, back_pic)
        # 输出横坐标, 即 滑块在图片上的位置
        return int(x)

接口验证

对几个参数再分析一下。point缺口距离,timespan如下图所示,timespan =参数提交时间-开始滑动时间。
在这里插入图片描述

arraydata 是 鼠标/手指移动轨迹。(距离,时间)
在这里插入图片描述
除了与距离相关外(缺口距离100,则轨迹为0-100),并没有和其他参数进行绑定。


轨迹生成

大家可以单独看一看这段轨迹的模拟。

import time

def get_tracks(distance, rate=0.5, t=0.2, v=0):
    """
    将distance分割成小段的距离
    :param distance: 总距离
    :param rate: 加速减速的临界比例
    :param a1: 加速度
    :param a2: 减速度
    :param t: 单位时间
    :param t: 初始速度
    :return: 小段的距离集合
    """
    tracks = []
    # 加速减速的临界值
    mid = rate * distance
    # 当前位移
    s = 0
    # 循环
    while s < distance:
        # 初始速度
        v0 = v
        if s < mid:
            a = 20
        else:
            a = -3
        # 计算当前t时间段走的距离
        s0 = v0 * t + 0.5 * a * t * t
        # 计算当前速度
        v = v0 + a * t
        # 四舍五入距离,因为像素没有小数
        tracks.append(round(s0))
        # 计算当前距离
        s += s0

    datelist = ''
    distance = 0
    for i in tracks[1:]:
        distance += i
        datelist_time = round(time.time() * 1000)
        datelist += str(distance) + ',' + str(datelist_time) + '|'
        time.sleep(0.005)
    return datelist[:-1]

print(get_tracks(200))

完整流程

在这里插入图片描述


检验结果

执行结果:
在这里插入图片描述

源码下载

【温馨提示:此处隐藏内容需要付费订阅后才能查看!】

点赞
  1. eliwang说道:
    Google Chrome Windows 10
    玺哥,现在注册验证码时,获取json返回值的同时,需要顺带获取cookie【ASP.NET_SessionId】,然后验证时需要携带该cookie才能过验证

发表回复