關於滑動驗證碼的破解,主要分爲幾個部分:
1、圖像獲取
2、缺口定位
3、軌跡模擬
4、加密傳輸
由於部分滑動驗證碼加密較爲複雜,故大部分均使用selenium繞過第四步進行實現。
近期有需求需要針對一下京東的滑動驗證碼,發現其加密並不是十分複雜,故本篇使用純代碼破解。
現在我們先一步一步走下去:
下圖爲京東驗證碼:
和其他驗證碼一樣,圖片分爲兩部分,一部分爲缺口,一部分爲背景,將缺口移動到背景上對應部分即可。
我們首先來獲取圖片:
請求並不複雜,appid可請求首頁取到或直接寫死,校驗得e並不影響登錄及圖片校驗,可隨機生成或直接寫死。
session = requests.session()
session.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'
}
url = 'https://passport.jd.com/new/login.aspx'
r = session.get(url, verify=False)
url = 'https://seq.jd.com/jseqf.html?bizId=passport_jd_com_login_pc&platform=js&version=1'
r = session.get(url, verify=False)
seqSid = re.compile('sessionId="(.+?)"').findall(r.text)[0]
eid = 'RKC352YG2KMGV4IJL54WU5ODSEKNI222QI3I6I4N25FE3XRN4U7EZQ3HW3R622BMXWUX3V3JNXXZOVLVKAN6IVJIRQ'
url = 'https://iv.jd.com/slide/g.html'
payload = {
'appId': '1604ebb2287',
'scene': 'login',
'product': 'click-bind-suspend',
'e': eid,
'callback': '',
}
r = session.get(url, params=payload, verify=False)
challenge = r.json().get('challenge')
我們再來看看返回值:
很明顯,bg和patch分別對應滑塊和背景圖片:
可以看到滑塊爲png格式,但是圖片大小僅僅只有滑塊那麼大,也就是說返回值裏面肯定有來確定滑塊Y軸方向的參數。
也就是 這個參數估計是表示滑塊Y軸的位置。
我們可以用ps打開背景圖片定位一下y=23的位置,發現正好是滑塊的頂部。
所以這裏我們只需對圖片二值化後即可找出陰影x的位置了:
def prt_x(path, y):
img = Image.open(path)
img = get_threshold(img)
# img.show()
# 灰度化
img = img.convert('L')
# img.show()
# 二值化閾值
threshold = 95
# 二值化
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
img = img.point(table, '1')
# img.show()
# 滑塊偏移量
X_SIZE = 12
Y_SIZE = 38
x_list = []
for x in range(0, img.size[0] - X_SIZE, 2):
# (x0,y0,x1,y1)
box1 = (x, y, x + X_SIZE, y + Y_SIZE)
image1 = img.crop(box1)
# image1.show()
matrix = np.asarray(image1)
x_list.append((np.average(matrix == circle), x))
# print x_list
aaa = max(x_list, key=lambda item: item[0])
print aaa
x = aaa[1]
box1 = (x, y, x + X_SIZE, y + Y_SIZE)
image1 = img.crop(box1)
image1.show()
return max(x_list, key=lambda item: item[0])
接下來我們看看滑動後的請求:
參數也不復雜,c和s可以請求首頁取到,關鍵就剩下一個d,明顯是加密過的,而且絕對是和軌跡相關。
那我們就追溯一下這個d:
這樣就找到了軌跡加密方法了,我們可以看到傳入是一個array,返回則是加密後的字符串。
現在就只需構造軌跡array即可。
我們先看看array是長什麼樣子的:
一看結構,肯定就是x,y座標+當前時間戳組成。
多試幾次就能發現,第一行是表示驗證碼在當前窗口的位置。
所以我們只需要模擬第一行之後的即可。
生成軌跡的方法網上也有很多了,我就隨便copy一個過來了:
def get_tracks(distance):
# 構造滑動軌跡
'''
:param distance: (Int)缺口離滑塊的距離
:return: (List)移動軌跡
'''
# 創建存放軌跡信息的列表
trace = []
# 設置加速的距離
faster_distance = distance * 3 / 5
# 設置初始位置、初始速度、時間間隔
start, v0, t = 0.0, 0.5, 0.2
# 當尚未移動到終點時
while start < distance:
# 如果處於加速階段
if start < faster_distance:
# 設置加速度爲2
a = round(random.uniform(0.5, 0.8), 2)
# 如果處於減速階段
else:
# 設置加速度爲-3
a = round(random.uniform(-0.7, -0.9), 2)
# 移動的距離公式
move = v0 * t + 1 / 2 * a * t * t
move = int(move)
# 此刻速度
v = v0 + a * t
# 重置初速度
v0 = v
# 重置起點
start += move
# 將移動的距離加入軌跡列表
trace.append(round(move))
# 返回軌跡信息
return trace, sum(trace)
加密方法解決還是參考之前提到過的,直接調用js的方法,然後將我們構造的軌跡數組傳入,即可得到參數d了。
這樣就可得到驗證成功返回值了: