爬蟲逆向之滑塊驗證碼加密分析(軌跡和坐標)

本文章中所有內容僅供學習交流使用,不用于其他任何目的。否則由此產生的一切后果均與作者無關!

在爬蟲開發過程中,滑塊驗證碼常常成為我們獲取數據的一大阻礙。而滑塊驗證碼的加密方式多種多樣,其中軌跡加密和坐標加密是比較常見的兩種方式。本文將詳細介紹這兩種加密方式的原理以及如何進行逆向分析。

驗證碼逆向過程分析

第一步,找生成圖片的接口(接口可能有加密參數)獲取圖片url或者圖片base64編碼,可能還有id,token等值。

第二步,用識別工具識別圖片,獲取缺口坐標(用函數模擬軌跡)

第三步,獲取缺口坐標(軌跡)(可能會進行加密)和第一次接口獲取id、token等(可能會進行加密),可能直接攜帶加密坐標(軌跡)和token、id直接請求頁面。也可能作為請求體或者cookie、請求頭進行另一個接口請求(驗證接口可能不止一個,甚至多級),請求成功返回一個成功的token(可能有時間效期,token可能只能用一次,也可能用多次)。攜帶token請求你要請求的頁面,成功請求。

最好用seesion = requests.session()進行請求

接口不一定一次性返回 圖片+id+token

?識別后并不一定直接拿“缺口坐標”(軌跡)就能用,可能會進行加密

驗證接口可能不止一個,甚至多級

還有一個典型的「狀態依賴」問題:

驗證碼接口依賴會話狀態

/captcha/image 只做一件事:根據當前會話生成一張圖并返回其token。

緊接著調 /captcha/check 也返回一個token

兩個token一模一樣

但如果只請求/captcha/image獲取token,沒有經過/captcha/check 直接去攜帶token請求頁面,是通過不了的。

以下兩個例子:

/captcha/image返回的token和/captcha/check返回的token一模一樣

但是如果跳過/captcha/check,直接用/captcha/image返回的token去請求頁面。是沒法通過的。一句話:token 必須被 /captcha/check 把狀態從 issued 變成 verified 才能繼續用,否則服務端會判定“未經驗證的驗證碼”。

/captcha/image接口

/captcha/check接口

返回響應內容

要請求的接口內容

這三個token一樣,但是跳過/captcha/check接口直接用/captcha/image接口的token是請求不通過的。

1.坐標加密

目標網址:aHR0cHM6Ly96YnRiLmdkLmdvdi5jbi8jL2p5Z2c=

大于五頁后都會有滑塊驗證碼

抓包分析,圖片接口

圖片接口請求頭字段加密字段

找加密位置非常簡單,xhr跟棧就行了

回調第一個就是加密的位置

事實上是搜不到的,對字段進行打亂重組,但是跟棧也輕輕松松

這個值是要求的

求出來這種格式像什么

CryptoJSWordArray 對象的內部格式,哈希加密,.toString()

測試,這個就是原型的SHA256加密,直接用原生庫即可

內部進行字符串拼接

最后一個參數需要傳驗證碼接口的請求體

輸出為請求體的拼接

直接寫死即可(這網站請求詳情,則不能寫死,因為頁數變化,時間變化)

簡簡單單請求出參數。獲取兩張圖片

在驗證碼接口一共四個參數有用到

兩張圖片

secretKey為密鑰

token為驗證接口攜帶

用ddddcor求出x距離

ocr = ddddocr.DdddOcr()
img_1 = base64.b64decode(response.json()['data']['repData']['jigsawImageBase64'])
img_2 = base64.b64decode(response.json()['data']['repData']['originalImageBase64'])
token = response.json()['data']['repData']['token']
secretKey = response.json()['data']['repData']['secretKey']
print(ocr.slide_match(img_1,img_2)['target'][0])

第二個接口check

直接搜,比較簡單

這個X對于是距離。

進去NO函數。非常容易的AES加密

def aes_encrypted(w, L):data = json.dumps(w, separators=(',', ':')).encode('utf-8')key = L.encode('utf-8')[:16].ljust(16, b'\0')cipher = AES.new(key, AES.MODE_ECB)ct = cipher.encrypt(pad(data, AES.block_size))return base64.b64encode(ct).decode('ascii')

對坐標加密

有個非常巨大的坑,表面L已經寫好了,其實傳參L不是這個值

惡心。實際L值是第一個接口返回secretKey

最后成功獲取token

在詳情頁攜帶token,就能順利請求內容了。

2.軌跡加密

目標網址:aHR0cHM6Ly9janljLmhiYmlkZGluZy5jb20uY24vaHViZWl5dGgvanl4eC90cmFkZV9pbmZvci5odG1s

驗證碼接口直接請求即可,非常友好(后面你就知道有一個巨大的坑)

圖片處理請求距離

img_1 = base64.b64decode(response.json()['captcha']['templateImage'].split('base64,')[1])
img_2 = base64.b64decode(response.json()['captcha']['backgroundImage'].split('base64,')[1])
ocr = ddddocr.DdddOcr()
x = ocr.slide_match(img_1,img_2)['target'][0]
print(x)

非常友好,瞬間出來

看看第二個驗證接口

一眼看出,id是第一個接口請求的,data加密很明顯是base64加密

響應cookies為第二個接口求這個值

base64解密看一下

解析一下

'bgImageWidth': 260,
'bgImageHeight': 159,
'sliderImageWidth': 49,
'sliderImageHeight': 159,這四個參數為兩張驗證碼大小

"startSlidingTime":"2025-08-14T09:55:48.568Z","endSlidingTime":"2025-08-14T09:55:50.110Z",

startSlidingTime開始時間,endSlidingTime點擊到驗證碼驗證時間

trackList就是軌跡

一大串軌跡,跟棧調試一下

x代表移動距離,y代表上下多動,由x的變化看出,是先慢后快,在慢,t則是時間

軌跡生成的函數

    def gen_track( gap_x, gap_y=0, seed=None):# ran_x = random.randint(19, 40)"""模擬軌跡生成生成「慢→快→慢」三段式軌跡gap_x : 缺口 x 像素gap_y : y 軸最大抖動像素seed  : 隨機種子,方便調試"""if seed:random.seed(seed)# 總步數 & 總耗時steps = random.randint(40, 60)  # 步數少一點更平滑total_t = random.randint(2800, 3500)  # 總耗時 2.8~3.5 strack = []x0, y0 = 0, 0t0 = 2383  # 起始時間戳gap_x = gap_xfor i in range(steps + 1):# 1. 三段式 S 曲線映射t = i / steps# 三次貝塞爾緩動:慢→快→慢ratio = 3 * t ** 2 - 2 * t ** 3if i != 0:# 2. 計算本次坐標x = int(round(x0 + gap_x * ratio)) + 1y = y0 + random.randint(-1, 1)if i == 0:x = 0y = 0# 3. 時間分布也按 S 曲線:開始稀疏、中間密集、末尾稀疏dt = int(total_t * (0.8 + 1.2 * (1 - math.sin(math.pi * t))) / steps)t0 += dt# 4. 事件類型if i == 0:ev_type = "down"elif i == steps:ev_type = "up"else:ev_type = "move"track.append({"x": x, "y": y, "type": ev_type, "t": t0})# 提前到達終點就停if x >= gap_x:track[-1]['x'] = gap_xtrack[-1]['type'] = "up"breakreturn track

y上下抖動,X先慢后快在慢,t時間不能太快,傳入x距離即可,t實際為毫秒,隨機累加

ocr = ddddocr.DdddOcr()
x = ocr.slide_match(img_1,img_2)['target'][0]
track = gen_track(x)

在把時間整理一下

tart_iso = datetime.datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
end_iso = (datetime.datetime.utcnow() +datetime.timedelta(milliseconds=track[-1]['t'])).isoformat(timespec='milliseconds') + 'Z'

全部求出來,再用base64編碼

ef gen_track(gap_x, gap_y=0, seed=None):# ran_x = random.randint(19, 40)"""模擬軌跡生成生成「慢→快→慢」三段式軌跡gap_x : 缺口 x 像素gap_y : y 軸最大抖動像素seed  : 隨機種子,方便調試"""if seed:random.seed(seed)# 總步數 & 總耗時steps = random.randint(40, 60)  # 步數少一點更平滑total_t = random.randint(2800, 3500)  # 總耗時 2.8~3.5 strack = []x0, y0 = 0, 0t0 = 2383  # 起始時間戳gap_x = gap_xfor i in range(steps + 1):# 1. 三段式 S 曲線映射t = i / steps# 三次貝塞爾緩動:慢→快→慢ratio = 3 * t ** 2 - 2 * t ** 3if i != 0:# 2. 計算本次坐標x = int(round(x0 + gap_x * ratio)) + 1y = y0 + random.randint(-1, 1)if i == 0:x = 0y = 0# 3. 時間分布也按 S 曲線:開始稀疏、中間密集、末尾稀疏dt = int(total_t * (0.8 + 1.2 * (1 - math.sin(math.pi * t))) / steps)t0 += dt# 4. 事件類型if i == 0:ev_type = "down"elif i == steps:ev_type = "up"else:ev_type = "move"track.append({"x": x, "y": y, "type": ev_type, "t": t0})# 提前到達終點就停if x >= gap_x:track[-1]['x'] = gap_xtrack[-1]['type'] = "up"breakreturn track
ocr = ddddocr.DdddOcr()
x = ocr.slide_match(img_1,img_2)['target'][0]
track = gen_track(x)
tart_iso = datetime.datetime.utcnow().isoformat(timespec='milliseconds') + 'Z'
end_iso = (datetime.datetime.utcnow() +datetime.timedelta(milliseconds=track[-1]['t'])).isoformat(timespec='milliseconds') + 'Z'
payload = {'bgImageWidth': 260,'bgImageHeight': 159,'sliderImageWidth': 49,'sliderImageHeight': 159,'startSlidingTime': tart_iso,'endSlidingTime': end_iso,'trackList': track
}
data = base64.b64encode(json.dumps(payload, separators=(',', ':')).encode()).decode()
url = "https://cjyc.hbbidding.com.cn/captcha/check2"
payload = {"id": id ,"data":data
}
response = requests.post(url, headers=headers, cookies=cookies, data=payload)
print(response.text)

最終結果請求失敗,請求多次還是失敗

我在想,這么完美的請求方式,為什么錯了,最后調試好久,對比距離得出

最后x的距離是網頁驗證碼的距離,不是實際下載圖片的距離

實際下載圖片這么大

最終的大小按頁面大小算

所以還得把圖片修改一下

用opencv,改一下圖片大小,再識別距離

 def _resize(b64: str, w: int, h: int) -> str:"""把 base64 圖片縮放成指定寬高后再 base64 編碼"""img = cv2.imdecode(np.frombuffer(base64.b64decode(b64), np.uint8), cv2.IMREAD_COLOR)img = cv2.resize(img, (w, h), interpolation=cv2.INTER_AREA)return base64.b64encode(cv2.imencode('.jpg', img)[1]).decode()

以下為核心代碼。圖片大小,軌跡生成

response = requests.get(url, headers=headers, cookies=cookies, params=params)
id = response.json()['id']def _resize(b64: str, w: int, h: int) -> str:"""把 base64 圖片縮放成指定寬高后再 base64 編碼"""img = cv2.imdecode(np.frombuffer(base64.b64decode(b64), np.uint8), cv2.IMREAD_COLOR)img = cv2.resize(img, (w, h), interpolation=cv2.INTER_AREA)return base64.b64encode(cv2.imencode('.jpg', img)[1]).decode()
slider_b64 = _resize(response.json()['captcha']['templateImage'].split(',', 1)[-1], 49, 159)
bg_b64 = _resize(response.json()['captcha']['backgroundImage'].split(',', 1)[-1], 260, 159)
target_bytes = base64.b64decode(slider_b64)
bg_bytes = base64.b64decode(bg_b64)
img_1 = base64.b64decode(slider_b64)
img_2 = base64.b64decode(bg_b64)
def gen_track(gap_x, gap_y=0, seed=None):# ran_x = random.randint(19, 40)"""模擬軌跡生成生成「慢→快→慢」三段式軌跡gap_x : 缺口 x 像素gap_y : y 軸最大抖動像素seed  : 隨機種子,方便調試"""if seed:random.seed(seed)# 總步數 & 總耗時steps = random.randint(40, 60)  # 步數少一點更平滑total_t = random.randint(2800, 3500)  # 總耗時 2.8~3.5 strack = []x0, y0 = 0, 0t0 = 2383  # 起始時間戳gap_x = gap_xfor i in range(steps + 1):# 1. 三段式 S 曲線映射t = i / steps# 三次貝塞爾緩動:慢→快→慢ratio = 3 * t ** 2 - 2 * t ** 3if i != 0:# 2. 計算本次坐標x = int(round(x0 + gap_x * ratio)) + 1y = y0 + random.randint(-1, 1)if i == 0:x = 0y = 0# 3. 時間分布也按 S 曲線:開始稀疏、中間密集、末尾稀疏dt = int(total_t * (0.8 + 1.2 * (1 - math.sin(math.pi * t))) / steps)t0 += dt# 4. 事件類型if i == 0:ev_type = "down"elif i == steps:ev_type = "up"else:ev_type = "move"track.append({"x": x, "y": y, "type": ev_type, "t": t0})# 提前到達終點就停if x >= gap_x:track[-1]['x'] = gap_xtrack[-1]['type'] = "up"breakreturn track
ocr = ddddocr.DdddOcr()
x = ocr.slide_match(img_1,img_2)['target'][0]
track = gen_track(x)

請求失敗

多試幾次就請求成功了。請求概率挺高的

請求成功

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95753.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95753.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95753.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微信小程序實現導航至目的地

本人做的導航頁面相關功能和效果的代碼 javascript相關 Page({data: {markers: [],latitude: , // 中心點坐標longitude: ,FIXED_LAT: 31.2304, // 1. 寫死的目標點坐標, 示例:上海人民廣場FIXED_LNG: 121.4737},onLoad: function () {// 如果要顯示地圖可以看onLo…

中國科學社簡史

中國科學社簡史中國科學社,作為中國近代史上第一個民間綜合性科學團體,為中國現代科學文化事業的發展做出了卓越貢獻。其歷程不僅見證了中國科學從萌芽到蓬勃發展的轉變,還反映了中國科學體制化的艱難探索與輝煌成就。中國科學社的起源可追溯…

若爾當型,Jordon Form

文章目錄一、相似二、若爾當型1.1 認識若爾當型1.2 凱萊-哈密頓定理 (Cayley-Hamilton Theorem)一、相似 Every matrix CB?1ABC B^{-1}ABCB?1AB has the same eigenvalues as A. These C’s are similar to A. 任意一個矩陣C,滿足 CB?1ABC B^{-1}ABCB?1AB 都和…

解決uni-app微信小程序編譯報錯:unexpected character `1`

問題原因在uni-app微信小程序開發中&#xff0c;當template模板中包含英文符號<或>時&#xff0c;微信小程序的編譯器會將這些符號誤認為是HTML標簽的開閉符號&#xff0c;從而導致類似unexpected character 1的編譯錯誤。錯誤示例<view class"plan-bmi">…

[Linux] RAID存儲技術

目錄 RAID實現方式 RAID 0 RAID 1 RAID 5 RAID 10 管理RAID0 創建RAID 查看RAID 格式化和掛載 刪除RAID 管理RAID1 創建RAID 查看RAID 格式化和掛載 增加熱備盤 模擬故障 刪除故障磁盤 刪除RAID 管理RAID5 創建RAID 查看RAID md5設備劃分分區 RAID實現方…

程序設計|C語言教學——C語言基礎4:進階

一、預處理指令預處理指令在編譯前執行&#xff0c;除了#include&#xff0c;還有以下常用指令&#xff1a;1. #define 宏定義無參宏&#xff1a;定義常量或代碼片段&#xff0c;編譯時直接替換&#xff08;無類型檢查&#xff09;。#define PI 3.1415926 // 定義常量 #define…

數據結構之heap算法

文章目錄前言1. heap結構概述2. push_heap3. pop_heap4. sort_heap5. make_heap前言 heap這種數據結構&#xff0c;允許用戶以任何次序將任何數據放入該結構中&#xff0c;但是最后取出數據的時候一定是權值最高&#xff08;或者最低&#xff09;的元素。主要和實現有關&#x…

MCU 軟件斷點調試注意事項!!!

——為什么調試器會在運行中改我的Flash程序&#xff1f;調試單片機時&#xff0c;很多人都有這樣的疑問&#xff1a;明明我在調試前刷進去的固件是好的&#xff0c;為什么加了一個斷點之后&#xff0c;調試器居然去改了 Flash&#xff1f; 如果我拔掉調試器&#xff0c;這個固…

啟發式合并 + 莫隊 戀戀的心跳大冒險

題目來源&#xff1a;2025 Wuhan University of Technology Programming Contest 比賽鏈接&#xff1a;Dashboard - 2025 Wuhan University of Technology Programming Contest - Codeforces 題目大意&#xff1a; Solution&#xff1a; 首先肯定要預處理出以每個節點為起點…

JCTools 無鎖并發隊列基礎:ConcurrentCircularArrayQueue

ConcurrentCircularArrayQueue ConcurrentCircularArrayQueue 是一個抽象類&#xff0c;它為基于數組的并發循環隊列提供了基礎功能。從其命名可以看出幾個關鍵特性&#xff1a;??Concurrent??&#xff1a;常指無鎖并發。??Circular Array??&#xff1a;內部使用循環數…

力扣(LeetCode) ——622. 設計循環隊列(C語言)

題目&#xff1a;622. 設計循環隊列示例1&#xff1a; MyCircularQueue circularQueue new MyCircularQueue(3); // 設置長度為 3 circularQueue.enQueue(1); // 返回 true circularQueue.enQueue(2); // 返回 true circularQueue.enQueue(3); // 返回 true circularQueue.…

在JVM跑JavaScript腳本 | Oracle GraalJS 簡介與實踐

這是2024年初的 GraalVM 系列博文&#xff0c;當時寫了大綱&#xff0c;知道一年半后的現在才得以完成發布&#x1f604; 1、概述 實話說&#xff0c;標題的場景為小眾需求&#xff0c;日常開發基本用不到&#xff0c;我是最近在做一個低代碼輪子玩具 app-meta 需要實現 FaaS&…

基于 EC 數據與大模型技術實現天氣預報:從數據到上線的全棧方法

1. 先校準“EC 數據”與“AI 預報”的語境 EC 數據家族(常用) IFS/HRES:確定性全球模式,水平分辨率約 9 km,常用預報范圍 10 天; IFS/ENS:51 成員集合預報,提供 15 天概率信息; ERA5:再分析數據,小時級、0.25,可追溯至 1940 年,用作訓練/評測黃金基準。 AI 預報…

迭代器模式及優化

迭代器模式&#xff08;Iterator Pattern&#xff09;是一種行為型設計模式&#xff0c;用于提供一種統一的方式遍歷聚合對象&#xff08;如集合、容器&#xff09;中的元素&#xff0c;而無需暴露對象的內部實現細節。它將遍歷邏輯與聚合對象分離&#xff0c;使得遍歷操作可以…

純Qt手撕gb28181協議/gb28181協議服務端/gb28181協議設備端/gb28181設備模擬器/gb28181虛擬監控設備

一、前言說明 搞完onvif設備模擬器&#xff0c;總想著把28181設備模擬也實現&#xff0c;因為之前已經花了大力氣把28181平臺軟件端實現了&#xff0c;為了實現這個組件&#xff0c;頭發掉了一大把&#xff0c;專門把國標文檔看了好幾遍&#xff0c;逐行閱讀&#xff0c;針對需…

【滲透實戰】無下載器環境(curl/wget)下玩轉 Metasploit 自動利用

1. 背景與問題場景 在滲透測試或漏洞利用中&#xff0c;Metasploit&#xff08;MSF&#xff09;是業界最常用的框架之一。 其許多 RCE&#xff08;遠程代碼執行&#xff09;模塊在落地 payload&#xff08;如 Meterpreter 或反彈 shell&#xff09;時&#xff0c;采用了 CMD St…

jd-hotkey探測熱點key

對任意突發性的無法預先感知的熱點數據&#xff0c;包括并不限于熱點數據&#xff08;如突發大量請求同一個商品&#xff09;、熱用戶&#xff08;如惡意爬蟲刷子&#xff09;、熱接口&#xff08;突發海量請求同一個接口&#xff09;等&#xff0c;進行毫秒級精準探測到。然后…

C#WPF實戰出真汁07--【系統設置】--菜品類型設置

1、菜品設置介紹 菜品設置跟餐桌設置的功能目的是相同的&#xff0c;包括了新增&#xff0c;刪除&#xff0c;編輯&#xff0c;分頁&#xff0c;查詢&#xff0c;重置&#xff0c;全選&#xff0c;全消&#xff0c;列表功能&#xff0c;實現流程也是布局設計&#xff0c;后臺邏…

aave v3 存款與借款利息的計算方式

本文只涉及到利率計算的數學原理&#xff0c;不作源碼解析:存款首先我們假設小明在aave里面存了10000usdt&#xff0c;存的時候年化收益率是5%,那么半年后其存款的利息是多少呢?常規的計算方式如下:利息10000*5%*(存款的時長/一年的時長)這么做有什么問題呢&#xff1f;假設現…

Windows MCP.Net:基于.NET的Windows桌面自動化MCP服務器深度解析

&#x1f4cb; 目錄 項目概述 技術架構深度解析 核心功能模塊詳解 代碼實現分析 使用場景與實戰案例 性能優化與最佳實踐 擴展開發指南 總結與展望 項目概述 什么是Windows-MCP.Net&#xff1f; Windows MCP.Net是一個基于.NET 10.0開發的Windows桌面自動化MCP&…