题目网址:https://match.yuanrenxue.com/match/2
本文比较新手向,大佬可能会觉得看着比较墨迹,如果有错误的地方欢迎指出。
全文手把手扣代码补环境,几乎包含了绝大部分可能会踩的坑。
土豪通道:
https://download.csdn.net/download/weixin_45608294/85427606?spm=1001.2014.3001.5503https://download.csdn.net/download/weixin_45608294/85427606?spm=1001.2014.3001.5503不过建议还是自己动手扣一下,下面是正文
这道题的题目要求是获取前五页的热度值并求得累加和,首先抓包看一下数据请求方式
可以看到请求数据的就只有一个get请求,本题的题目就是动态cookie,从实际请求中也可以看出m就是那个加密的参数,所以完成这道题的关键就是如何获取m。
清除缓存重新加载界面遇到了无限debugger,直接鼠标右键停用断点过掉。刷新页面
可以看到两个一样的请求,但是一个没有cookie,看不到响应,且响应头没有setcookie,另一个带有cookie m 并且正好是网页的几面内容。由此可以看出,cookie m的值是第一次请求后由js生成出来的。
使用postman尝试发送第一个get请求
可以看到是将js代码以script标签的形势放入了html文档中,进一步验证了cookie是由js生成的猜想。知道了大概位置,就可以开始找具体代码了。
在源代码选项卡中找到事件监听断点, 勾选脚本,这样在遇到js时会自动断下,清除浏览器中保存的cookie,刷新界面
成功将页面断下,不过可以看到此处的代码与postman收到的不一样,点击继续运行
成功来到postman验证的代码,格式化以后尝试寻找cookie生成位置, 发现代码混淆严重,只能动态调试。
现在在控制台输入hook cookie的代码(这里我是在控制台手工hook,有条件的话也可以用油猴脚本等浏览器插件或者fiddler插件之类的进行hook),
(function () {
Object.defineProperty(document, 'cookie', {
set: function (val) {
debugger;
return val;
},
});
})();
hook好以后 让网页继续运行,成功断在cookie生成位置
此时的m已经生成出来了,不过可以通过调用堆栈找到生成的位置。
将代码格式化以后可以看到,cookie的生成位置在4922行这一超长的代码中
document[$dbsm_0x42c3(qqLQOq, iOiqII) + $dbsm_0x42c3(q1IoqQ, QQlLlq)] = _0x5500bb['\x4e\x74\x44' + '\x72\x43'](_0x5500bb[$dbsm_0x42c3(qqqQoq, oqQiiO) + '\x6d\x65'](_0x5500bb[$dbsm_0x42c3(Ioo0ql, olq0Oq) + '\x6d\x65'](_0x5500bb[$dbsm_0x42c3(qOIqQi, OOqIQi) + '\x72\x44'](_0x5500bb[$dbsm_0x42c3(Q1qoqQ, lILOOq) + '\x72\x44'](_0x5500bb[$dbsm_0x42c3(qOO1Q0, oiqlQQ) + '\x72\x44'](Ql1OO0, _0x5500bb['\x7a\x76\x67' + '\x6c\x77'](_0x3c9ca8)), Qoqq0I), _0x5500bb[$dbsm_0x42c3(iqOiQ0, QOiq0Q) + '\x47\x6b'](_0x313b78, _0x160e3a)), lOo0QQ), _0x160e3a), _0x5500bb[$dbsm_0x42c3(qiOOiO, liQIoQ) + '\x4e\x5a']),
将等号右边的一大长串代码放到控制台中查看,可以确定一大长串代码就是m的生成位置 。
仔细看看这一大对代码的结构,可以看出虽然很长,但是也是由一部分一部分拼接出来的。
将代码拆解一下得到如下结果
_0x5500bb['\x4e\x74\x44' + '\x72\x43'](_0x5500bb[$dbsm_0x42c3(qqqQoq, oqQiiO) + '\x6d\x65']
(_0x5500bb[$dbsm_0x42c3(Ioo0ql, olq0Oq) + '\x6d\x65']
(_0x5500bb[$dbsm_0x42c3(qOIqQi, OOqIQi) + '\x72\x44']
(_0x5500bb[$dbsm_0x42c3(Q1qoqQ, lILOOq) + '\x72\x44']
(_0x5500bb[$dbsm_0x42c3(qOO1Q0, oiqlQQ) + '\x72\x44']
(Ql1OO0, _0x5500bb['\x7a\x76\x67' + '\x6c\x77'](_0x3c9ca8)), Qoqq0I),
_0x5500bb[$dbsm_0x42c3(iqOiQ0, QOiq0Q) + '\x47\x6b']
(_0x313b78, _0x160e3a)), lOo0QQ), _0x160e3a),
_0x5500bb[$dbsm_0x42c3(qiOOiO, liQIoQ) + '\x4e\x5a'])
可以看到,出现最多的东西,就是_0x5500bb
通过控制台验证可以看到,这个_0x5500bb里面包含了很多字符串和函数,也就是说上面的代码都是在调用它里面的函数。
那么只需要验证里面的东西都是什么,就可以找到m的生成位置了。
利用控制台将内容简单还原方便后续观察,得到如下结果
_0x5500bb['NtDrC'](_0x5500bb['KGBme']
=(_0x5500bb['KGBme']
(_0x5500bb['mQKrD']
(_0x5500bb['mQKrD']
(_0x5500bb['mQKrD']
(Ql1OO0, _0x5500bb['zvglw'](_0x3c9ca8)), Qoqq0I),
_0x5500bb['wWbGk']
(_0x313b78, _0x160e3a)), lOo0QQ), _0x160e3a),
_0x5500bb['TfZNZ'])
可以看出,这是一个函数套函数的大套娃,接下来进行逐一验证
可以看出,最后一个调用的函数结果是cookie中最后的部分,而_0x160e3a则是一个时间戳(_0x313b78, _0x160e3a)的结果仍然是一个时间戳,而_0x5500bb['wWbGk'](_0x313b78, _0x160e3a)的值就是cookie的前半部分。还记得之前找到的cookie是什么样的嘛?
综上所述,我们可以得出
Cookie = _0x5500bb['wWbGk'](_0x313b78,_0x160e3a)+lOo0QQ+_0x160e3a
再通过控制台看一下这个wWbGk是个什么东西
原来是一个函数,里面有两个参数,就是用第一个参数调用第二个参数 。进一步简化上面的等式
Cookie = _0x313b78(_0x160e3a)+"|"+_0x160e3a
// _0x160e3a为时间戳
//此时值为: 1653183600000
找到了生成逻辑以后,就可以开始补环境大法了。
我这里使用装了NodeJs插件的Pycharm进行调试,当然,都是根据个人喜好自由选择。
这里提示了_0x313b78没有找到,我们去控制台看看它是怎么定义的
可以看到这是一个函数,在源代码中搜索找到它的位置,将其完完整整的抠出来,再次调试js。
又提示_0x5500bb没有定义,接下来只需要不停的重复上面的步骤,什么没有定义,就去源代码里面找到它并将其扣下来。
这里还有一个需要注意的小技巧,就是你将扣下来的代码放到自己的js文件中时,要注意它提示not defined的行是哪一行,找到它然后放在那一行所在的函数的前面即可。一味的放到文件开头有时候可能也会出错。
这里可以看到_0x5500bb是一个大列表,而它的实质则是上面的_0x443ddb,所以这里要赋值的这一行,到上面的整个_0x443ddb都给扣下来。
补完以后又开始提示未定义了,通过控制台确定是什么东西以后继续补。
这里碰到个乱码没有定义,去控制台看是什么发现报错了。但是不要慌,在源码里面先搜一下看看
可以看到这个东西是在一个自执行函数里面定义的,为了防止多余的变量污染,秉持着缺啥补啥的原则,这里我只把这一大串乱码扣下来补了上去,而不是整个函数都扣。在扣的时候还发现这串定义的乱码正好接在了前面补的代码头上,抠出来的代码也随着原来的样子拼了上去。(这个和前面提到的将抠出来的代码放到报错行前面并不冲突)
再次运行调试
出现了新的未定义,继续开始扣扣补补。
在补完一个大数组以后,猛的发现NodeJs报错了,运行不了?遇到这个问题的时候,把整个代码拿出来放到真实的浏览器去debug一下先。
新开一个 标签页打开开发人员工具,避免前面补环境用的标签页出现意外。
在源代码选项卡中找到代码段(找不到的话可以点那个向右的>>然后再进代码段),新建一个代码段将刚才抠出来的所有代码复制进去。
为了方便调试,咱们在所有代码的最前面,也就是第一行加上一个debugger,来手动执行代码。
在代码段上右键运行代码,成功进入debug状态,然后点击按钮一个函数一个函数的来执行。
还没点几下,在执行到乱码这里时突然整个标签页都卡死了,开发者工具也无法操作,基本上可以确定问题就是出现在这里了。
刷新刚才新建的标签页重新来到开发者工具。将第一行的debugger删除掉在乱码前面重新加上。 重新运行代码段,在进入debug状态后,点击进入函数调用进一步查看出错的地方。
点着点着你就会发现当执行到这里的时候,进入了死循环,怎么也出不去。这里就是NodeJs执行报错的原因了。
向上翻看一下源码可以看到,只要来到了这个函数就会必然进入这个死循环,所以我们必须想办法将其绕过。
从代码和和调用堆栈中都可以看出,在WjJIeN函数中有一个if语句,如果!Boolean(~_0x4859ef)为真的话就会返回一个_0x4859ef为假的话则会调用这个死循环函数。
所以我们需要让这个布尔值变成是真,但是这个布尔值是通过判断参数来决定的。通过调用关系可以看出这个参数是由上一个函数提供的。
分析到这里就已经很明显了,是否进入这个死循环就是由vnuqco函数中的_0x3fba94决定的。而它则是由下面这个三元表达式决定的
_0x2940ac['test'](this['XxpyjG']['toString']()) ? --this['yHmSUE'][0x1] : --this['yHmSUE'][0x0];
使用控制台验证判断的表达式
可以看到,是调用_0x2940ac的test方法。如果你仔细看前面的代码,就会发现这个_0x2940ac其实是定义的一个正则表达式,而被检测的内容,也恰好就是上面的函数定义。
正则表达式的内容如下:
/\w+ *\(\) *{\w+ *['|"].+['|"];? *}/
就是用这个正则表达式去匹配上面定义的XxpyjG的函数代码。匹配成功则返回true否则返回false。
其实如果毕竟懂正则表达式的话,应该可以看出这个表达式就是验证代码是否被格式化过。我这里在控制台验证一下
调试的时候默认情况下是false的,但是把格式化代码生成的空格和换行符删掉后再次匹配就变成了true。
修改好函数定义以后,清除掉所有的debugger和断点。重新尝试运行脚本,一定要主要这里的函数定义必须把所有的换行和多余的空格(return后面那一个不算)全部清理干净,可以参考我的图片。
系统提示有东西未定义,而不是直接卡死,说明刚才那个格式化检测已经过掉了。
此时将Pycharm中的js文件对应修改好后,进行调试
也是提示未定义,没有问题。继续重复之前的步骤,准备扣代码补环境。
但是此时你再仔细观察一下,会发现报错的这行代码的上面不是有一个已经定义的_0x313b78吗?怎么会提示还提示没有定义呢。其实在这里是因为前面补的一大长串代码的最后面是一个逗号,我们将其改成语句结束的分号再调试一下。
成功通过,但是又报了一个新的无定义。但是这个报错的内容怎么好像找不到?其实是因为上面的字符串被转码后导致的,你可以根据报错的行号和错误提示上面的原代码来找到具体的内容。
找到以后在控制台查看这是个什么东西
可以看到这是调用的_0x5500bb里面的KNqZI函数。
利用之前过格式化检测的方法,将代码放到浏览器中进行调试
可以看到函数显示未定义,而且里面调用的函数也和原版中的不一样。
通过比较可以看出,所有的参数都是没问题一致的,所以问题只能出在$dbsm_0x42c3上面
这里可以看到这个函数虽然是已经补过的,但是上面的自执行函数的参数却是这个函数。
其实这里是一个比较常见的混淆套路,一个大数组保存16进制的函数名,然后有一个在代码里出现频率极高的解密函数(此处就是$dbsm_0x42c3),除了解密函数以外,还有一个用于将16进制字符串还原的移位函数(也就是上面那个参数是解密函数的自执行函数)。
在上次补解密函数的时候,没有补上这个移位函数,所以解密函数解密出来的内容出了错(乱码),自然也就找不到正确的函数,也就会报函数未定义了。
解决办法很简单,只要把移位函数补上去即可。
成功通过,然后继续开始扣代码补环境。
在补移位函数的时候需要注意下里面还存在格式化检测,有兴趣了解具体原因的可以参考上次过格式化检测的方法来搞定。如果懒得调试可以直接将移位函数压缩,然后再补上即可。
补完移位函数后会有非常多的函数要一个一个补,一定要有耐心,自行参考前面补环境的方法,此处不再详解。
当补到_0x1316f4的时候你可能会发现调试js文件时,node会一直执行,其实这里是因为你扣代码的时候把最后的自执行括号给加上去了,实质上这里应该是一个函数
在补环境的时候要注意与原版的要保持一致,不清楚时可在debugger的控制台中查看。
补环境的途中你可能会碰到如下报错:
提示这个是因为要用到浏览器的属性,而我们用NodeJs没有这个环境导致的,只需要在文件开头补上即可,环境代码如下:
window = global;
window.navigator = {};
补到最后,你会发现,程序直接运行结束了
这说明环境已经补的差不多了。在底下console.log一下看看
又报了一个未定义,但是这个报错的确实console.log,看来是上面的环境代码为了防止调试把console.log给改掉了。我们在文件的最开头把console.log保存一下然后再调用咱们自己保存的来调试。
至此,整个补环境已经完成,接下来只需要调用这个js就可以获取到m的值