难点没有难点,只是有一个小小的坑Tip:例子:https://pan.bigdataboy.cn/s/g4JuP猿人学该题官网:https://match.yuanrenxue.com/match/16分析参数有一个m加密参数,控制台还一直输出直接跟栈进去,下断点看看看看btoa在哪定义,直接全部复制到本地window直接补上,看看u在什么定义把这部分,组合一下,复制到本地调用测试,发现f未定义,打断点,发现f其实是一串固定的字符串,直接复制下来继续执行,发现d未定义,这段代码还有个md5,也是没有扒下来发现d是一个函数,直接拔下来然后发现md5在一个匿名函数里,把这个匿名函数扒下来,改为自执行,执行测试,发现出值了,但这个值是不能用的继续分析是不是还缺少什么东西,走了错误的逻辑,所以才没有报错,首先考虑try..catch把n.g继续补上,但是这里要注意,变量名冲突验证通过
难点使用SoJsonV5加密混淆代码,各种反调,还有各种坑Tip:例子:https://pan.bigdataboy.cn/s/aZOsL猿人学该题官网:https://match.yuanrenxue.com/match/9分析流程题目已经说了是cookie了,那就跳过看值的步骤清空缓存,一打开控制台,就被断住,并且下一步一直在循环带,但是一直放行,就能正常加载,说明不是检测控制台调试,但是一格式化,重新加载,再放行就不行了,说明检测了代码格式化反调试sojson混淆后的代码,很恶心,各种混淆,反调,阻止你调试代码sojson检测代码格式,使用了正则匹配toString()方式,所以我们在所有的正则表达式出,打上断点,然后使用本地替换的方式来进行慢慢调试修改方式有两种,一种是修改检测代码块的位置,另一种是取反正则匹配的结果,看那种结果能正常执行然后保存,执行,下一步,正常执行,说明这个反调就过了当所有的正则都按照正常的流程执行后,会在这里一直debugger,但是为什么网站加载又不会被debugger,因为debugger只有当控制台打开时才会生效,直接把这两个debugger注释,然后再执行,发现来到另一个js文件,说明udc的反调就过掉了,并且debugger还在VM里面向上找栈,看看有什么信息然后设置一律不在此暂停继续执行,有时候能正常进入,有时候提示cookie失效,但至少说明已经可以执行了Hook现在反调已经过了,启用HookCookie大法,定位生成点,这就定位到生成点了分析加密逻辑//源代码for(varm=0x1;f[$b('\x30\x78\x31\x34\x31','\x72\x64\x71\x5d')+'\x68\x42'](m,5);m++){res=f[$b('\x30\x78\x65\x38','\x4c\x44\x70\x29')+'\x79\x67'](f[$b('\x30\x78\x61\x36','\x4f\x58\x71\x66')+'\x68\x43'](decrypt,'1650879458'),'\x72');}document[$b('\x30\x78\x35\x35','\x32\x6d\x25\x21')+$b('\x30\x78\x32\x39','\x31\x48\x56\x25')]=f[$b('\x30\x78\x37\x31','\x30\x46\x35\x73')+'\x79\x67'](f[$b('\x30\x78\x31\x61','\x56\x39\x5b\x42')+'\x79\x67'](f[$b('\x30\x78\x35\x32','\x6f\x41\x29\x28')+'\x7a\x58']('\x6d\x3d',f[$b('\x30\x78\x31\x65','\x54\x5e\x64\x6a')+'\x43\x77'](m,0x1)[$b('\x30\x78\x63\x38','\x30\x46\x35\x73')+$b('\x30\x78\x61\x34','\x6b\x46\x52\x77')+'\x6e\x67']()),res),f[$b('\x30\x78\x31\x37\x63','\x4f\x58\x71\x66')+'\x4c\x42']);//手动解混淆一下,就很清晰了,使用的第五次加密的结果,但是这里有个小疑问,难道每次加密的值一样???这就需要注意,如果不成功这些地方就需要注意for(varm=1;m<=5;m++){res=decrypt('1650879458')+'r';}document['cookie']='m='+(m-1)['toString']()+res+';path=/';代码整理然后把之前改写的udc.js放到Node去跑,然后补充缺少的环境,选择用const补环境,有个就是当代码里有重定义window之类的能报错,然后去删除,然后把定时器干掉,没有报错测试加密,然后呢console.log不来???原来是console.log被置空了整理流程1.访问https://match.yuanrenxue.com/match/9获取sessionid和时间戳,每次都写死在Js文件里的,需要重新请求2.然后传入时间错,获得加密的m3.带上m和seesion请求数据测试
难点代码混淆相比第五题简单一点Tip:例子:https://pan.bigdataboy.cn/s/3KXFv猿人学该题官网:https://match.yuanrenxue.com/match/6分析参数有一个m是加密值,q有点组合调用栈,进入看看怎么生成的,很简单的逻辑//简单分析一下t=Date.parse(newDate());varlist={"page":window.page,"m":r(t,window.o),"q":window.i+=window.o+'-'+t+"|",};window.o+=1;'t':每次请求重新生成'window.i+=window.o'-->'window.i=window.i+window.o''window.o'每次请求一次加一次进入r()函数,来到一个单独的Js文件,一眼就能分析出,最重要的加密函数是z()函数,window.o不能大于6分析代码把这个单独的Js复制下来在本地看看结构最上面这种混淆,直接去掉最后的('_')然后控制台就可以打印出源代码了直接运行,提示缺少window,我们直接补上,这里有个坑需要注意,在浏览器里,window是不能被重新赋值了搜索MessagetoolongforRSA,是此处提示错误,打上断点运行此处,发现堆栈很短,看来并不是加密过程中有什么值有问题,这时可能思路就断了这时可以折叠代码,看一下整个代码的执行流程,很容易发现此处异常,正常是false,本地总会缺少一点东西,能补的可以补上,那就直接改成false再次执行,得出结果,对比浏览器的加密结果一样然后出现{'0.0':'风控不通过,别匆忙,放慢速度'},只有第一页有数据出现这情况是它代码里有个全局变量,每次加密都会增加,该值与页面之间有关系,不对应就出现风控,如果用execjs执行就不行,execjs每次执行都是重新执行Js,不会累加//测试js代码window=globalwindow.o=1functionxx(){returnwindow.o++;}module.exports={//node_vm2需要导出xx}使用execjs使用node_vm2,会延续上下文使用node_vm2执行,正常获得值验证
难点代码混淆、cookie加密,还有有点难度Tip:例子:https://pan.bigdataboy.cn/s/5DvCq?password=phm69t猿人学该题官网:https://match.yuanrenxue.com/match/5分析参数mf疑似时间戳,但不一样,也是需要注意的[]m,RM4hZBv0dDon443M这两个确定是加密值分析‘m’‘f’通过调用栈,发现mf是直接赋值,都是通过window获取的可以先上下翻翻,看看有什么需要注意的地方,这里用Hook来定位window._$is(function(){'usestrict';varvalue_;Object.defineProperty(window,'_$is',{//修改_$ss为你需要查询的window属性get:function(){console.log('WindowHook捕获到->',value_);returnvalue_;},set:function(value){debugger;value_=value;console.log('WindowHook捕获到->',value_);returnvalue;},});})();最终定位此处_0x4e96b4['_$is']=_$yw;//_0x4e96b4就是window_$yw=_0x2d5f5b()["toString"]();//_0x2d5f5b()function_0x2d5f5b(){returnnew_0x35bb1d()['valueOf']();//_0x35bb1d是Date()对象}//综上分析_0x4e96b4['_$is']=newDate()['valueOf']()['toString']()继续通过Hook的方式,定位window.$_zw[23](function(){'usestrict';varvalue_;Object.defineProperty(window,'$_zw',{//修改_$ss为你需要查询的window属性get:function(){console.log('WindowHook捕获到->',value_);debugger;returnvalue_;},set:function(value){value_=value;console.log('WindowHook捕获到->',value_);debugger;returnvalue;},});})();最终定位此处//$_aiding['$_zw'][7]--->Datewindow.$_zw[23]=$_aiding['$_zw'][7].parse(new$_aiding['$_zw'][7]()//所以window.$_zw[23]=Date.parse(newDate())接着是两个加密的Cookie也是采用Hook的方式先定位赋值的地方(function(){'usestrict';varcookie_;Object.defineProperty(document,'cookie',{//修改_$ss为你需要查询的window属性get:function(){console.log('GetCookieHook捕获到-->',cookie_);debugger;returncookie_;},set:function(cookie_){cookie_=cookie_;console.log('SetCookieHook捕获到-->',cookie_);debugger;returncookie_;},});})();HookCookie加密m值_$Wa=_0x12eaf3();//_0x12eaf3function_0x12eaf3(){return_0x35bb1d[_$UH[0xff]](new_0x35bb1d());//_0x35bb1d是Date_$UH[0xff]是parse}//所以,但是还需要加密把加密函数扒下来_$Wa=Date.parse(newDate())HookCookie加密RM4hZBv0dDon443M值,就使用上面的Hook脚本就行继续使用Hookwindow方式,来Hook_$ss,定位到此处,发现是AES加密寻找AES的KeyModepadding值//CryptoJS.enc.Utf8.stringify(WordArray)-->WordArray.init对象转Utf-8_$UH[0x2e5]"ECB"_$UH[0x33c]+_$UH[0x33d]"Pkcs7"_0x4e96b4[_0xc77418('0x6','OCbs')]WordArray.init{words:Array(4),sigBytes:16}_$WwWordArray.init{words:Array(41),sigBytes:164}CryptoJS.enc.Utf8.stringify(_0x4e96b4[_0xc77418('0x6','OCbs')]);"MTY1MDE5ODgxNjUy"CryptoJS.enc.Utf8.stringify(_$Ww);"37cdb337788229c60faf0aaffb2d6e65,0df6d975c6b74ba95888b9ac8f0b335c,c333e16a0ae6031b2a1c7be404fdc3e8,61eed28470638021cb96fd084d2f1899,57b5c27e56210e05f2eea98b3bfb0cdb"经过几次的实验,发现这个Key是会变的,所以还需要分析Key是怎么来的,继续使用HookWindow脚本window._$qFWordArray.init{words:Array(4),sigBytes:16}_0x4e96b4[_0xc77418('0x6','OCbs')]WordArray.init{words:Array(4),sigBytes:16}Hook到此处,是Key生成的地方,原来Key是m值而来寻找原加密的值,继续使用HookWindow脚本来定位可以发现前四个是循环此处添加的,时间戳后面是三个零_$Wa=_0x12eaf3();_0x3d0f3f[_$Fe]='m='+_0x474032(_$Wa)+';\x20path=/';_0x4e96b4['_$pr']['push'](_0x474032(_$Wa));第五个,在这里,时间戳后面不是三个零try{_$yw=_0x2d5f5b()[_$UH[0x1f]]();_0x3d0f3f[_$Fe]='m='+_0x474032(_$yw)+';\x20path=/';_0x4e96b4['_$is']=_$yw;_0x4e96b4['_$pr']['push'](_0x474032(_$yw));}catch(_0x3c2e99){}整理一下流程参数f最先生成带三个0的时间戳参数m是加密时间戳数组的第五个未加密时的值时间戳数组前四个使用`Date.parse(newDate())`方式获取,结尾有三个零时间戳数组前第五个使用`newDate()['valueOf']()['toString']()`方式获取cookie的m就是参数的m加密而来MTY1MDE5ODgxNjUy-->由五个加密的时间戳再进行AES加密而来扒代码用手撕的方式吧定位到加密函数出_0x474032function_0x474032(_0x233f82,_0xe2ed33,_0x3229f9){return_0xe2ed33?_0x3229f9?v(_0xe2ed33,_0x233f82):y(_0xe2ed33,_0x233f82):_0x3229f9?_0x41873d(_0x233f82):_0x37614a(_0x233f82);}//美化一下格式function_0x474032(_0x233f82,_0xe2ed33,_0x3229f9){return_0xe2ed33?//undefined_0x3229f9?v(_0xe2ed33,_0x233f82):y(_0xe2ed33,_0x233f82):_0x3229f9?//undefined_0x41873d(_0x233f82):_0x37614a(_0x233f82);//所以最终执行的是`_0x37614a`}//所以只需要_0x37614a(时间戳)然后把_0x37614a()函数复制到本地,然后把所有缺少的函数补齐function_0x37614a(_0x32e7c1){return_0x499969(_0x41873d(_0x32e7c1));}这就是补完后的样子,然后运行,报错提示_$UH未定义打断点查看是个大数组,所以只需要这样补充就好_$UH={15:'charCodeAt',108:'length',276:'fromCharCode',}接着运行,报_0x1badc3[_$UH[31]]isnotafunction错,然后源文件该行断点,发现没有断住,说明没有执行这里,然后有个trycatch,断点看看接着就是报什么错,就到原文件看是什么,对比着来,这里有几个值需要注意,不然就算出值了也不能过,_0x4e96b4['_$tT']、_0x4e96b4['_$Jy'],需要该函数计算cookiem时才是正确的验证现在整个过程都清楚了,验证一下
难点头部顺序反爬、某些框架内部会对头部处理造成获取不到CookieTip:文末可以下载例子猿人学该题官网:http://match.yuanrenxue.com/match/3确定逆向目标请求加密一般在参数头部(Cookie,token,还有其他的自定义的),这里就只有Cookie了开始逆向既然第二个请求,带有Cookie,那么就需要确定该Cookie是生成,还是服务器返回,这里还有第一个请求,所以大概率是服务器返回分析请求第一个请求状态码是202(表示服务器端已经收到请求消息,但是尚未进行处理),还有Set-Cookie,那就可以确定是服务器返回的了获取不到Cookie的原因这里获取不到Cookie,有两个原因一、反爬(对请求头部的顺序有要求,最好是使用原始顺序)二、requests框架的原因(框架会对设置的头部进行优化修改)获取Cookie规避了上面两个问题后,就可以获取到Cookie请求数据importrequestsdefapp(page:int)->dict:session=requests.Session()session.headers={'Host':'match.yuanrenxue.com','Connection':'keep-alive','Pragma':'no-cache','Cache-Control':'no-cache','User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.101Safari/537.36','DNT':'1','Accept':'*/*','Origin':'http://match.yuanrenxue.com','Referer':'http://match.yuanrenxue.com/match/3','Accept-Encoding':'gzip,deflate','Accept-Language':'zh-CN,zh;q=0.9',}session.post(url="http://match.yuanrenxue.com/jssm",)print(session.cookies)s=session.get(url="http://match.yuanrenxue.com/api/match/3",headers={"User-Agent":"yuanrenxue.project",},params={"page":page})returns.json()if__name__=='__main__':for_inrange(1,6):print(app(_))结果正确Tip:直接访问数据接口,会返回一段Js其实这段代码一点用都没有。完整代码:https://pan.bigdataboy.cn/s/JZh7
难点加密在Cookie,Cookie有时间限制,暗桩反调,混淆,调试阻塞Tip:文末可以下载例子猿人学该题官网:http://match.yuanrenxue.com/match/2确定逆向目标开始逆向加密值在Cookie有两种情况,该加密Cookie是服务器返回或者是本地生成分析请求反ob混淆这段代码经过了ob混淆,格式化后是这样的一键ob反混淆(猿人学提供有)网址:http://tool.yuanrenxue.com/decode_obfuscator分析加密处反混淆后,直接看见了document["cookie"],还有location["reload"]();这个不就就是:设置Cookie然后再次请求嘛调试加密值发现M()函数为空,V(Y)函数疑似加密结果导出加密值这个加密值在函数内部,想要使用就用全局变量接收一下暗桩一这段代码,其中!a6["test"](a2)执行起来很慢,并且一直返回true,所以直接写死,就会发现执行起来很快了,一些调试工具也不会阻塞了vara2=B(this,function(){vara5=function(){vara6=a5["constructor"]("return/\"+this+\"/")()["compile"]("^([^]+(+[^]+)+)+[^]}");return!a6["test"](a2);//true};returna5();});暗桩二如果说暗桩一是技巧,那暗桩二才是真正的暗桩,可以看这段代码改造了console.log只要我们一使用就会陷入,虽然这个暗桩对Js执行没有影响,但是调试就很容易陷入console=newObject()console.log=function(s){while(1){for(i=0;i<1100000;i++){history.pushState(0,0,i)}}}console.toString='[objectObject]'console.log.toString='?toString(){[nativecode]}'请求计算结果稍微修改Js,计算结果importexecjsimportrequestsdefget_c():returnexecjs.compile(open('./main.js','r',encoding="utf-8").read()).call('f')defapp(page:int)->dict:rep=requests.get(url="http://match.yuanrenxue.com/api/match/2",headers={"Cookie":f"sessionid=你的sessionid;m={get_c()};","User-Agent":"yuanrenxue.project",},params={"page":page})returnrep.json()if__name__=='__main__':sum=0for_inrange(1,6):data=app(_)forvalueindata.get('data'):sum=sum+value.get("value")print(sum)结果正确完整代码:https://pan.bigdataboy.cn/s/EYSW
难点源码压缩,混淆,反调,时间检测Tip:文末可以下载例子猿人学官网:http://match.yuanrenxue.com/确定逆向目标这个m值开始逆向过反调跟值此处是加密的url在发送出去最后出现的地方,我们可以通过函数调用的栈,进行跟值,找到大体加密的地方本地调试最终我们跟到了此处,发现它在<script>标签里,我们无法格式化,就无法断点调试我采用把整个网页扒下来,修改其中的<href>为绝对路径,采用fiddler本地替换网页响应,实现把在本地把<script>压缩的代码格式化,在手动加断点寻找window.f值逆向需要有怀疑精神,oo0O0()这个函数返回空,不会这么简单吧,进入它定位加密位置经过分析最终确定在这一行改造加密函数到这里,差不多就分析完成,就改造一下加密函数就完成了functionf(){varmv=Date.parse(newDate())+100000000;varwindow={};varm=eval("把eval里的全部复制过来window.f=hex_md5('bigdataboy')".replace(/bigdataboy/,mv))//这里有个小坑必须要用替换,因为拼接的md5值是不对的returnm+'丨'+mv/1000}请求计算结果importexecjsimportrequestsdefget_m():returnexecjs.compile(open('./main.js','r',encoding="utf-8").read()).call('f')defapp(page:int)->dict:rep=requests.get(url="http://match.yuanrenxue.com/api/match/1",headers={"Cookie":"自己的sessionid",'User-Agent':'yuanrenxue.project',},params={"page":page,"m":get_m(),})returnrep.json()if__name__=='__main__':sum=0count=0for_inrange(1,6):data=app(_)count=count+len(data.get("data"))forvalueindata.get("data"):sum=sum+value.get("value")print(sum/count)结果成功完整代码:https://pan.bigdataboy.cn/s/Nnsy