Skip to content

加速乐补环境

更新: 2025/2/24 字数: 0 字 时长: 0 分钟

补环境时,大家经常遇到的是 'window' is not defined 那像这样的报错提示应该如何处理?Node环境下一般如下定义:

javascript
// 运行js时遇到window未定义,添加下面的代码:
window = global;

如果只是单单缺少了 window 这一个变量的定义,像上面这样报错自然就消失了。除此之外,我们经常还遇到 'document' is not defined'document.cookie' is not defined 之类的错误。那像这样的 document 应该怎么补?Node 环境下一般如下定义:

javascript
// 运行js时遇到document未定义,添加下面的代码:
document = {};
// 运行js时遇到document.cookie未定义,添加下面的代码:
document.cookie = '';

常用补环境的方法还有:

javascript
// 运行js时遇到navigator is not defined报错,解决办法在最上面添加下面代码:
global.navigator = {userAgent: 'node.js'};

还有一些补环境的应用或者常见操作:

javascript
window || global  // 在浏览器中返回window,在node中window未定义
global || window  // 在浏览器中global未定义,在node中返回global

还比如,有些只能在浏览器中运行的方法:

20220327020458

javascript
// 在浏览器中输出当前网页dom节点中class名称为hero-paragraph的第一个节点中的文本信息
document.getElementsByClassName('hero-paragraph')[0].textContent

但在node.js中运行报错,会提示 document is not defined document未定义,当我们补上 document={}; 后,又会提示 document.getElementsByClassName is not a function 不是一个方法,那么我们就重写一个方法:

javascript
// 定义document一个对象
document = {};
// 将getElementsByClassName重写为一个方法
document.getElementsByClassName = function (){
    // 因为使用到了下标,因此这里返回一个数组
    return [
        // 数组中第[0]元素有textContent属性和其对应值
        {textContent: '原赛事简介【比赛已结束】:'}
    ]
}
sign = document.getElementsByClassName('hero-paragraph')[0].textContent
console.log(sign);

20220327021711

简单加速乐

题目难度:简单

这道题是内部题目,就不提供网址了,只分享解题流程。

访问题目获取题目信息后,我们从Doc选项卡中的第二个请求找到了数据来源:

20220319223323

查看Fiddler的抓包一模一样,也是有两次请求,数据出现在了第二次请求响应中,和第一次请求相比发现在Cookie中多了一个 __jsl_clearance 字段,这就是加速乐的典型特征:

20220319223833

回看第一次请求中的响应头,没有发现有 Set_cookie 的信息,查看其响应发现是一串JS代码:

20220319224106

将代码拷贝后去掉头尾 <script> 标签,格式化后得到如下代码,运行这段代码发现没有结果输出,那么这十几行的代码唯一可疑的地方就是 while(z++)try{}catch{} 循环异常处理这段:

20220319225125

我们在 catch 当中添加输出操作,运行代码发现不断的在输出 error,就说明上面的eval函数执行的字符串类型的JS代码报错了:

20220319225813

我们将 eval 函数改成 console.log 函数进行输出,得到了下面的JS代码:

20220319230300

继续拷贝JS代码并进行格式化,在函数第23行中 document.cookie 就是我们想要的值,但要注意 _N 变量后面赋值的内容有格式化检测,不能对其进行格式化,否则会报错:

20220319230714

第22行有一个setTimeout定时器来进行刷新操作,并使用到了 location.pathnamelocation.search 变量,在Console栏打印得到内容如下:

20220320015419

javascript
// 补一个setTimeout空函数
setTimeout = function(){};
// 补一个location变量
location = {};
location.pathname = '/challenge/11';
location.search = '';
// 提示location.href是被赋值不用补。

第23行使用 document.cookie 属性,因此所补的环境如下:

javascript
document = {};
document.cookie = '';

第29行 var _N = document.createElement('div'); 创建了一个div标签节点,这里要注意的是关于这种标签节点的都是补不了的,但可以用函数临时返回空进行替代,因此所补的环境如下:

20220320020723

javascript
// 上面定义了document就直接写,这里函数名必须和方法一样才有替换效果
document.createElement = function(val){
    return ''
}

第30行 _N.innerHTML = '<a href=\'/\'>_1H</a>';innerHTML 属性赋值,说明29行结果返回的是一个对象且有这个属性,因此修改上面代码为:

javascript
// 修改上面代码
document.createElement = function(val){
    return {
        // 该属性是被赋值操作,直接置为空
        innerHTML: "", 
    }
}

第31行 _N = _N.firstChild.href;_N 变量赋值对象的 firstChild 中的 href 属性值,说明该对象有该属性,直接打印 _N.firstChild.href 值如下,因此修改上面代码为:

20220320021713

javascript
// 修改上面代码
document.createElement = function(val){
    return {
        innerHTML: "", 
        // 直接将该对象的属性值置为结果值
        firstChild: {
            href: "https://www.python-spider.com/"
        }
    }
}

第58行 return !!window.addEventListener; 返回了一个事件监听的布尔值,通过在浏览器调试实际返回的是一个 true,因此我们可以将此函数置空,其关系如下图,因此我们所补的代码如下:

20220320170007

javascript
window = global;
window.addEventListener = function (){};

第63行 document.addEventListener('DOMContentLoaded', _N, false) 添加了一个事件监听,并传入3个实参,其中一个就是 _N 函数,传入了肯定是要执行的,因此我们所补的代码如下:

javascript
// 上面定义了document就直接写
document.addEventListener = function (a, b, c){b()};

第64行 document.attachEvent('onreadystatechange', _N) 通过调试发现并不走这行代码,直接置为空函数即可,因此我们所补的代码如下:

javascript
document.attachEvent = function (){};

最后我们添加一个cookie的输出操作:

javascript
console.log(document.cookie);

将上面所有代码补全后,运行就得到了正确的结果了:

20220320171048

但到这里还没有结束,因为这段代码是 eval 函数执行的第二层代码,我们需要在第一层去执行看看效果,将所有补环境的代码拷贝到第一层代码的最上面运行,记得将之前第一层的 console.log 改回 eval 函数,发现也能得到正确的结果:

20220320171536

还有个问题,就是第一次请求的返回的JS代码是变化的,因此我们可以封装第一响应的JS代码为sdk,直接放在爬虫代码中运行:

javascript
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;}

马蜂窝旅游网

题目难度:中等

地址:https://www.mafengwo.cn

马蜂窝旅游网是广受中国年轻一代追捧的旅行玩乐平台,被誉为中国的旅行圣经。得益于“内容+交易”的核心优势,马蜂窝将复杂的旅游决策、预订和体验,变得简单、高效和便捷。马蜂窝是旅游社交网站,是数据趋动平台,也是新型旅游电商,提供全球6万个旅游目的地的交通、酒店、景点、餐饮、购物、当地玩乐等信息内容和产品预订服务。

首先我们访问马蜂窝的网站,回看Fiddler中产生了三次请求,在第三次请求中返回了我们想要的结果,在Cookie中发现存在 jsl 字段,这是加速乐的典型特征

20220326020319

看第一次请求,在响应头中有设置Cookie的 __jsluid_s 字段操作:

20220326020702

看第一次请求,响应内容中有设置Cookie的 __jsl_clearance_s 字段操作:

20220326021030

看第二次请求,发现在Cookie中多出了两个 __jsluid_s__jsl_clearance_s 字段,其值和上面第一次请求中的响应头和响应内容设置的值完全一样,可以说带着第一次请求设置的值进行的第二次访问。响应内容是经过ob混淆的js代码,暂不知作用:

20220326021841

看第三次请求,发现Cookie当中的 __jsl_clearance_s 的值发生了变化,说明第二次请求返回的内容会修改 __jsl_clearance_s 的值:

20220326022335

这里我们首先处理第一次请求返回的js代码,首先我们将代码拷贝下来,去掉 <script> 标签,补上缺失的环境,成功返回我们想要的结果:

20220326024442

javascript
// 所补环境
document = {};
location = {};
location.pathname = '';
location.search = '';

function a(){
    // 加上第一次请求的内容
    return document.cookie
};

现在我们处理第二次请求返回的ob混淆的js代码,首先我们将代码拷贝下来,去掉 <script> 标签,补上环境,通过断点调试发现,在150行设置了 document.cookie 属性,其字段恰好是 __jsl_clearance_s,其值刚好就是go函数中传递实参中的bts数组的拼接值,但中间有两个字符 Av 未知来源:

20220326030153

最后通过调试断点跟踪,破解了所有字段:

javascript
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,经过下面的验证猜想正确:

20220326032356

?> 提示:除了 sha1 哈希外, md5 哈希、sha256 哈希,这些哈希算法我们都可用python的hashlib库进行模拟。

创宇超防

下图就是访问一个使用了加速乐的网站抓包后的数据包:

第一层:返回一个需要带上的Cookie的js内容。

20220306002908

第二层:带上第一层的Cookie访问,返回一个ob混淆后的js内容 。

20220306003117

第三层:带上第二层通过js内容设置的Cookie后返回网页内容。

20220306003257