此题来源于某一个对抗网站,无聊玩玩分析和对抗,锻炼锻炼自己的逻辑和分析能力!
该题还是有些东西了,需要运用一些技巧,分析过程极其头疼,调试过程中经常出现运行结果和执行结果不同!
题目内容
任务5:抓取全部5页直播间热度,计算前5名直播间热度的加和
注:本页cookie有效期仅有50秒钟
初步分析
请求内容如下:
1 | GET https://match.yuanrenxue.com/api/match/5?page=2&m=1650885312646&f=1650885310000 HTTP/1.1 |
返回内容:
1 | HTTP/1.1 200 OK |
将请求进行重放,发现返回内容为空;对返回头进行系查看,发现返回头正常,说明服务器响应正常
1 | Date: Mon, 25 Apr 2022 11:17:33 GMT |
那么应该就是提交的参数存在问题,回想题目存在cookie50秒有效,猜测可能是cookie过期,应该不是由于数据包的单一禁重放问题,那么重新抓包,并马上二次发送
1 | {"status": "1", "state": "success", "data": [{"value": 7880}, {"value": 1179}, {"value": 8637}, {"value": 6510}, {"value": 6346}, {"value": 3460}, {"value": 3690}, {"value": 8395}, {"value": 2406}, {"value": 5634}]} |
OK,返回正常。对cookie参数以及url参数进行二分法发送和修改,发现参数中m,f,以及cookie中的m和RM4hZBv0dDon443M是必要的!
那么可以有一个初步结论,那么就是url中的参数是时间戳,两者是否存在相关性不确定,但是两者应该和cookie的参数存在强烈的相关性,也就是cookie应该是由m和f生成的!
深度分析
ok,基本可以确定本次攻防是和cookie有关,那么只需要等待50秒或者将cookie修改成错误的值,使cookie重新生成,并且定位到cookie生成的位置,那么基本就能得到题目的解了!
对于cookie生成的定位,可以通过hook使其生成时中断,然后查看堆栈即可定位到,hook代码如下:
1 | // ==UserScript== |
在开发者工具的调试器中输入该代码,并修改本地存储的cookie,使其cookie错误开始生成
ok,点击下一页,理论上就会触发断点
但从实际操作发现,并没有发生断点,进行调试检查操作。重新输入hook代码,在console处输入:
1 | document.cookie="" |
成功断下来,说明hook代码没问题,那么继续定位刚刚的问题,重新点下一页,还是没有断下来,再次在console处修改cookie,发现此时并没有成功断下来
经过上面的问题定位,可以基本确认可能是点击下一页后导致hook代码失效,也就是说是页面刷新导致的!
此时,解决方案如下,利用油小猴插件,注入脚本代码,在页面载入时自动注入hook代码即可!
注入后,成功断下来,观察堆栈信息
将其在控制台打印,可以基本确认此处是cookie的m生成值,$Wa 是1650886418000,_0x474032($Wa)是’69e2b50c58fa7c93da4ba854306fe5e0’。这里进行小小的猜测,对1650886418000进行md5加密是4ef47f4ebd857f2efc40f1749239ff3b,那么 _0x474032应该不是一个简单的md5加密函数!
此时进入_0x474032定义处
1 | function _0x474032(_0x233f82, _0xe2ed33, _0x3229f9) { |
这是一个简单的混淆,利用控制台对三元判断进行筛选,可以得到函数解混淆是:
1 | function _0x474032(_0x233f82, _0xe2ed33, _0x3229f9) { |
再次进入_0x37614a,找到定义,下个断点
1 | function _0x37614a(_0x32e7c1) { |
不难发现,_0x32e7c1是1650886418000,_0x41873d(_0x32e7c1)运算后结果是’iâµ\fXú|\x93ÚK¨T0oåà’,再进行_0x499969(‘iâµ\fXú|\x93ÚK¨T0oåà’)得到’69e2b50c58fa7c93da4ba854306fe5e0’。
此时分为两步,分别分析_0x41873d和,再进行_0x499969。一步一步分析,虽然过程不是很顺利,但是最终也可得到结果。
ok,那么下一个分析RM4hZBv0dDon443M的生成,RM4hZBv0dDon443M定位还是有点难的
还是一样,下hook断点,定位生成的地方
1 | try { |
很明显,此处 ‘R’ + ‘M’ + ‘4’ + ‘h’ + ‘Z’ + ‘B’ + ‘v’ + ‘0’ + ‘d’ + ‘D’ + ‘o’ + ‘n’ + ‘4’ + ‘4’ + ‘3’ 是为了防止被搜索得到,并且将 _0x4e96b4[‘_$ss’]得到
1
2
3
_0x4e96b4['_$ss']
'4YdsK7l1cxkyOncsZ+PsF6dlJF1G9NaEDwPSVc0pWsWBXxzbmb7RlsBVegcRDL/v3/hHJba/RySK+nJvfKHa7215u7ScHaIxwDNqgumavj19vcyrFqHwEGjgoPMGSNU4D2iCTFXKDcDh62fQS/LMDGk5zNznY/fm1hrRyMxuH9c4Afas/4/KJfEDrY162S8YWUZoKJJUPRuAXmrw7ZOhzWdrqSCfHoRQsclDh0rNcTE='
那么就知道RM4hZBv0dDon443M应该是和_0x4e96b4[‘_$ss’]挂钩,也就是说,只要找到_0x4e96b4[‘_$ss’]赋值的地方就行了,ok,直接搜索_0x4e96b4[‘_$ss’],发现仅有一次,没有对他赋值的地方,应该是在赋值的过程中混淆了
对堆栈进行检查,仅发现四处堆栈,且关系不是很大
进入该函数
发现这里是一个while循环,并且不断的判断,猜测应该是不断对if下再次赋值再次if,不断的控制流然后赋值。此处可以用一个比较巧妙的方式,在第一个if处进行断点,且该断点是日志断点,打印出_0x456805和_0x4e96b4[‘_$ss’],那么就可以知道_0x4e96b4[‘_$ss’]是在什么时候被赋值了,日志断点代码如下:
1 | _0x456805, _0x4e96b4['_$ss'] |
发现日志打印如下:
1 | VM15735:1 353 undefined |
也就是说,0x4e96b4[‘$ss’]应该是在154这个过程中找到的,那么只要我们进入154的判断,就能找到生成的地方了。
但,事实上,代码经过混淆
1 | while (0x1) { |
首先他不是以明文数字进行判断,而是十六进制,如果人为肉眼识别16进制,还是非常麻烦的。并且他的顺序并不是从小到大或者从大到小的,所以154的判断分支也不一定只有一个,还有判断的过程中是否和上一个if值有关。
一个折中的方法,就是进行条件断点,比如说当xxx == 154时中断下来,但是这个过程中xxx== 154的有很多个,从下面的代码可以看到整个循环不包括if判断就有800次,如果加上if判断,可以呈现几万次控制流行动。
1 | for (var _0x307e6a = 0x0; _0x307e6a <= 0x320; _0x307e6a++) { |
ok,如何解决上面的难题呢?一个很巧妙的方式如下,既然要找154,并且这个154要求下一步是生成_0x4e96b4[‘_$ss’]的地方,那么如果能将上述的值进行关联,也就是说,如果能确定最后三个值分别是56,56,154,那么就能确定这个154是我们要找的154,并且这个154是生成_0x4e96b4[‘_$ss’]的地方!
在while开始前,下一个条件断点,代码如下:
1 | if (_0x5aa5a3[_0x89f606] == 154 && _0x5aa5a3[_0x89f606 - 1] == 56 && _0x5aa5a3[_0x89f606 - 2] == 56) { true } |
成功断下后,单步走,发现敏感位置,有点像ob混淆,单步走到生成位置
发现关键代码,最关键的是’_$‘ + _$UH[0x348][0x1] + $UH[0x353][0x1]就是’$ss’,也就是说_0x4e96b4[‘_$‘ + _$UH[0x348][0x1] + _$UH[0x353][0x1]]就是_0x4e96b4[‘_$ss’],此处为_0x4e96b4[‘_$ss’]赋值位置!
1 | _0x43e7b7(), |
对上述代码进行调试后,发现生成值应该就是:
1 | _$Ww = _$Tk[_$UH[0x2db]][_$UH[0x2dc]][_$UH[0xff]](_0x4e96b4['_$pr'][_$UH[0x1f]]()), |
继续分析,_$Ww 是一个41长的数组,对他进行还原
1 | _0x4e96b4['_$pr']=[ |
对生成位置进行还原
1 | _0x4e96b4['_$qF'] = { |
ok,那么加密应该就是AES,AES导入可以通过封包搜索找到以下代码:
1 | <script src="/static/src/core.js"></script> |
将上述代码进行合并写入同一个js即可调用aes了。
那么剩下最后两个问题,一个是_0x4e96b4[‘_$qF’]怎么来的,一个是_0x4e96b4[‘_$pr’]。
先分析下_0x4e96b4[‘_$qF’],通过搜索得到
即如下,通过打印发现_0x4e96b4[‘_$is’]是’1650889239317’,基本确定应该是和url中的参数有关。
1 | _0x4e96b4 = window; |
相比_0x4e96b4[‘_$qF’],_0x4e96b4[‘_$pr’]的分析就有点困难了。
还是整体搜索,找到有关的地方,发现有7处
1 | _0x4e96b4['_$pr'] = new _0x4d2d2c(); |
对他们分别打上断点,_0x4d2d2c()是[],作用是新建一个数组吧,发现相关的仅有
1 | _0x4e96b4['_$pr']['push'](_0x474032(_$yw)); |
可以基本确定_0x474032生成的,在这两处打上日志断点,分别输出传入值和传出值
1 | _$yw,_0x474032(_$yw) |
得到5个值得,可以确定传入值4个一样,一个不同,并且这两个值来源于url中,都是有个问题,传入值相同,可传出值不同。
1 | VM190324: 1650890846000 'aeb9062cfd96d41d94822c42fd158605' |
对push位置进行分析,看一下执行后是否存在修改其他值
1 | _0x4e96b4['_$pr']['push'](_0x474032(_$Wa)); |
果然,push后,对两个关键值进行了修改,对_0x2d5f5b,_0x12eaf3定义分析
1 | function _0x2d5f5b() { |
1 | function _0x12eaf3() { |
调试后进行解混淆
1 | function _0x2d5f5b() { |
1 | function _0x12eaf3() { |
ok,基本解决了这两个参数,那么最后进行个收尾工作,进行封装
实验
生成url和cookie代码:
1 | function getReq(t_f,page) { |
生成RM4hZBv0dDon443M代码
1 | function generateCookie_RM4hZBv0dDon443M(t_f, t_m) { |
生成m代码:
1 | function _0x37614a(_0x32e7c1) { |
调用结果:
1 | ["https://match.yuanrenxue.com/api/match/5?page=1&m=1650879335110&f=1650879335110", |
解答题目:
1 | * “{"status": "1", "state": "success", "data": [{"value": 3401}, {"value": 9806}, {"value": 1497}, {"value": 5012}, {"value": 5676}, {"value": 4509}, {"value": 8380}, {"value": 6019}, {"value": 3862}, {"value": 1989}]}” |
成功过关!
总结
请问有什么话想说的吗?
坑
加密处居然出现了3处,虽说混淆程度还是很低,但是很难受
这道题其实有瑞数的影子,真的,首先官网明说了,后面自己做的时候感受到了。瑞数是反爬界的大山,大山。。。。