第六题:js 混淆 - 回溯
1、前言
万万没想到,距离上次做完第四题之后,再次动手写猿人学赛题解析的博客已经是四个多月之后了。其中各种艰辛,难以言表,只能说逆向这鬼东西水太深了,要掌握的各种知识和骚操作是真的不少,奈何自己人菜瘾还大,不信邪非要靠自己手撕代码,想在不借助外力的情况下,自己把代码还原出来,结果是撞南墙撞的头破血流,一颗勤奋好学的热血之心险些就此熄灭。第六题相比第五题还算简单,即使这样我也前前后后琢磨了差不多小半个月,惭愧啊,话不多说,直蹦主题。
2、题目理解
一开始看到这题的时候,我第一眼关注的是回溯两个字,想着肯定是要靠层层挖掘混淆后加密代码逻辑,最终还原出真实的加密代码。事实证明有时候过度理解不是啥好事,因为这题他尽管有回溯,但回的没我想象的那么多,最后证明了使用正确解题方法要比死扣代码高效的多!
3、加密原理解析
事实上,这题解析过程我大致经历了两个阶段,从一开始的生啃硬扣代码逻辑到后面得知这加密原理可以一眼看出,我的内心是崩溃的o(╥﹏╥)o,所以了解RSA的朋友们,可以直接跳到3.2了,如果需要的话。。。
3.1、加密位置判断
先看题目加载内容,既然是要找奖金总额,那想必奖金金额应该是通过ajax请求异步返回的:
果不其然,在chrome浏览器的XHR请求里,找到了这个请求的url和对应的返回值,观察一下发现这些值只是三等奖的中奖金额,但题目要求的可是全部中奖金额,那一等奖和二等奖的值是从哪来的呢?
这里回到网页的源码部分,可以很容易找到其他金额的生成逻辑,这段代码出现了跟数值相关的操作,推测就是其他奖金金额生成的位置。算了一下,二等奖是三等奖的8倍,一等奖是三等奖的15倍,总金额是一等奖的24倍,跟最终请求结果里的数值是一致的,所以这里的问题解决了。
回到请求奖金的这个url这里,发现这里有两个参数,m
和q
,我们要模拟正确的请求必然要找到这两个参数的生成逻辑,对于有经验的爬虫老手来说,在源代码里观察一下估计就能大概找到位置了。
这里还是用朴素的寻找逻辑讲讲这个过程,万一有比我还萌新的萌新看到这篇博客了呢,这里我是选择设置XHR断点,当检测到发送了包含api/match/6?m=
这段字符串的请求发起时,请求就会自动在这里断下来:
然后我们就可以看到整个调用栈了,一步步网上追,观察m
和q
最早是在哪里被赋值的:
很轻易的就在request
函数这里找到了m
和q
被赋值的地方,粗略一看,m
是由r
函数生成的,q
是由时间戳加其他字符一起拼接而成的,相对来说比较简单,q
里的window.o
在多观察几次以后发现就是翻页的页数,或者说每发送一次数据请求,这个值都会加1:
再来看m
,调试的时候鼠标放这一看,发现它是由pf.js
文件里的r
函数生成,好家伙,这就是第六题最难解决的加密参数了吧,毕竟一般能被单独放到js文件里加密的参数都不简单,反正我是经常惨败而归。
3.2、解密_萌新试错版
先看看我使用硬扣法,试图通过一步步还原m
值的惨痛经历。
首先在pf.js
文件文件内,我们一开始就追到了r
函数的位置,param1
是前面的时间戳t,param2
是翻页页数,初看是根据这两个值来进行加密。
r
函数里又调用了z
函数,z
函数内调用了函数_n
:
在这里,_n
的本体是n
函数,n
函数是匿名函数function(i)
的闭包函数:
_n("jsencrypt")
等于n("jsencrypt")
,这个函数运行结果实际上就是这个函数内的匿名函数n
:
匿名函数n
被new
了一下之后,变成了g