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

[100casesofJSreverseengineering]WstoreUA,OBanti-obfuscation,packetcaptureandreplacementCORScross-domainerroranalysis

时间:2023-03-26 16:28:59 Python

FollowWeChat公众号:Kbrothercrawler,continuetosharetechnicaldrygoodssuchascrawleradvancement,JS/Androidreverseengineering!Itisdeclaredthatallthecontentinthisarticleisforlearningandcommunicationonly.Thecapturedcontent,sensitiveURLs,anddatainterfaceshavebeendesensitized,andarestrictlyprohibitedfrombeingusedforcommercialorillegalpurposes.Otherwise,allconsequencesarisingtherefromhavenothingtodowiththeauthor.Infringement,pleasecontactmetodeleteimmediately!逆向目标目标:W店登录接口UA参数加密,JS代码经过了OB混淆主页:aHR0cHM6Ly9kLndlaWRpYW4uY29tLw==接口:aHR0cHM6Ly9zc28xLndlaWRpYW4uY29tL3VzZXIvbG9naW4=逆向参数:FormData:ua:H4sIAAAAAAAAA91ViZUbMQhtiVOIcnRRxRafr%2FGuN5ukgoyfLUZC...OB混淆简介OB混淆全称Obfuscator,ObfuscatorInfact,itmeansconfusion.Theofficialwebsite:https://obfuscator.io/,whoseauthorisaRussianJavaScriptdevelopmentengineernamedTimofeyKachalov,releasedthefirstversionasearlyas2016.一段正常的代码如下:functionhi(){console.log("HelloWorld!");}hi();OB混淆后的结果:function_0x3f26(){var_0x2dad75=['5881925kTCKCP','Hello\x20World!','600mDvfGa','699564jYNxbu','1083271cEvuvT','log','18sKjcFY','21485'7,eMgFSU'77856FUKcuE','736425OzpdFI','737172JqcGMg'];_0x3f26=函数(){返回_0x2dad75;};return_0x3f26();}(function(_0x307c88,_0x4f8223){var_0x32807d=_0x1fe9,_0x330c58=_0x307c88();while(!![]){try{var_0x5d63654=parseIntf(_0x1)parseInt(_0x(32607)d)0x2+parseInt(_0x32807d(0x70))/0x3+-parseInt(_0x32807d(0x69))/0x4+parseInt(_0x32807d(0x71))/0x5+parseInt(_0x328076d)/0x6c*(parseInt(_0x32807d)(0x6a))+-parseInt(_0x32807d(0x73))/0x8*(parseInt(_0x32807d(0x6d))/0x9);如果(_0x5d6354===_0x4f8223)中断;否则_0x330c58[push'](_0x330c58['shift']());}赶上(_0x3f18e4){_0x330c58['push'](_0x330c58['shift']());}}}(_0x3f26,0xaa023));函数_0x1fe9(_0xa907e7,_0x410a46){var_0x3f261f=_0x3f26();返回_0x1fe9=函数(_0x1fe950,_0x5a08da){_0x1fe950=_0x1fe950-0x69;var_0x82a06=_0x3f261f[_0x1fe950];返回_0x82a06;},_0x1fe9(_0xa907e7,_0x410a46);}函数hi(){var_0x12a222=_0x1fe9;控制台[_0x12a222(0x6b)](_0x12a222(0x72));}hi();OB混淆具有以下特点:1、一般由四部分组成:一个大数组或包含一个大数组的函数、自执行函数、解密函数、加密函数;2、函数名和变量名通常以_0x或0x开头,后接1~6位数字或字母的组合;3.移位操作自执行功能,有明显的push和shift关键字;比如上面的例子,定义了_0x3f26()方法创建了一个大数组,自执行函数中有push和shift关键字,主要是对大数组进行移位操作,_0x1fe9()是解密函数,hi()是加密函数抓包分析点击登录抓包,可以看到有一个ua参数,这个参数已经加密了,每次登陆都会变,如下图:如果你直接搜索ua,结果太多,不方便筛选。通过XHR断点更容易找到加密位置,如下图所示,最后提交的r参数中包含ua值。如果您查找,可以看到i的值已经过URL编码。查了一下,i的值是通过window.getUa()得到的,其实就是uad。js中的匿名函数。跟进uad.js,可以看到调用了window[_0x4651('0x710')]方法,最后返回的_0x261229就是加密后的ua值。'0x440'),可以看到其实是一些字符串,通过直接搜索可以在header中的一个大数组中找到这些字符串,如下图:Confusedrestoreandreplacealargearray,Aself-执行带有明显push和shift关键字的function进行shifting操作,对于OB来说无疑是迷惑的,那我们应该如何处理才更顺眼呢?您可以在浏览器中手动选择和查看值并在本地替换它们。当然,您不需要全部更换。只需按照堆栈并在您使用它们的地方替换它们。不要愚蠢地手动将它们一一替换。这种方法适用于不太复杂的代码。如果遇到很多代码,建议使用反混淆工具来处理。这里推荐国内的OB混淆工具和国外的de4js。OB混淆工具还原度高,但有些OB运行后会报错,经测,本例OB混淆经过猿人工具处理后无法正常运行,可能需要自行预处理。de4js工具是由越南的一位作者开发的。它是开源的,您可以将其部署在您自己的机器上,支持多种混淆还原,包括Eval、OB、JSFuck、AA、JJ等,直接粘贴代码即可自动识别混淆方式。这种情况下,推荐使用de4js,如下图:我们将还原后的结果复制到本地File,使用Fiddler的Autoresponder功能替换response,如下图:如果打开数据包此时抓包刷新页面,会发现请求状态显示CORS错误,JS替换不成功。也有可能在控制台中看到错误No'Access-Control-Allow-Origin'headerispresentontherequestedresource。如下图所示:CORS跨域错误CORS(Cross-OriginResourceSharing,跨域资源共享)是W3C的一个标准,该标准使用额外的HTTP头来告诉浏览器运行在一个源上的web应用是允许访问不同来源的资源。如果请求URL的协议、域名、端口与当前页面地址不同,则属于跨域。一个常见的跨域问题是浏览器提示域名A下无法访问域名B的API,进一步了解CORS可以参考W3CCORSEnabled。简要流程如下:1.消费者向提供者发送一个Origin头:Origin:http://www.site.com;2、提供者向消费者发送一个Access-Control-Allow-Origin响应头,如果值为*或Origin对应的站点,则表示允许与消费者共享资源。如果该值为null或不存在,则表示不允许将资源共享给消费者;3、除了Access-Control-Allow-Origin,有些站点还可以检测到Access-Control-Allow-Credentials,如果为真,则表示允许;4、浏览器根据提供者的响应消息判断是否允许消费者跨域访问提供者源;我们在控制台中使用之前的错误信息,可以知道是响应头中缺少Access-Control-Allow-Origin导致的。在Fiddler中有两种方法可以将此参数添加到响应头中。下面分别介绍一下:第一种是利用Fiddler的Filter功能,在ResponseHeaders中设置即可,分别填写Access-Control-Allow-Origin和允许的域名,如下图所示:二是修改CustomRules.js文件,依次选择Rules—>CustomizeRules,在静态函数OnBeforeResponse(oSession:Session)模块下添加如下代码:if(oSession.uriContains("待处理的URL")){oSession.oResponse["Access-Control-Allow-Origin"]="Alloweddomainname";}两种方式任选其一,设置完成后即可成功替换。再次刷新调试,可以看到恢复后的JS,如下图:逆向分析,window.getUa是主要的加密函数,我们先来分析一下这个函数。:window.getUa=function(){var_0x7dfc34=newDate().getTime();如果(_0x4a9622){_0x2644f4();}_0x55b608();变种_0x261229=_0x1722c3(_0x2e98dd)+'|'+_0x1722c3(_0x420004)+'|'+_0x7dfc34.toString(0x10);_0x261229=btoa(_0x570bef.gzip(_0x261229,{'to':'string'2_t'}6);};_0x7dfc34是时间戳,后面是if判断,我们可以把鼠标放在判断里看,发现即判断_0x4a9622为假,则不执行_0x2644f4(),然后执行_0x55b608()方法,_0x261229的值主要是通过调用_0x1722c3()方法获取的,依次传入_0x2e98dd和_0x420004。很明显这两个值比较重要,分别搜索,可以发现:_0x2e98dd定义了一些header,Browser信息,屏幕信息,系统字体信息等,这些信息可以直接作为固定值传入,如下图:_0x420004查找有用的结果是只定义了一个空对象,实际里面包含了一些键盘鼠标点击和移动的数据,其实经过测试发现这个值of_0x420004没有经过强有力的验证。它可以是通用的通过随机数模拟,或者直接复制一个固定值_0x2e98dd和_0x420004。参数均未经过强校验,可以固定值传入。两个值都是JSON格式。我们可以直接在控制台使用copy语句复制它们的值,或者使用JSON.stringify()语句输出结果然后手动复制。本地联调中每个函数的调用次数很多。可以直接复制整个JS。我们注意到整个函数是一个自执行函数。在本地调用的时候,我们可以定义一个全局变量,然后在window.getUa函数中,将_0x261229的值赋给全局变量,相当于导出值,最后取这个全局变量。另一种方法是不让它自己执行,改写成一个普通的函数,然后调用window.getUa方法获取ua值。首先,我们在本地定义_0x2e98dd和_0x420004的值。这是一个小细节。我们需要在原JS代码中注释掉这两个值的定义,防止冲突。本地调试时会提示window、location、document未定义,定义为空对象即可,然后提示attachEvent未定义。搜索一下,是_0x13cd5a的原型对象。除了attachEvent,还有addEventListener。addEventListener()方法用于向指定元素添加事件处理程序。在IE中,使用attachEvent()方法来实现。让我们在GoogleChrome中埋一个断点进行调试。刷新页面会直接进入addEventListener()方法。事件为keydown,即按下键盘时,调用后续的_0x5cec90方法,输出后面返回的this。实际上并没有产生有用的值,所以可以直接将_0x13cd5a.prototype.bind方法注释掉,实际测试也没有效果。本地调试后会提示btoa未定义。btoa和atob是window对象的两个函数。btoa是binarytoascii,用于用ascii码表示二进制数据,即Base64的编码过程,而atob是asciitobinary,用于将ascii码解析为二进制数据,即base64的解码过程Base64。在NodeJS中,提供了一个名为Buffer的本地模块,可以用来进行Base64编码和解码。这里就不详细介绍了,大家可以自行百度。window.getUa方法中原来的btoa语句如下:_0x261229=btoa(_0x570bef.gzip(_0x261229,{'to':'string'}));在NodeJS中,我们可以这样写:_0x261229=Buffer.from(_0x570bef.gzip(_0x261229,{'to':'string'}),"latin1").toString('base64');注意:Buffer.from()传入了一个latin1参数,这是因为_0x570bef.gzip(_0x261229,{'to':'string'})的结果是Latin1(ISO-8859-1别名)编码,如果不传,或者传入其他参数,最终的结果可能和btoa方法得到的结果不一样!此后,本地联调完成后,就可以获取到正确的ua值了!完整代码GitHub关注K哥的爬虫,持续分享爬虫相关代码!欢迎加星!https://github.com/kgepachong/下面只是演示了部分关键代码,不能直接运行!完整代码仓库地址:https://github.com/kgepachong...JavaScript加密密钥代码结构varwindow={};varlocation={};vardocument={};var_0x5a577d=function(){}();var_0xe26ae=函数(){}();var_0x3204b9=函数(){}();var_0x3c7e70=函数(){}();var_0x4a649b=函数(){}();var_0x21524f=函数(){}();var_0x2b0d61=函数(){}();var_0x53634a=函数(){}();var_0x570bef=函数(){}();var_0xd05c32=function(_0x5c6c0c){};window.CHLOROFP_STATUS='start';//这里省略了N个函数var_0x2e98dd={//对象的具体值已经省略了"basic":{},"header":{},“navigator”:{},“screenData”:{},“sysfonts”:[],“geoAndISP”:{},“browserType”:{},“performanceTiming”:{},“canvasFp”:{},"visTime":[],"other":{}}var_0x420004={//对象的具体值已省略"keypress":true,"scroll":true,"click":true,"mousemove":true,"mousemoveData":[],"keypressData":[],"mouseclickData":[],"wheelDeltaData":[]}window.getUa=function(){var_0x7dfc34=newDate().getTime();如果(_0x4a9622){_0x2644f4();}_0x55b608();var_0x261229=_0x1722c3(_0x2e98dd)+'|'+_0x1722c3(_0x420004)+'|'+_0x7dfc34.toString(0x10);//_0x261229=btoa(_0x570bef.gzip(_0x261229,{'to':'string'}));_0x261229=Buffer.from(_0x570bef.gzip(_0x261229,{'to':'string'}),"latin1").toString('base64');返回_0x261229;};//测试输出//console.log(window.getUa())Python登录键代码#====================================#--*--coding:utf-8--*--#@Time:2021-11-15#@Author:WeChat公众号:K爬虫哥#@FileName:weidian_login.py#@Software:PyCharm#=====================================从urllib导入execjs导入请求importparseindex_url="off脱敏处理,完整代码关注GitHub:https://github.com/kgepachong/crawler(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/94.0.4606.81Safari/537.36"session=requests.session()defget_encrypted_ua():withopen('get_encrypted_ua.js','r',encoding='utf-8')asf:uad_js=f.read()ua=execjs.compile(uad_js).call('window.getUa')ua=parse.quote(ua)返回uadefget_wd_token():headers={"User-Agent":UserAgent}response=session.get(url=index_url,headers=headers)wd_token=response.cookies.get_dict()["wdtoken"]returnwd_tokendeflogin(phone,password,ua,wd_token):headers={"user-agent":UserAgent,"origin":"脱敏处理,完整代码关注GitHub:https://github.com/kgepachong/crawler","referer":"脱敏处理,完整代码遵循GitHub:https://github.com/kgepachong/crawler",}data={"phone":phone,"countryCode":"86","password":密码,“version”:“1”,“subaccountId”:“”,“clientInfo”:'{“clientType”:1}',“captcha_session”:“”,“captcha_answer”:“”,“vcode”:"","mediaVcode":"","ua":ua,"scene":"PCLogin","wdtoken":wd_token}response=session.post(url=login_url,headers=headers,data=data)打印(response.json())defmain():phone=input("请输入您的登录手机号码:")password=input("请输入您的登录密码:")ua=get_encrypted_ua()wd_token=get_wd_token()login(phone,password,ua,wd_token)if__name__=='__main__':main()