背景
对于滑动验证码校验的原理,如果我理解的没错的话,大致如下:我们在浏览器调出验证码,拖动滑块,同时页面开始记录鼠标的坐标,当松开鼠标的时候,页面完成对坐标数据的加密,携带该数据发送请求,在服务器端进行校验。要想用js伪造该过程,关键是获取滑块的坐标和对坐标进行加密的算法,下面仍然以58同城二手车为例,学习一下该过程。
大致过程
经过反复操作,发现要想获取验证码图片,需要先对https://callback.58.com/antibot/codev2/getsession.do发送post请求,获取sessionid,然后携带sessionid访问https://verifycode.58.com/captcha/getV3?&showType=win&sessionId={session_id},从而获取验证码图片的连接,同时返回一个responseid;之后伪造滑动轨迹,经过算法加密后,携带加密数据,sessionid和responseid访问https://verifycode.58.com/captcha/checkV3?callback={callback}&responseId={response_id}&sessionId={session_id}&data={data}完成校验。另外,仍然用上篇博客模板匹配的方法获取滑块坐标,不过查找的子图改为了下图(黑色周围是有一圈白色的),改了之后成功率提高很多。

代码
滑块轨迹的加密实际上就是一个aes加密,经过验证,58并未对aes加密算法进行修改,直接使用node.js中的crypto-js就可以,代码如下:
// import CryptoJS from 'crypto-js'
const CryptoJS = require('crypto-js')
function random_num(){
return Math.floor(Math.random()*25)
}
// 获取滑块的x坐标
var offset = parseInt(process.argv[2])
// 获取sessionid
var responseId = process.argv[3]
// var responseId = "dc7b4e746cc442319944e061e7023f6d"
// var offset = 200
var stmap = 140
var track = [{p: "43,22", t: 1}]
var arg = 22
for(var s=44; s < 43 + offset; s++, stmap=stmap+random_num()){
if(s%20 == 0){
arg = arg - 1
track.push({p:String(s)+','+String(arg), t:stmap})
}else{
track.push({p:String(s)+','+String(arg), t:stmap})
}
};
track.push({p:String(s-1)+','+String(arg), t:stmap+random_num()})
for (var r = track.slice(0, 260), o = "", l = 0; l < r.length; l++)
o += r[l].p + "," + r[l].t + "|";
AESEncryption = function(e) {
var t = CryptoJS.enc.Utf8.parse(responseId.substr(0, 16));
return CryptoJS.AES.encrypt(e, t, {
iv: t,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString().toUpperCase()
};
var d = AESEncryption('{"x":"' + (offset) + '","track":"' + o + '","p":"' + [0,0] + '","finger":"' + ("") + '"}');
console.log(d)
callback.js
var now = (new Date).getTime()
var jq = "jQuery" + ("1.10.1" + Math.random()).replace(/\D/g, "");
var claaback = jq + "_" + now++;
console.log(claaback)
python校验代码car_verify.py:
import requests
import time
from PIL import Image
import aircv as ac
import os
import re
import json
def save_captcha():
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
'accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
'cookie': 'id58=c5/nfF8oqage5rsmEt8aAg==; 58home=tj; city=tj; 58tj_uuid=f0606c8f-bfe0-4918-a05c-54792979825d; als=0; wmda_new_uuid=1; wmda_uuid=8d04e2a5433c9b87989e8dd97f0910d7; xxzl_deviceid=hk4tMTLXOoZDMq1OWS%2FbYdQsr1%2F6tJGVp6JnDKghAebk%2BWYpclsNs7e5ckbFgJ6m; wmda_visited_projects=%3B11187958619315%3B1732038237441; myLat=""; myLon=""; mcity=tj; new_uv=5; xxzl_cid=279a85402ac4446f9b35cada3a75d9e8; xzuid=32ea41f7-b931-49f6-bda8-2b622227d207',
'referer': 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D8',
'sec-fetch-dest': 'image'
}
headers_f = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
}
data = {
'serialId': '52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764',
'code': '22',
'sign': '235448960cb8f4d6cd710b06c61dc57a',
'url': 'https://info5.58.com:443/tj/ershouche/?PGTID=0d100000-0001-2e0e-cd43-870ea87150b4&ClickID=8',
'namespace': 'usdt_infolist_car'
}
s = requests.session()
callbacked = os.popen('node callback.js')
global callback, _
callback = callbacked.readlines()[0].strip()
_ = callback.split('_')[1]
# url_main = 'https://tj.58.com//ershouche/?utm_source=market&spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT&PGTID=0d100000-0001-2cb9-a60b-cca97abb456d&ClickID=4'
# url_f = 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D2'
# resp_f = s.get(url_f,headers=headers_f)
# print(resp_f)
# 获取sessionid
url_session = f'https://callback.58.com/antibot/codev2/getsession.do?{time.time()}'
resp_session = s.post(url_session, headers=headers_f, data=data).json()
session_id = resp_session['data']['sessionId']
url_pic = f'https://verifycode.58.com/captcha/getV3?callback={callback}&_={_}&showType=win&sessionId={session_id}'
resp = s.get(url_pic, headers=headers).text
print(resp)
# 获取responseid和验证码图片
resp = json.loads(re.findall(r'jQuery\d+.\d+\((.*?)\)', resp, re.S)[0])
response_id = resp['data']['responseId']
url_bgImg = 'https://verifycode.58.com' + str(resp['data']['bgImgUrl'])
pic = s.get(url_bgImg).content
with open('./imgs/1.png', 'wb') as f:
f.write(pic)
return s, session_id, response_id
# 处理图片,进行灰度转换和二值化处理
def handle_img(threshold=140):
img = Image.open('./imgs/1.png')
img = img.resize((300,169),Image.ANTIALIAS)
img = img.convert('L')
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
img = img.point(table, '1')
img.save('./imgs/handled.png')
def crop_img():
img = Image.open('./imgs/small.png')
box = (2,2,8,14)
small = img.crop(box)
small.save('./imgs/croped.png')
# 模板匹配,找到滑块坐标
def match_template(confidencevalue=0.6):
croped = ac.imread('./imgs/croped.png')
big = ac.imread('./imgs/handled.png')
match_result = ac.find_template(big, croped, confidencevalue)
if match_result is not None:
match_result['shape'] = (big.shape[1], big.shape[0])
return match_result['result'][0]
# 获取滑动轨迹加密结果
def get_data(loc, response_id):
ret = os.popen(f'node car_verify.js {loc} {response_id}')
data = ret.readlines()[0].strip()
return data
# 发送验证码校验请求
def check(s, response_id, session_id, data):
# proxy = {'http':'http://127.0.0.1:8888','https':'http://127.0.0.1:8888'}
url = f'https://verifycode.58.com/captcha/checkV3?callback={callback}&responseId={response_id}&sessionId={session_id}&data={data}&_={_}'
headers_check = {'referer': 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D2',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
'Cookie': 'id58=c5/nfF8oqage5rsmEt8aAg==; 58home=tj; city=tj; 58tj_uuid=f0606c8f-bfe0-4918-a05c-54792979825d; als=0; wmda_new_uuid=1; wmda_uuid=8d04e2a5433c9b87989e8dd97f0910d7; xxzl_deviceid=hk4tMTLXOoZDMq1OWS%2FbYdQsr1%2F6tJGVp6JnDKghAebk%2BWYpclsNs7e5ckbFgJ6m; wmda_visited_projects=%3B11187958619315%3B1732038237441; myLat=""; myLon=""; mcity=tj; new_uv=9; init_refer=https%253A%252F%252Fcallback.58.com%252Fantibot%252Fverifycode%253FserialId%253D52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764%2526code%253D22%2526sign%253D235448960cb8f4d6cd710b06c61dc57a%2526namespace%253Dusdt_infolist_car%2526url%253Dhttps%25253A%25252F%25252Finfo5.58.com%25253A443%25252Ftj%25252Fershouche%25252F%25253FPGTID%25253D0d100000-0001-2e0e-cd43-870ea87150b4%252526ClickID%25253D2; wmda_session_id_11187958619315=1597305356023-87e5e97d-4b43-b080; new_session=0; xxzl_cid=279a85402ac4446f9b35cada3a75d9e8; xzuid=32ea41f7-b931-49f6-bda8-2b622227d207; spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT; utm_source=link',
'Accept': '*/*',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Dest': 'script'
}
resp = s.get(url, headers=headers_check)
print(resp.text)
# 程序运行的主要逻辑
def verify():
s, session_id, response_id = save_captcha()
handle_img()
loc = match_template() - 10
print(loc)
data = get_data(loc, response_id)
check(s, response_id, session_id, data)
if __name__ == "__main__":
# crop_img()
verify()
总结
还有一个要注意的地方,通过模板匹配获得的滑块x坐标,要比真正的坐标大10,所以在传递给加密算法的时候要减去10,一开始在这个地方花费了很多时间。此外,58又新增了一种验证码,图片如下,还有待探索。

结语
以上只做学习交流使用,欢迎各位提出宝贵意见,多多指正交流,另转载请注明出处。
本文详细解析了58同城网站滑动验证码的工作原理,包括如何获取验证码图片、伪造滑动轨迹、加密数据及服务器端校验流程。并提供了使用Node.js和Python实现的代码示例。
738

被折叠的 条评论
为什么被折叠?



