滑块还原和验证(法律数据库)
接口分析
- 注册接口和验证接口是同一个
- 验证参数中,包括缺口距离,操作滑块的时间,明文轨迹(起始坐标,时间戳)
参数分析
- 在初次请求响应中通过控制台打印出 result, 发现参数组成
- 全局搜索 GetVerifiCodeResult ,找到result = JSON.parse(result)进行断点打印,分析参数
-
y 和 array 暂时未知。array 是 0 到 20 的数字
-
图片信息 imgx 是宽 300, imgy 是高200
-
small 和 normal 分别是滑块图片和背景图片
-
small
- normal 未还原
图片还原
源码部分
-
把图片以 宽/10, 高/2 进行分割,分割成 20 张小图片然后进行偏移
-
首先把 array 转为数组, 然后计算每一张小图片的宽高,然后遍历数组,进行偏移和还原。
-
比如如下 arrayList:
- 第一张小图位于混淆前的图片位置 9, 第二张小图位于混淆前的图片位置 3, 第三张小图位于混淆前的图片位置 19。。。
- 代码本地运行结果
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 = [5,6,18,14,7,0,3,8,12,4,10,1,15,17,13,11,19,16,9,2]
bg_huanyuan(bgarray)
- 既然有了图片偏移的坐标,那么就可以开始着手还原图片。
- 还原的原理:把原图的小块扣出来,然后根据偏移值贴到一副新的图片中。
- 但是要注意,偏移值和真实图片的区别,偏移值是基于前端页面的postion,如果我们想在本地用的话,需要重新计算,根据array中给出的图片顺序来偏移。
# -*- 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 识别缺口图片,缺口的距离
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))
完整流程图
全部代码
# -*- coding: utf-8 -*-
import io
import json
from pathlib import Path
from PIL import Image
import base64
import cv2
import time
import requests
sess = requests.session()
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)
def bg_captcha_0(img,bgarray:list):
"""
滑块背景图还原
"""
if isinstance(img, (str, Path)):
_img = Image.open(img)
elif isinstance(img, bytes):
_img = Image.open(io.BytesIO(img))
else:
return
# 创建一个新图
new_img = Image.new('RGB', (300, 200))
# 图片还原顺序
__imgx = 300
__imgy = 200
_cutX = int(__imgx / 10)
_cutY = int(__imgy / 2)
for i in range(len(bgarray)):
# 从背景图中裁剪出对应位置的小块 Image.crop(左,上,右,下)坐标的4元组
if i<10:
crop_tuple = (int(i*_cutX),0,int((i+1)*_cutX),100)
else:
crop_tuple = (int((i-10)*_cutX),100,int((i-10+1)*_cutX),200)
img_cut = _img.crop(crop_tuple)
# 将小块拼接到新图中,按bgarray数组给的位置来写坐标。
if bgarray[i]<10:
new_img.paste(img_cut,(bgarray[i]*30,0))
else:
new_img.paste(img_cut, ((bgarray[i]-10)*30,100))
new_img.save('./image/bg.png')
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
# 生成轨迹
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]
# 注册和校验
def check_request(data):
headers = {
'Cookie': 'ASP.NET_SessionId=qnfya5nvtn2b3pkycs5hs1zv; gr_user_id=f0c2c220-c9b3-4d70-96dd-1ee89e08a87f; gr_session_id_81eec0d0f0be2df1=da98e241-8c1a-4496-a734-61ad1907ef90; gr_session_id_81eec0d0f0be2df1_da98e241-8c1a-4496-a734-61ad1907ef90=true; xCloseNew=27; redSpot=false',
'Host': 'gov.pkulaw.cn',
'Origin': 'http://gov.pkulaw.cn',
'Pragma': 'no-cache',
'Referer': 'http://gov.pkulaw.cn/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
url = 'http://gov.pkulaw.cn/VerificationCode/GetVerifiCodeResult'
res = sess.post(url,headers=headers,data=data)
return res
def process_main():
register_data = {
'act': 'getcode',
'spec': '300*200'
}
data = json.loads(json.loads(check_request(register_data).text))
bgarray = [int(i) for i in data['array'].split(',')]
# 分割字符串
normal = data['normal'].split(',')[1]
small = data['small'].split(',')[1]
# base64解码
b64decode = base64.b64decode(normal)
# 背景还原
bg_captcha_0(b64decode, bgarray=bgarray)
# 滑块图片
hk_image = base64.b64decode(small)
with open('./image/hk.png', 'wb') as f:
f.write(hk_image)
# 获取缺口距离
sc = SlideCrack('./image/bg.png','./image/hk.png')
slide_s = sc.discern()
check_data = {
'act': 'check',
'point': str(slide_s),
'timespan': '452',
'datelist': get_tracks(slide_s),
}
print(check_request(check_data).text)
process_main()
本文来自博客园,作者:愺様,转载请注明原文链接:https://www.cnblogs.com/wyh0923/p/16528354.html