
加速乐补环境
更新: 2025/2/24 字数: 0 字 时长: 0 分钟
补环境时,大家经常遇到的是 'window' is not defined
那像这样的报错提示应该如何处理?Node环境下一般如下定义:
// 运行js时遇到window未定义,添加下面的代码:
window = global;
如果只是单单缺少了 window
这一个变量的定义,像上面这样报错自然就消失了。除此之外,我们经常还遇到 'document' is not defined
和 'document.cookie' is not defined
之类的错误。那像这样的 document
应该怎么补?Node 环境下一般如下定义:
// 运行js时遇到document未定义,添加下面的代码:
document = {};
// 运行js时遇到document.cookie未定义,添加下面的代码:
document.cookie = '';
常用补环境的方法还有:
// 运行js时遇到navigator is not defined报错,解决办法在最上面添加下面代码:
global.navigator = {userAgent: 'node.js'};
还有一些补环境的应用或者常见操作:
window || global // 在浏览器中返回window,在node中window未定义
global || window // 在浏览器中global未定义,在node中返回global
还比如,有些只能在浏览器中运行的方法:
// 在浏览器中输出当前网页dom节点中class名称为hero-paragraph的第一个节点中的文本信息
document.getElementsByClassName('hero-paragraph')[0].textContent
但在node.js中运行报错,会提示 document is not defined
document未定义,当我们补上 document={};
后,又会提示 document.getElementsByClassName is not a function
不是一个方法,那么我们就重写一个方法:
// 定义document一个对象
document = {};
// 将getElementsByClassName重写为一个方法
document.getElementsByClassName = function (){
// 因为使用到了下标,因此这里返回一个数组
return [
// 数组中第[0]元素有textContent属性和其对应值
{textContent: '原赛事简介【比赛已结束】:'}
]
}
sign = document.getElementsByClassName('hero-paragraph')[0].textContent
console.log(sign);
简单加速乐
题目难度:简单
这道题是内部题目,就不提供网址了,只分享解题流程。
访问题目获取题目信息后,我们从Doc选项卡中的第二个请求找到了数据来源:
查看Fiddler的抓包一模一样,也是有两次请求,数据出现在了第二次请求响应中,和第一次请求相比发现在Cookie中多了一个 __jsl_clearance
字段,这就是加速乐的典型特征:
回看第一次请求中的响应头,没有发现有 Set_cookie
的信息,查看其响应发现是一串JS代码:
将代码拷贝后去掉头尾 <script>
标签,格式化后得到如下代码,运行这段代码发现没有结果输出,那么这十几行的代码唯一可疑的地方就是 while(z++)try{}catch{}
循环异常处理这段:
我们在 catch
当中添加输出操作,运行代码发现不断的在输出 error
,就说明上面的eval函数执行的字符串类型的JS代码报错了:
我们将 eval
函数改成 console.log
函数进行输出,得到了下面的JS代码:
继续拷贝JS代码并进行格式化,在函数第23行中 document.cookie
就是我们想要的值,但要注意 _N
变量后面赋值的内容有格式化检测,不能对其进行格式化,否则会报错:
第22行有一个setTimeout定时器来进行刷新操作,并使用到了 location.pathname
、location.search
变量,在Console栏打印得到内容如下:
// 补一个setTimeout空函数
setTimeout = function(){};
// 补一个location变量
location = {};
location.pathname = '/challenge/11';
location.search = '';
// 提示location.href是被赋值不用补。
第23行使用 document.cookie
属性,因此所补的环境如下:
document = {};
document.cookie = '';
第29行 var _N = document.createElement('div');
创建了一个div标签节点,这里要注意的是关于这种标签节点的都是补不了的,但可以用函数临时返回空进行替代,因此所补的环境如下:
// 上面定义了document就直接写,这里函数名必须和方法一样才有替换效果
document.createElement = function(val){
return ''
}
第30行 _N.innerHTML = '<a href=\'/\'>_1H</a>';
给 innerHTML
属性赋值,说明29行结果返回的是一个对象且有这个属性,因此修改上面代码为:
// 修改上面代码
document.createElement = function(val){
return {
// 该属性是被赋值操作,直接置为空
innerHTML: "",
}
}
第31行 _N = _N.firstChild.href;
给 _N
变量赋值对象的 firstChild
中的 href
属性值,说明该对象有该属性,直接打印 _N.firstChild.href
值如下,因此修改上面代码为:
// 修改上面代码
document.createElement = function(val){
return {
innerHTML: "",
// 直接将该对象的属性值置为结果值
firstChild: {
href: "https://www.python-spider.com/"
}
}
}
第58行 return !!window.addEventListener;
返回了一个事件监听的布尔值,通过在浏览器调试实际返回的是一个 true
,因此我们可以将此函数置空,其关系如下图,因此我们所补的代码如下:
window = global;
window.addEventListener = function (){};
第63行 document.addEventListener('DOMContentLoaded', _N, false)
添加了一个事件监听,并传入3个实参,其中一个就是 _N
函数,传入了肯定是要执行的,因此我们所补的代码如下:
// 上面定义了document就直接写
document.addEventListener = function (a, b, c){b()};
第64行 document.attachEvent('onreadystatechange', _N)
通过调试发现并不走这行代码,直接置为空函数即可,因此我们所补的代码如下:
document.attachEvent = function (){};
最后我们添加一个cookie的输出操作:
console.log(document.cookie);
将上面所有代码补全后,运行就得到了正确的结果了:
但到这里还没有结束,因为这段代码是 eval
函数执行的第二层代码,我们需要在第一层去执行看看效果,将所有补环境的代码拷贝到第一层代码的最上面运行,记得将之前第一层的 console.log
改回 eval
函数,发现也能得到正确的结果:
还有个问题,就是第一次请求的返回的JS代码是变化的,因此我们可以封装第一响应的JS代码为sdk,直接放在爬虫代码中运行:
function sdk(){
setTimeout = function(){};
location = {};
// 提示location.href是被赋值不用补。
location.pathname = '/challenge/11';
location.search = '';
document = {};
document.attachEvent = function (){};
document.createElement = function(val){
return {
innerHTML: "",
firstChild: {
href: "https://www.python-spider.com/"
}
}
}
document.addEventListener = function (a, b, c){b()};
document.attachEvent = function (){};
window = global;
window.addEventListener = function (){};
/*
第一次请求响应的JS代码
*/
return document.cookie;}
马蜂窝旅游网
题目难度:中等
马蜂窝旅游网是广受中国年轻一代追捧的旅行玩乐平台,被誉为中国的旅行圣经。得益于“内容+交易”的核心优势,马蜂窝将复杂的旅游决策、预订和体验,变得简单、高效和便捷。马蜂窝是旅游社交网站,是数据趋动平台,也是新型旅游电商,提供全球6万个旅游目的地的交通、酒店、景点、餐饮、购物、当地玩乐等信息内容和产品预订服务。
首先我们访问马蜂窝的网站,回看Fiddler中产生了三次请求,在第三次请求中返回了我们想要的结果,在Cookie中发现存在 jsl
字段,这是加速乐的典型特征:
看第一次请求,在响应头中有设置Cookie的 __jsluid_s
字段操作:
看第一次请求,响应内容中有设置Cookie的 __jsl_clearance_s
字段操作:
看第二次请求,发现在Cookie中多出了两个 __jsluid_s
、__jsl_clearance_s
字段,其值和上面第一次请求中的响应头和响应内容设置的值完全一样,可以说带着第一次请求设置的值进行的第二次访问。响应内容是经过ob混淆的js代码,暂不知作用:
看第三次请求,发现Cookie当中的 __jsl_clearance_s
的值发生了变化,说明第二次请求返回的内容会修改 __jsl_clearance_s
的值:
这里我们首先处理第一次请求返回的js代码,首先我们将代码拷贝下来,去掉 <script>
标签,补上缺失的环境,成功返回我们想要的结果:
// 所补环境
document = {};
location = {};
location.pathname = '';
location.search = '';
function a(){
// 加上第一次请求的内容
return document.cookie
};
现在我们处理第二次请求返回的ob混淆的js代码,首先我们将代码拷贝下来,去掉 <script>
标签,补上环境,通过断点调试发现,在150行设置了 document.cookie
属性,其字段恰好是 __jsl_clearance_s
,其值刚好就是go函数中传递实参中的bts数组的拼接值,但中间有两个字符 Av
未知来源:
最后通过调试断点跟踪,破解了所有字段:
go({
"bts": ["1648231315.137|0|TG4", "PqsyjB7CBobpmQHgyOTC4M%3D"], // __jsl_clearance_s的值
"chars": "nHCaAKLYoaXvshPXndsWQw", // 提供拼接字符
"ct": "e3ab1c60ec1220af58399a0b455ee6a28496dcbf", // 加密后的值
"ha": "sha1", // 加密方式
"tn": "__jsl_clearance_s", // cookie中的__jsl_clearance_s字段
"vt": "3600", // Max-age的值(不重要)
"wt": "1500" // 定时器的间隔时间(不重要)
}
// 逻辑流程如下:从chars的值中随机提供两个字符,将字符拼接在bts数组的值当中,通过ha的值的加密方式,直到加密结果的值等于ct的值,最后值就是1648231315.137|0|TG4AvPqsyjB7CBobpmQHgyOTC4M%3D
我们来验证一下字符 1648231315.137|0|TG4AvPqsyjB7CBobpmQHgyOTC4M%3D
的sha1加密是否等于 e3ab1c60ec1220af58399a0b455ee6a28496dcbf
,经过下面的验证猜想正确:
?> 提示:除了 sha1
哈希外, md5
哈希、sha256
哈希,这些哈希算法我们都可用python的hashlib库进行模拟。
创宇超防
下图就是访问一个使用了加速乐的网站抓包后的数据包:
第一层:返回一个需要带上的Cookie的js内容。
第二层:带上第一层的Cookie访问,返回一个ob混淆后的js内容 。
第三层:带上第二层通过js内容设置的Cookie后返回网页内容。