總結(jié)幾種常見 驗證碼的解決方案,非常有用!

來源 |?咸魚學(xué)Python
截止到今天咸魚已經(jīng)寫了很多期關(guān)于 Js 逆向的文章,不過這么多的文章都有一個共同點,都是關(guān)于加密參數(shù)或者密碼加密的解析,很多讀者在后臺私信希望能夠出一些關(guān)于滑動驗證或者人機驗證的分析教程。
于是咸魚總結(jié)了目前遇到過的的驗證碼類型以及總結(jié)出來的相關(guān)處理方式和大家聊一聊。
現(xiàn)在市面上的驗證碼的類型大致有下面幾種類型。
一、圖形驗證碼
比較常見的英文數(shù)字組合成的圖形驗證碼,常常輔以各類干擾線和扭曲圖片中的內(nèi)容達(dá)到提高混淆難度的目的,并且通過加長圖片中的文字長度提升識別成本。



像這類驗證碼的處理方案有很多種,簡單給大家概括一下。
難度中低的兩類驗證碼,安裝?tesserocr,通過 OCR 技術(shù)結(jié)合 Python 的?tesserocr?庫可以就可以完成識別。如果驗證碼中帶有簡單干擾線可以使用灰度和二值化的方法提高代碼的識別率。
常用示例代碼:
import tesserocrfrom PIL import Imageimage = Image.open('code2.jpg')image = image.convert('L')threshold = 127table = []for i in range(256):if i < threshold:table.append(0)else:table.append(1)image = image.point(table, '1')result = tesserocr.image_to_text(image)print(result)
難度較高的多位英數(shù)+扭曲的圖形驗證碼包括上面總結(jié)的中低難度的圖形驗證碼,可以通過 TensorFlow 訓(xùn)練的方式達(dá)到識別驗證碼的目的。
使用這個方式的朋友記得要先準(zhǔn)備好足夠的驗證碼的樣本,只要你的模型不是太差,通過足量的樣本,不斷調(diào)優(yōu)是可以達(dá)到一個較為可觀的識別率。
目前體驗過最好的程序是冷月的四位英數(shù),識別成功率高達(dá) 99.99% ,不過據(jù)知情人透露整個訓(xùn)練的樣本達(dá)到了 6000 W ,耗費的時間精力可想而知。
二、旋轉(zhuǎn)驗證碼
這類驗證碼是將驗證碼的圖片旋轉(zhuǎn)并且需要用戶拖動下方滑塊完成將圖片擺正的操作才可以完成驗證。

不過某家的這個驗證碼有一些小小的 bug,依靠勞苦大眾的智慧,我在 GitHub 上發(fā)現(xiàn)了一個很 Nice 的項目。
項目地址:https://github.com/scupte/xuanzhaunyanz
因為圖庫的容量問題,沒有超大的圖庫作為后盾,將全部的原圖抓取下來對比完全可以得到旋轉(zhuǎn)的角度了。
不過鑒于該家的驗證碼并沒有普及所以了解一波即可。
部分對比代碼:
# -*- coding: utf-8 -*-import cv2import numpy as npimagepath = '9_1.png'img = cv2.imread(imagepath)gray = cv2.cvtColor ( img , cv2.COLOR_BGR2GRAY )ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#cv2.drawContours(img,contours,-1,(0,0,255),1)for cnt in contours:# 最小外界矩形的寬度和高度width, height = cv2.minAreaRect(cnt)[1]if width* height > 100:# 最小的外接矩形rect = cv2.minAreaRect(cnt)box = cv2.boxPoints(rect) # 獲取最小外接矩形的4個頂點box = np.int0(box)print boxif 0 not in box.ravel():#繪制最小外界矩形for i in range(4):cv2.line(img, tuple(box[i]), tuple(box[(i+1)%4]), 0) # 5theta = cv2.minAreaRect(cnt)[2]if abs(theta) <= 45:print('圖片的旋轉(zhuǎn)角度為%s.'%theta)# angle = thetaprint thetacv2.imshow("img", img)cv2.waitKey(0)
三、滑動驗證碼
說到滑動驗證碼,一定一定要提某驗,雖然說市面上關(guān)于滑動驗證碼的產(chǎn)品有很多,但是某驗的地位就像 10 年前腦白金在保健品市場的地位一樣,業(yè)界標(biāo)桿啊。
它越牛逼,市場上用它做防護的網(wǎng)站也越多,像國家企業(yè)信用信息公示系統(tǒng)、B 站、狗東等等。
像某驗的解決方案也有很多,不過原理大同小異。
selenium 模擬滑動
使用 selenium 這個大家都聽過,步驟大致是將缺口圖和原圖進行對比獲取缺口的橫坐標(biāo),并使用計算完成拖動軌跡模擬,之后使用 selenium 按照軌跡滑動完成缺口的拼接。
這一類方法的優(yōu)點是門檻低,原理簡單,缺點是完成滑動耗時較長,成功率無法估計(同一軌跡計算規(guī)則使用多次后成功率迅速下降)
常見的軌跡生成代碼:
import numpy as npimport mathdef ease_out_expo(x):"""曲線函數(shù):param x::return:"""if x == 1:return 1else:return 1 - pow(2, -10 * x)def get_tracks(distance, seconds):"""軌跡生成函數(shù):param distance: 滑動總距離:param seconds: 滑動總時間:return:"""tracks = [0] # 存放軌跡的數(shù)組offsets = [0] # 存放滑動總距離的記錄數(shù)組for t in np.arange(0.0, seconds, 0.1): # 產(chǎn)生一個數(shù)列如[0.0, 0.1, 0.2, 0.3]offset = round(ease_out_expo(t/seconds) * distance) # 根據(jù)時間t計算在曲線上的滑動距離tracks.append(offset - offsets[-1]) # 本次計算的距離減去上一次移動的距離,得到本次的軌跡offsets.append(offset) # 至本次滑動了的總距離return offsets, tracksa, b = get_tracks(138, 3)print(a, b)def get_tracksb(distance):"""根據(jù)物理的先加速再減速規(guī)律計算:param distance::return:"""distance += 20 # 加上20是為了滑動超過缺口再回滑v = 0 # 初速度t = 0.2 # 以0.2秒為一個計算周期forward_tracks = [] # 軌跡記錄數(shù)組current = 0 # 初始移動距離mid = distance * 3 / 5 # 減速閥值即五分之三的距離加速剩下距離減速while current < distance: # 總移動距離等于輸入距離時結(jié)束if current < mid: # 加速狀態(tài)a = 2 # 加速度為+2else: # 減速狀態(tài)a = -3 # 加速度-3s = v * t + 0.5 * a * (t ** 2) # 計算0.2秒周期內(nèi)的位移v = v + a * t # 計算本次周期后的速度current += s # 將之前移動的總距離,加上本次0.2秒周期內(nèi)移動的距離forward_tracks.append(round(s)) # 記錄本次0.2秒周期內(nèi)的移動距離為軌跡back_tracks = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1] # 手動將開頭加上的20,生成減去軌跡,即回滑軌跡return {'forward_tracks': forward_tracks, 'back_tracks': back_tracks}
Js 破解關(guān)鍵的參數(shù)
這類方法的門檻就比較高了,通過斷點調(diào)試 Js,逆向分析滑動后提交參數(shù)的生成邏輯完成參數(shù)的生成,之后構(gòu)造請求完成提交,當(dāng)然這中間也是需要分析圖片的缺口位置與模擬軌跡,不過沒有使用到模擬所以速度快成功率高。
缺點是風(fēng)險高,代碼維護成本高,更新一個新版本就要重新分析而且逆向相關(guān)產(chǎn)品的代碼是有一定的法律風(fēng)險的,免費吃住也不是開玩笑的,所以很多能夠商業(yè)化的大佬們都悶聲發(fā)大財不會到處張揚。
使用現(xiàn)有的服務(wù)
上面兩種方法各有各的優(yōu)缺,很多人就想把這一塊的工作量與風(fēng)險分出去,這就要使用到第三方的服務(wù)商了。
不過目前國內(nèi)市場上的服務(wù)商并沒有這類服務(wù),目前咸魚在使用的是一家俄羅斯的服務(wù)商 - 2Captcha
這個服務(wù)商提供的驗證碼服務(wù)有很多種,其中包含了我們比較關(guān)心的 GeeTest 。

下面咸魚給大家簡單介紹下如何使用服務(wù)。(不要問為啥收費,人家服務(wù)商也要吃飯,況且這個價格實在便宜了)
首先,注冊一個賬號,官網(wǎng)是 http://2captcha.com/zh

完成注冊之后會跳轉(zhuǎn)到控制臺界面,這里最重要的是獲取到屬于你的 API Key 。

好,拿到這個 API Key 之后就可以上手使用服務(wù)完成滑動的破解了。
通過參考官方的 API 文檔,我們只需要構(gòu)建兩個 Get 請求就可以了。
第一個 Get 請求的組成是這樣的:
https://2captcha.com/in.php?key= 上面獲取的API KEY&method=geetest>=?某驗參數(shù)&challenge=?某驗參數(shù)&api_server=api-na.geetest.com(可選)&pageurl= 滑動驗證碼所在的網(wǎng)頁地址
參數(shù)列表:
| 參數(shù)名 | 參數(shù)介紹 |
| key | API KEY |
| method | 表示驗證碼類型 |
| gt | 某驗參數(shù)1 |
| challenge | 某驗參數(shù)2 |
| api_server | api-na.geetest.com(選填) |
| pageurl | 滑動驗證碼所在的網(wǎng)頁地址 |
這里解釋下關(guān)于?gt?與?challenge?這兩個參數(shù)的獲取。
第一個請求中這兩個參數(shù)其中?gt這個參數(shù)是固定的,找一個使用某驗的網(wǎng)站就可以獲取。例如:

challenge這個參數(shù)是由請求返回,你找到這個請求之后按照請求重新獲取一次,如果是 XHR 的話也可以直接 replay XHR 。

完成參數(shù)構(gòu)建,提交完第一個請求之后,成功會返回類似下面的結(jié)果。
OK|2122988149 or as JSON {"status":1,"request":"2122988149"}這里面的一串?dāng)?shù)字就是會話 ID。
有了這個會話 ID 之后我們就可以構(gòu)建下一個請求,記住兩個請求中間需要等待一些時間哦。
https://2captcha.com/res.php?key=API KEY&action=get&id=2122988149
參數(shù)列表:
| 參數(shù)名 | 參數(shù)介紹 |
| key | API KEY |
| action | Get |
| id | 上一個請求返回的會話ID |
這個請求返回的結(jié)果就是我們需要的加密參數(shù)。
{"challenge":"1a2b3456cd67890e12345fab678901c2de","validate":"09fe8d7c6ba54f32e1dcb0a9fedc8765","seccode":"12fe3d4c56789ba01f2e345d6789c012|jordan"}
以上常見的幾類驗證碼,已經(jīng)全部介紹完了。
肯定有人問像 google 家的 ReCaptcha 以及和他相似的 hCaptcha 的解決方案沒有提到啊?
像以上兩類驗證碼,剛剛提到的服務(wù)商也同樣有提供接口打碼。
至于其他不依靠服務(wù)商的解決方案,目前咸魚還沒有接觸過,畢竟這兩類驗證碼,咸魚手動點擊都沒辦法做到一次通過,目前也只能依賴服務(wù)商了。
