网页链接:https://www.anjuke.com/captcha-verify/?callback=shield&from=antispam

请求接口:1.https://anjuke.com/captcha/getInfoTp

​ 2.https://anjuke.com/captcha/checkInfoTp

加密参数:请求参数dInfo,响应参数info,请求参数data

请求接口分析

请求参数:

image-20230410193002735

响应体:

image-20230410192946459

  1. sessionId通过搜索可以发现是隐藏在html里面

    image-20230410193118239

  2. dinfo字段通过搜索,可以找到对应的加密方式

    image-20230410193518519

    两个参数和对应的加密函数,一个是请求头信息,一个是sessionid,加密方式是AES的cbc模式,iv偏移值是获取sessionid的奇数位字符

    image-20230410193636328

    image-20230410193748534

  3. 使用python的Crypto模块进行加密获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from urllib.parse import quote_plus
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad

    session_id = html.xpath('//*[@id="sessionId"]/@value')[0]
    iv = ""
    for k, v in enumerate():
    if k % 2 != 0:
    iv += v
    text = {
    "sdkv": "3.0.1",
    "busurl": "",
    "useragent": "",
    "clienttype": 1
    }
    cipher = AES.new(key=bytes(aes_key_iv, encoding='utf-8'), mode=AES.MODE_CBC, iv=bytes(aes_key_iv, encoding='utf-8'))
    result = base64.b64encode(cipher.encrypt(pad(text.encode('utf-8'), 16))).decode('utf- 8')
    result = quote_plus(result)
  4. 对获取的响应体进行解析,猜测也是aes加密,打上断点查看,果然如此,传入的_jzC正是响应体,同样使用Crypto模块进行解密,这样我们就拿到了responseId和两张验证码的图片链接

    image-20230411105440574

    1
    2
    3
    4
    5
    6
    cipher = _AES.new(
    key=bytes(aes_key, encoding='utf-8'),
    mode=_AES.MODE_CBC,
    iv=bytes(aes_key, encoding='utf-8')
    )
    result = unpad(cipher.decrypt(base64.b64decode(ciphertext)), 16).decode('utf-8')

验证码缺口位置解析

1.可以使用opencv和ddddocr模块进行解析,这里使用cv2对验证码图片进行处理,找出缺口位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def get_slice():
# 读取图片
bg = cv2.imread("./imgs/ajk_bg.png") # 获取带缺口的背景图片
slice = cv2.imread("./imgs/ajk_sl.png") # 滑块图片

# 灰度处理,将rgb三原色全部改成灰色,这样便于图片亮度处理
bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY) # 蓝绿红改灰色
slice = cv2.cvtColor(slice, cv2.COLOR_BGR2GRAY)

# 边缘检测,图片边缘处理
bg_cam = cv2.Canny(bg, 255, 255)
slice = cv2.Canny(slice, 255, 255)

# 将两张图片进行匹配,会对每一个像素点进行匹配
r = cv2.matchTemplate(bg_cam, slice, cv2.TM_CCOEFF_NORMED)
# print(r)

# 获取最佳匹配多,返回四个结果
# 第一个是最不匹配的值,第三个是最不匹配的坐标
# 第二个是最匹配的值,第四个是最匹配的坐标
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(r)
print(minVal, maxVal, minLoc, maxLoc)
distance = maxLoc[0] # maxLoc是最匹配的坐标,第一个值就代表着横向的距离
print(distance)

轨迹处理

1.直接搜索track定位到轨迹加密的地方,_Ug0 由三个参数组成,x 是水平滑动的距离,track 是轨迹,p 是定值,_crv是轨迹信息,同样使用aes加密

image-20230411152329663

image-20230411152830153

2.轨迹生成可以采集一条正常的,手动滑出来的轨迹,然后根据识别出的实际距离和样本轨迹中的距离相比,得到一个比值,然后将样本中的 x 值和时间值都做一个对应的缩放,生成新的轨迹,将之前的缺口位置加入参与计算

1
2
3
4
5
6
7
8
9
10
def generate_track(self, distance):
ratio = distance / 126
new_track = ""
base_track = "29,11,0|29,11,11|29,11,26|33,11,56|34,11,66|36,11,67|39,11,76|41,11,83|43,11,86|46,11,92|49,11,98|50,11,102|52,11,106|53,11,111|55,11,116|57,11,118|59,11,123|60,11,126|62,11,132|64,12,134|65,12,138|66,12,142|68,12,148|69,12,151|70,13,155|71,13,158|72,13,164|74,13,166|75,13,170|76,14,174|77,14,180|79,14,182|81,14,186|82,14,196|84,14,198|86,14,207|87,15,212|89,15,219|90,15,223|92,15,230|93,15,234|94,15,239|95,15,243|98,15,246|100,15,250|102,15,260|105,15,262|106,15,266|108,15,270|109,16,276|111,16,278|113,16,283|115,16,286|117,16,291|118,16,294|119,16,298|121,16,302|123,16,309|124,16,311|125,16,315|126,16,319|129,16,324|130,16,327|131,16,331|132,16,334|132,16,388|132,16,522|133,16,566|134,16,574|135,16,575|136,16,594|137,16,620|138,16,625|139,16,652|140,16,657|141,17,676|141,18,680|142,18,684|143,18,688|144,18,716|145,18,724|146,18,796|147,19,828|148,19,860|149,19,888|149,19,890|150,19,916|151,20,932|152,20,936|152,20,1021|153,20,1150|154,20,1152|155,20,1236|155,20,1388|155,20,1522|155,20,1717|"
base_track = base_track.split("|")[:-1]
for track in base_track:
t = track.split(",")
new_track += str(int(int(t[0]) * ratio)) + "," + str(t[1]) + "," + str(int(int(t[2]) * ratio)) + "|"
# logger.info(f"new_track ==> {new_track}")
return new_track

3.最后生成轨迹和缺口位置以及上诉的iv值进行aes加密,就得到checkInfoTp接口的请求参数data