当前位置: 首页 > 后端技术 > Python

【JS逆向百例】某网站加速音乐Cookie混淆详解

时间:2023-03-25 20:43:25 Python

声明本文所有内容仅供学习交流,抓包内容、敏感网址、数据接口等内容均已脱敏处理,并严禁用于商业用途和非法使用,否则由此产生的一切后果与作者无关,如有侵权,请立即联系我删除!逆向目标:加速加密逆向网址:aHR0cHM6Ly93d3cubXBzLmdvdi5jbi9pbmRleC5odG1s逆向难点:混淆、动态加密算法、多层Cookie获取加速加速是智创鱼推出的网站CDN加速和网站安全防护平台。加速的特点是三个请求访问网站:第一次请求网站,网站返回的响应状态码为521,返回的响应是经过AAEncode混淆后的JS代码;第二次请求网站,网站返回的响应状态也是同样的代码为521,返回的响应是经过OB混淆后的JS代码;第三次请求网站,网站返回响应状态码200,可以正常访问网页内容。逆向思考根据我们上面提到的AcceleratedMusic的特点,如果我们想要得到真正的HTML页面,需要经过以下三个步骤:第一次向网站请求,服务器返回的Set-Cookie携带jsluid_s参数,以及获取的Decrypt响应内容第一次获取jsl_clearance_s参数的值;用第一次请求网站得到的cookie值再次访问网站,对得到的响应内容进行反混淆和逆向得到第二个jsl_clearance_s参数的值;使用jsluid_s和jsl_clearance_s参数Cookie再次访问网站,获取真实的HTML页面内容,然后进行数据采集。抓包分析进入网站,打开开发者工具抓包,在Network中我们可以看到请求的页面响应了3次index.html,前两次返回的状态码是521,在符合Accelerated的特点:获取一层cookie,直接查看响应显示无响应内容。我们通过Fiddler抓取网站,可以看到第一个index.html返回的响应内容是经过AAEncode加密的。大致内容如下,还可以看到一堆表情,挺有意思的:document.cookie中的表达式字符串实际上是第一次使用__jsl_clearance_s的值。直接通过正则化提取加密内容后,使用execjs.eval()方法获取解密后的值:location',AAEncode_text)[0]jsl_clearance_s=execjs.eval(content_first).split(';')[0]print(jsl_clearance_s)#__jsl_clearance_s=1658906704.109|-1|7n4kX8Rwzc8wTjrbHmWHj9GXCtI%3D第二个index.html捕获层cookie返回经过OB混淆后的JS文件。我们需要对其进行调试分析,但是直接在网页中通过搜索很难找到JS文件的位置,这里推荐两种定位方式:1.替换第二个索引。保存到本地:保存??到本地后,会发现JS文件被压缩了,不利于观察。可以通过以下网址的JS格式化工具进行格式化:https://spidertools.cn/#/form...,将格式化后的代码粘贴到编辑器中进行处理,可能需要进行一些微调,例如,第一个和最后一个Script标签前后会有更多的空格,在debugger;var_0x1c58=['wpDCsRDCuA==','AWc8w7E=','w6llwpPCqA==','w61/wow7',最后通过Fiddler替换,点击AddRule添加新的规则,如下完成替换:以上操作完成后,开启Fiddler抓包(F12左下角显示Capturing为抓包状态),清除网页缓存,刷新网页,你会发现打断成功,即定位到JS文件所在位置,断点调试:2.HookCookie值因为我们获取到的JS文件生成了一个cookie,里面包含了jsluid_s和jsl_clearance_s参数的值,所以我们不妨直接把cookiehook到JS文件所在的位置,而我们不知道Hook方法的可以看看K哥以前的文章。以下是Hook代码:(function(){'usestrict';varorg=document.cookie.__lookupSetter__('cookie');document.__defineSetter__('cookie',function(cookie){if(cookie.indexOf('__jsl_clearance_s')!=-1){debugger;}org=cookie;});document.__defineGetter__('cookie',function(){returnorg;});})();注入Hook的方式有很多种。这里,是通过Fiddler中的插件注入的。在K哥爬虫中发送【Fiddler插件】即可获取插件公众号:同理,设置完成后,打开抓包清除网页缓存,刷新网页,页面即可成功中断。上半部分是我们通过Hook方法注入的代码段,显示了cookie中__jsl_clearance_s关键字的值。OB混淆后的JS文件内容:调试分析Hook后的JS文件,转发到栈中找到加密位置,我们知道JavaScript一般使用document.cookie属性创建、读取、删除cookie,分析完JS文件有些参数是动态变化的,所以我们用局部替换的方法固定一组,然后在JS文件中通过CTRL+F搜索文档,只有一个,在558行断点调试,选择_0x2a9a(后'0xdb','WGP(')+'ie',鼠标悬停你会发现这是cookie的混淆样式:全选等号后面的内容,将鼠标悬停在上面,发现这里生成了cookie中的__jsl_clearance_s参数的值:至此,我们知道cookie生成的位置,接下来我们需要了解其加密逻辑和加密方式,然后通过python复现,文档部分完整代码如下:document[_0x2a9a('0xdb','WGP(')+'ie']=_0x2228a0[_0x2a9a('0x52','$hOV')+'W'](_0x2228a0[_0x2a9a('0x3','*hjw')+'W'](_0x2228a0[_0x2a9a('0x10b','rV*F')+'W'](_0x60274b['tn']+'='+_0x732635[0x0],_0x2228a0[_0x2a9a('0x3d','QRZ0')+'q']),_0x60274b['vt']),_0x2228a0[_0x2a9a('0x112',']A89')+'x']);OB混淆相关内容可以查看K哥之前的文章,这里等号后面的内容比较复杂,其实我们想要的get的是jsl_clearance_s参数的值,通过调试可以看到它的值由0x60274b['tn']+'='+_0x732635[0x0]生成:从上面我们可以看出0x60274b['tn']对应的部分是__jsl_clearance_s,其值为0x732635[0x0],所以我们需要进一步追溯0x732635生成的位置,通过查找,可以在第538行找到它的定义生成的位置,可以看到0x732635[0x0]实际上取的是0x732635数组第一个位置的值:下面我们进一步分析0x732635后面的代码各自的含义,_0x14e035(_0x60274b['ct'])取go函数传入的字典中ct参数的值:go({"bts":["1658906704.293|0|YYj","Jm5cKs%2B1v1GqTYAtpQjthM%3D"],"chars":"vUzQIgamgWnnFOJyKwXiGK","ct":"690f55a681f304c95b35941b20538480","ha":"md5","tn":"__jsl_clearance_s","vt":"3600","wt":"1500"})分析表明_0x60274b[_0x2a9a('0xf9','uUBi')]数组中的值按照一定的规则拼接,即为__jsl_clearance_s参数的值,而_0x2a9a('0xf9','uUBi')对应字典中的bts值:接下来,进一步追踪_0x14e035,可以发现它是一个函数体,第533行return后的返回值是__jsl_clearance_s参数的值:第532行断点调试,以及可以知道hash后的_0x2a7ea9是__jsl_clearance_s参数Value:hash(_0x2a7ea9)的值是_0x2a7ea9的加密结果,本例中加密结果是0-9和a-f组成的32位字符串。是明显的MD5加密特征,网上找了一个MD5加密验证是否一致,这里的加密方式,也就是hash方式,不全是MD5.如果你刷新几次,你会发现它会改变。其实这个hash方法对应的是调用go函数传入的字典中ha的值,ha就是加密。算法一共有三种:md5、sha1、sha256,所以我们在本地处理的时候,必须同时拥有这三种加密算法,通过ha的值来匹配不同的算法进一步观察这里是一个for循环,分析发现每次循环hash(_0x2a7ea9)的值是动态变化的,原因是_0x2a7ea9的值是动态变化的,只有_0x2a7ea9中的中间两个字母在变化,不仔细看连看都看不出来:跟进_0x2a7ea9生成的位置,分析发现_0x2a7ea9参数的值是0x5e5712数组的第一个值加上两个字母和数组的第二个值:中间两个字母是通过写两次下面的段落生成的,即_0x60274b['chars']['substr'][1],取字典中chars参数的一个字母,取两次,这里通过循环。取这两个值,直到加密后的值等于_0x56cbce(即ct)的值,然后传递给__jsl_clearance_s参数作为返回值:_0x60274b[_0x2a9a('0x45','XXkw')+'s'][_0x2a9a('0x5a','ZN)]')+'tr'](_0x8164,0x1)0x56cbce是ct的值:第一个0x2228a0[_0x2a9a('0x6d','U0Y3')+'s']是一个方法,我们再往下看这个方法实现了什么样的逻辑:内容如下,可以看到这个方法返回的值是两个相等的参数:_0x560b67[_0x2a9a('0x15','NwFy')+'s']=function(_0x4573a2,_0x3855be){return_0x4573a2==_0x3855be;};模拟执行综上所述,_0x14e035函数中的逻辑是判断经过hash方法加密后的_0x2a7ea9的值是否与ct的值相等,若相等则将返回值传递给__jsl_clearance_s参数。循环结束后,如果没有匹配成功的值,则执行509行,提示失败。传入参数中ha的值在变化,即加密算法也在变化。变化,有SHA1、SHA256和MD5三种加密方式,我们可以推导出三种hash方式,或者直接使用crypto-js库实现:varCryptoJS=require('crypto-js');functionhash(type,value){如果(type=='md5'){returnCryptoJS.MD5(value).toString();}if(type=='sha1'){returnCryptoJS.SHA1(value).toString();}if(type=='sha256'){returnCryptoJS.SHA256(value).toString();}}var_0x2228a0={“mLZyz”:函数(_0x435347,_0x8098d){返回_0x435347<_0x8098d;},"SsARo":函数(_0x286fd4,_0x10b2a6){return_0x286fd4+_0x10b2a6;},"jfMAx":函数(_0x6b4da,_0x19c099){返回_0x6b4da+_0x19c099;},"HWzBW":函数(_0x3b9d7f,_0x232017){return_0x3b9d7f+_0x232017;},"DRnYs":function(_0x4573a2,_0x3855be){return_0x4573a2==_0x3855be;},"ZJMqu":function(_0x3af043,_0x1dbbb7){return_0x3af043-_0x1dbbb7;},};函数cookies(_0x60274b){var_0x34d7a8=newDate();函数_0x14e035(_0x56cbce,_0x5e5712){var_0x2d0a43=_0x60274b['字符']['长度'];for(var_0x212ce4=0x0;_0x212ce4<_0x2d0a43;_0x212ce4++){for(var_0x8164=0x0;_0x2228a0["mLZyz"](_0x8164,_0x2d0a43);_0x8164++){var_0x2a7ea9=_0x5e5712[0]+_0x60274b["chars"]["substr"](_0x212ce4,1)+_0x60274b["字符"]["substr"](_0x8164,1)+_0x5e5712[1];if(_0x2228a0["DRnYs"](hash(_0x60274b['ha'],_0x2a7ea9),_0x56cbce)){return[_0x2a7ea9,_0x2228a0["ZJMqu"](newDate(),_0x34d7a8)];}}}}var_0x732635=_0x14e035(_0x60274b['ct'],_0x60274b['bts']);return{'__jsl_clearance_s':_0x732635[0]};}//console.log(cookies({//"bts":["1658906704.293|0|YYj","Jm5cKs%2B1v1GqTYAtpQjthM%3D"],//"chars":"vUzQIgamgWnnFOJyKwXiGK",//"ct":"690f55a681f304c95b35941b20538480",//"ha":"md5",//"tn":"__jsl_clearance_s",//"vt":"3600",//"wt":"1500"//}))//__jsl_clearance_s:'1658906704.293|0|YYjzaJm5cKs%2B1v1GqTYAtpQjthM%3D'完整代码bilibili关注K哥的爬虫,小助手动手视频教学:https://space.bilibili.com/16...GitHub关注K哥的爬虫,继续分享爬虫相关代码!欢迎加星!https://github.com/kgepachong/下面只是演示了部分关键代码,不能直接运行!完整代码仓库地址:https://github.com/kgepachong...#==========================#--*--coding:utf-8--*--#@Time:2022/7/27#@Author:WeChat公众号:K哥爬虫#@FileName:jsl.py#@Software:PyCharm#=======================importjsonimportreimportrequestimportexecjscookies={}headers={"User-Agent":"Mozilla/5.0(WindowsNT10.0;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/86.0.4240.198Safari/537.36"}url="脱敏处理,完整代码按照https://github.com/kgepachong/crawler/"defget_first_cookie():globalcookiesresp_first=requests.get(url=url,headers=headers)#获取cookie值__jsluid_scookies.update(resp_first.cookies)#获取第一层响应内容,AAEncode加密content_first=re.findall('cookie=(.*?);location',resp_first.text)[0]jsl_clearance_s=execjs.eval(content_first).split(';')[0]#获取cookie值__jsl_clearance_scookies['__jsl_clearance_s']=jsl_clearance_s.split("=")[1]defget_second_cookie():globalcookies#通过携带jsluid_s和jsl_clearance_s值的cookie获取二层响应内容resp_second=requests.get(url=url,headers=headers,cookies=cookies)#获取go字典参数go_params=re.findall(';go\((.*?)\)',resp_second.text)[0]params=json.loads(go_params)returnparamsdefget_third_cookie():withopen('jsl.js','r',encoding='utf-8')asf:jsl_js=f.read()params=get_second_cookie()#传入字典third_cookie=execjs.compile(jsl_js).call('cookies',params)cookies.update(third_cookie)defmain():get_first_cook即()得到_third_cookie()resp_third=requests.get(url=url,headers=headers,cookies=cookies)resp_third.encoding='utf-8'print(resp_third.text)if__name__=='__main__':main()