一、案例分析
-
如图
register
接口请求验证码
-
fveify
接口校验,通过响应返回PASS
,失败则返回REJECT
二、fveify校验分析-滑块/点选
1、滑块
-
校验请求参数如下,红色框出部分是动态的key名和des加密的结果,其实
js版本号
一周大概变一次,所以12个请求参数
名也是随着js版本一周变一次,包括密钥
也是一周变一次,当前js版本v1.0.3-155
,即对应的请求参数里面的protocol
-
定位:通过堆栈回溯定位到js文件
-
然后在js文件随意搜索一个请求参数,比如
'vl'
然后打上断点,滑动滑块,如图断住了,开始分析
-
如下图,我们调试进入
this['getEncryptContent']
函数,发现每个参数其实都是DES_ECB
模式加密而得,参数和加密的密钥不一样,此处可以尝试用python的des加密测试对比发现一样
-
然后我们再看下那12个参数当中的几个,如下图,主要是
滑块距离
(这里的滑块距离是长300宽150的滑块,实际下载下来的滑块是其两倍长宽,所以识别的时候缩放下),滑块轨迹xyt
,以及滑动时长
这三个是每次变动的,其余基本都是默认的如zh-cn
,要注意的是密钥都是不一样的
2、点选
- 有文字/图标/语序/空间推理这四类,分析逻辑与上面的滑块一样的分析逻辑,如图除了与坐标相关的3个参数与滑块不一致,其它的都是一致的参数,其中点选没有轨迹,只传了坐标识别占比值
- 最终结果如下
三、动态的js参数名以及密钥获得方式
-
有13个动态的参数名称与密钥,解决方法有3种
-
1、通过ast的方式获得:某美动态参数对抗思路
-
2、通过正则的方式获得:数美滑块的加密及轨迹~~动态js参数分析
-
3、监控js版本是否发生变化,如果发生变化,手动替换变量名与密钥
四、DES/滑块缺口/轨迹xyt代码
-
用python还原下如上的DES加密如下,此处选择0的方式填充,加密结果和浏览器一样,到此des加密还原结束
from Crypto.Cipher import DES from Crypto.Util.Padding import pad import base64 def des_encrypt(text, key): """des ecb 加密""" des_obj = DES.new(key.encode('utf-8'), DES.MODE_ECB) encrypt_text = des_obj.encrypt(pad(str(text).encode('utf-8'), DES.block_size, style='x923')[:-1] + bytes([0])) return base64.b64encode(encrypt_text).decode('utf-8') print(des_encrypt(300, '8313c1b8')) # pkDpSt6wUfA=
-
滑块缺口识别代码
import cv2 import numpy as np import requests def get_slide_distance(bg_content, fg_content): """ 识别滑块缺口 :param bg_content: 带有缺口的滑块背景字节流 :param fg_content: 缺口字节流 :return: 距离 """ target = cv2.imdecode(np.asarray(bytearray(fg_content), dtype=np.uint8), 0) template = cv2.imdecode(np.asarray(bytearray(bg_content), dtype=np.uint8), 0) result = cv2.matchTemplate(target, template, cv2.TM_CCORR_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) return max_loc[0] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36"} bg_con = requests.get("https://********c3a0853f68fd0426e360fc3e_bg.jpg", headers=headers).content s_con = requests.get("https://********ef04c583a0853f68fd0426e360fc3e_fg.png", headers=headers).content print(get_slide_distance(bg_con, s_con))
-
利用网页真实轨迹xty列表,给定距离缩放法生成新的轨迹
def get_trace(distance): """缩放法生成轨迹""" trace = [[0,-1,0],[1,0,105],[41,0,201],[60,-2,300],[126,-2,400],[170,-7,511],[174,-7,600],[188,-7,701],[192,-7,806],[200,-7,901],[207,-7,1006],[217,-6,1100],[225,-4,1204],[225,-4,1313],[221,-8,1400],[221,-8,1516],[221,-8,1609],[221,-8,1703],[221,-8,1813],[220,-8,1907],[218,-9,2004],[218,-9,2115],[218,-9,2208],[218,-9,2305],[218,-9,2412],[218,-9,2505],[218,-9,2613]] x_slope = distance/trace[-1][0] # 斜率 new_trace = [[int(x_slope*item[0]), int(x_slope*item[1]), int(x_slope*item[2])] for item in trace] return new_trace print(get_trace(124))
-
其它轨迹代码,来自百度,生成新的轨迹
import random def get_random(distance): """ 返回滑块xyt的轨迹 :param distance: 414 :return: [[0, 0, 0], [207, -1, 102], [207, 0, 201], [207, 0, 300], [207, 0, 401], [207, -1, 501], [416, 0, 602], [415, 1, 702], [418, -1, 801], [416, 1, 901], [414, 0, 1001]] """ ge = list() ge.append([0, 0, 0]) for i in range(10): x = 0 y = random.randint(-1, 1) t = 100 * (i + 1) + random.randint(0, 2) ge.append([x, y, t]) for items in ge[1:-5]: items[0] = distance // 2 for items in ge[-5:-1]: items[0] = distance + random.randint(1, 4) ge[-1][0] = distance return ge print(get_random(414))