close

聲明

本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據接口等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關!

本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K哥爬蟲】聯繫作者立即刪除!

逆向目標
目標:安某客滑動驗證碼逆向分析
主頁:aHR0cHM6Ly93d3cuYW5qdWtlLmNvbS9jYXB0Y2hhLXZlcmlmeS8/Y2FsbGJhY2s9c2hpZWxkJmZyb209YW50aXNwYW0=


抓包分析

首頁請求,有個初始化函數,其中有個 sessionId 後續會用到。

然後有個 getInfoTp 的請求,Form Data 里有個 dInfo 是加密參數,返回值里 info 也是加密的,包含了圖片信息,返回值 responseId 在後續的請求也會用到。

滑動之後,有個 checkInfoTp 請求,Form Data 里有個 data 是加密參數,包含了軌跡信息,返回值 message 可以看到是否校驗成功。

整體流程就是:請求首頁獲取 sessionId,請求 getInfoTp 獲取圖片信息和 responseId,請求 checkInfoTp 校驗是否成功,中間涉及到 dInfo 和 data 兩個加密參數,以及 getInfoTp 返回得到的 info 的解密。

dInfo 生成

先來看 getInfoTp 請求的 dInfo 參數,直接搜索可定位,刷新斷下,大致就可以看出是 AES 加密,傳入了 sessionId 和一個 _taN() 函數的返回值:

_taN() 函數是一些 URL,UA 之類的信息,可以寫死:

往裡跟就可以看到 AES 算法了:

這裡簡簡單單扣一下,JavaScript 代碼如下:

/*==================================#@Time:2021-12-14#@Author :微信公眾號:K哥爬蟲#@FileName:ajk.js#@Software:PyCharm#==================================*/varCryptoJS=require('crypto-js')functionAESEncrypt(_cRV,_2undefinedp){_2undefinedp=_2undefinedp.split("").reduce(function(_PUi,_JrX,_JP9){return_JP9%2==0?_PUi+"":_PUi+_JrX;},"");_2undefinedp=CryptoJS.enc.Utf8.parse(_2undefinedp);_cRV="string"==typeof_cRV?_cRV:JSON.stringify(_cRV);_cRV=CryptoJS.AES.encrypt(_cRV,_2undefinedp,{iv:_2undefinedp,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencodeURIComponent(_cRV.toString())}functionu(){return{"sdkv":"3.0.1","busurl":"https://www.脫敏處理.com/captcha-verify/?callback=shield&from=antispam","useragent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/105.0.0.0Safari/537.36","clienttype":"1"}}functiongetDInfo(sessionId){returnAESEncrypt(u(),sessionId)}//測試樣例varsessionId="a8b339ec0c26459598786fee1cce8dc2"console.log(getDInfo(sessionId))

這段邏輯也可以用 Python 來實現,關鍵代碼如下(脫敏處理,不能直接運行):

#==================================#--*--coding:utf-8--*--#@Time:2021-12-14#@Author :微信公眾號:K哥爬蟲#@FileName:ajk.py#@Software:PyCharm#==================================importjsonimportbase64importrequestsfromlxmlimportetreefromloguruimportloggerfromurllib.parseimportquote_plusfromCrypto.CipherimportAESfromCrypto.Util.Paddingimportpad,unpadclassAESAlgorithm:@staticmethoddefencrypt(aes_key_iv,text):"""對明文進行加密"""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)returnresult@staticmethoddefdecrypt(aes_key_iv,text):"""對密文進行解密"""cipher=AES.new(key=bytes(aes_key_iv,encoding='utf-8'),mode=AES.MODE_CBC,iv=bytes(aes_key_iv,encoding='utf-8'))result=unpad(cipher.decrypt(base64.b64decode(text)),16).decode('utf-8')returnresultclassAJKSlide:def__init__(self,index_url,user_agent):self.aes=AESAlgorithm()self.index_url=index_urlself.user_agent=user_agentself.headers={"user-agent":self.user_agent}defget_session_id(self):"""獲取sessionId"""response=requests.get(url=self.index_url,headers=self.headers).textsession_id=etree.HTML(response).xpath("//input[@name='sessionId']/@value")[0]logger.info(f"sessionId==>{session_id}")returnsession_id@staticmethoddefget_aes_key_iv(session_id):"""設置AESkey和iv"""aes_key_iv=''forindex,valueinenumerate(session_id):ifindex%2!=0:aes_key_iv+=valuelogger.info(f"處理sessionId獲取aeskeyiv==>{aes_key_iv}")returnaes_key_ivdefget_d_info(self,aes_key_iv):"""獲取dInfo"""sdk_info={"sdkv":"3.0.1","busurl":self.index_url,"useragent":self.user_agent,"clienttype":1}d_info=self.aes.encrypt(aes_key_iv,json.dumps(sdk_info))logger.info(f'dInfo==>{d_info}')returnd_infodefrun(self,session_id=None):ifnotsession_id:session_id=self.get_session_id()aes_key_iv=self.get_aes_key_iv(session_id)self.get_d_info(aes_key_iv)if__name__=='__main__':UA="Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/96.0.4664.45Safari/537.36"index_url_="https://www.脫敏處理.com/captcha-verify/?callback=shield&from=antispam"ajk_slide=AJKSlide(index_url_,UA)ajk_slide.run()

getInfoTp 解密

getInfoTp 這個接口返回的 info 的值是加密的,前面我們已經知道用到了 AES 加密算法,這裡可以直接猜測也是用的的 AES 來解密的,找到 AESDecrypt 這個方法,下個斷點,刷新發現斷下之後傳入了兩個參數,第一個正是 info 的內容,第二個則是 sessionId。

解密結果可以看到滑塊的圖片地址等信息:


data 生成

接下來就是 checkInfoTp 提交驗證了,要搞清楚提交的 data 是什麼東西,同樣搜索打斷點,如下圖所示 _5DD 就是 data 值,傳過來的。

往上跟棧,可以看到 _Ug0 裡面有個 track 參數,這明顯就是軌跡了,同樣最後的結果經過了 AES 加密。

再往上跟,可以看到 _Ug0 由三個參數組成,x 是水平滑動的距離,track 是軌跡,p 是定值。


軌跡處理

軌跡生成前,得先識別缺口得到要滑動的距離,方式有很多,比如 OpenCV、開源的 ddddocr,或者直接打碼平台都行,這裡唯一要注意的一點就是圖片是有縮放的,原始尺寸 480 × 270 px 渲染後的尺寸 280 × 158 px,比例大概是 1:0.5833333333333333,可以先將圖片進行縮放後再識別,也可以先識別距離後再將距離進行縮放。

軌跡的處理,該站點校驗並不太嚴格,所以可以自己寫一下,關於滑塊的軌跡處理,主要有縮放法、本地軌跡庫、根據一些函數來生成軌跡,如緩動函數、貝塞爾曲線等,K哥以後再單獨寫一篇文章來介紹,本例中可以使用縮放法,先採集一條正常的,手動滑出來的軌跡,然後根據識別出的實際距離和樣本軌跡中的距離相比,得到一個比值,然後將樣本中的 x 值和時間值都做一個對應的縮放,生成新的軌跡,主要代碼如下:

defgenerate_track(distance):"""生成軌跡,樣本距離為126"""ratio=distance/126new_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]fortrackinbase_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}")returnnew_track
結果驗證

整個過程比較簡單,驗證成功。


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()