声明本文所有内容仅供学习交流,不作任何其他用途,不提供完整代码,抓包内容、敏感网址、数据接口等.脱敏严禁用于商业和非法用途,否则由此产生的一切后果与作者无关!本文未经许可禁止转载,修改后禁止二次传播。对于因未经授权使用本文所解释的技术而导致的任何事故,作者概不负责。如有侵权,请第一时间联系作者公众号【K爬虫哥】删除!逆向目标target:某客户滑动验证码逆向分析首页:aHR0cHM6Ly93d3cuYW5qdWtlLmNvbS9jYXB0Y2hhLXZlcmlmeS8/Y2FsbGJhY2s9c2hpZWxkJmZyb209YW50aXNwYW0=数据包分析首页请求,有初始化函数,后面会用到。然后有getInfoTp的请求。FormData中有一个dInfo,是一个加密参数。返回值中的信息也是加密的,包括图片信息。返回值responseId也会在后续请求中使用。滑动之后有一个checkInfoTp请求,FormData中有一个数据是加密参数,包括track信息,返回值消息可以看到是否验证成功。整体流程是:请求首页获取sessionId,请求getInfoTp获取图片信息和responseId,请求checkInfoTp验证是否验证成功,涉及dInfo和data两个加密参数,info的解密涉及getInfoTp返回的信息。dInfo生成先看getInfoTp请求的dInfo参数,直接搜索location,refresh和break,大致可以看出是AES加密,传入sessionId和_taN()函数的一个返回值:_taN()函数是一些URL,UA等信息可以硬编码:跟着它就可以看到AES算法:这里是一个简单的按钮,JavaScript代码如下:/======================================#@时间:2021-12-14#@作者:微信公众号:哥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+_JP9;_J}r"");_2undefinedp=CryptoJS.enc.Utf8.parse(_2undefinedp);_cRV="字符串"==_cRV类型?_cRV:JSON.stringify(_cRV);_cRV=CryptoJS.AES.encrypt(_cRV,_2undefinedp,{iv:_2undefinedp,模式:CryptoJS.mode.CBC,填充:CryptoJS.pad.Pkcs7});returnencodeURIComponent(_cRV.toString())}functionu(){return{""".0",3dkv":busurl":"https://www.desensitization.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="a8b339ec0c26459598786f??ee1cce8dc2"console.log(getDInfo(sessionId))这个逻辑可以也可以用Python实现,关键代码如下(去敏化,不能直接运行):#=======================================#----coding:utf-8----#@Time:2021-12-14#@Author:WeChat公众号:K哥爬虫#@文件名:ajk.py#@Software:PyCharm#===================================importjsonimportbase64importrequestsfromlxml导入etreefromloguru从urllib导入记录器=AES.ci=字节(aes_key_iv,encoding='utf-8'),模式=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)')返回结果类AJKSlide:definit__(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("//输入[@name='sessionId']/@value")[0]logger.info(f"sessionId==>{session_id}")returnsession_id@staticmethoddefget_aes_key_iv(session_id):"""设置AES密钥和iv"""aes_key_iv=''对于索引,枚举中的值(session_id):index%2!=0:取aes_key_iv+=valuelogger.info(f"处理sessionId获取aeskeyiv==>{aes_key_iv}")returnaes_key_ivdefget_d_info(self,aes_key_iv):""Info"d"sdk_info={“sdkv”:“3.0.1”,“busurl”:self.index_url,“useragent”:self.user_agent,“clienttype”:1}d_info=self.aes.aes.aes.cens.encrypt(aes_key_ekey_iv,json.dkson.dumps(json.dkumps(json.dk_infumps))))logger.info(f'dInfo==>{d_info}')returnd_infodefrun(self,session_id=None):如果不是session_id:session_id=self.get_session_id()aes_key_iv=self.get_aes_id)iv(session.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.desensitization.com/captcha-verify/?callback=shield&from=antispam"ajk_slide=AJKSlide(index_url_,UA)ajk_slide.run()getInfoTpdecryptgetInfoTp该接口返回的info值被加密。我们已经知道使用的是AES加密算法。这里我们可以直接猜测也是使用了AES解密,找到AESDecrypt方法,下一个断点,刷新发现断了之后,传入了两个参数,第一个是info的内容,第二个是sessionId解密结果,可以看到slideBlock图片地址等信息:数据生成下一步是checkInfoTp提交验证。要找出提交的数据是什么,还要搜索断点。如下图,_5DD为数据值,传过去。往栈上走,可以看到_Ug0中有一个track参数,很明显就是track,最后的结果是经过AES加密的。再往上看,_Ug0由三个参数组成,x是水平滑动的距离,track是轨迹,p是固定值。轨迹处理在生成轨迹之前,首先要识别间隙,得到滑动距离。方式有很多种,比如OpenCV,开源的ddddocr,或者直接编码平台。这里唯一需要注意的是图片被放大了,原尺寸480×270px,渲染后尺寸为280×158px,比例约为1:0.5833333333333333。图片可以先放大再识别,也可以先识别距离再放大。对于轨迹的处理,现场验证不是太严格,可以自己写。关于滑块的轨迹处理,主要有缩放方法,局部轨迹库,以及基于一些函数的轨迹生成,比如easingfunction,BezierCurves等,K哥以后会单独写一篇文章来介绍。在这个例子中,可以使用zoom的方法采集到一条手动滑出的正常轨迹,然后将识别到的实际距离与样本轨迹中的距离进行比较,得到一个比例,然后缩放样本中的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,15|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,126,1918,2|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,14,659,616|141,14,62,616|140,16,657|141,14,606|1142,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,816|159,19,816|1519,20,930|1,2932,220,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结果验证整个过程比较简单,验证成功
