愺様愺様

滑块还原和验证(法律数据库)

愺様·2022-07-28 14:09·394 次阅读

滑块还原和验证(法律数据库)

接口分析#

  • 注册接口和验证接口是同一个

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

参数分析#

  • 在初次请求响应中通过控制台打印出 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。。。
  • 代码本地运行结果
Copy
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 重写代码
Copy
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中给出的图片顺序来偏移。
Copy
# -*- 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 识别缺口图片,缺口的距离
Copy
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),并没有和其他参数进行绑定。

轨迹生成#

Copy
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))

完整流程图#

全部代码#

Copy
# -*- 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()
posted @   愺様  阅读(394)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
目录
点击右上角即可分享
微信分享提示