ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JS逆向通杀模版:破解创宇盾(加速乐)动态JS防护全流程

JS逆向通杀模版:破解创宇盾(加速乐)动态JS防护全流程 做JS逆向的兄弟应该都有印象第一次碰到创宇盾加速乐防护的站点时是什么感觉请求发过去返回的不是正常数据而是状态码521或一段HTML里面夹着巨长的混淆JS文件名还带时间戳。那天晚上我第一次意识到原来在真实数据和加密参数之间还隔着一道“用浏览器换Cookie”的墙。今天这个案例与其说是在破解某一个站点不如说是在整理一套“通杀模版”把创宇盾加速乐这类JS动态防护的通用破解思路抽出来做成一份可以反复套用的逆向框架。它解决的核心问题是不同目标站点虽然JS混淆方式和Cookie命名不同但防护逻辑高度同源只要把共性逻辑吃透就可以把单站点的逆向时间从一两天压缩到一两个小时。这篇文章适合两类人看一类是刚接触JS逆向、想搞懂前端防护对抗流程的初学者另一类是经常接数据采集需求、希望把手动逆向沉淀成通用工具的工程型选手。不管你是哪种这套思路都会帮你少走不少弯路。1. 项目背景与核心思路1.1 创宇盾加速乐的前端防护到底做了什么先搞清楚对手是谁。创宇盾和加速乐早期其实是两个独立品牌后来合并成同一套云防御体系前端部分的核心机制可以简单理解为“动态JS挑战”服务端不直接拒绝你的请求而是返回一段经过复杂混淆的JavaScript这段JS会在浏览器里执行执行完毕会生成一个带加密参数的Cookie。浏览器下一次请求带上这个Cookie服务端校验通过才放行真实数据。我是怎么判断一个网站走了这套防护的这里有几个非常明显的特征第一次请求返回的状态码是521并且响应体是一段HTML而非目标数据接口。HTML里通常会有一个script标签引入的JS文件名包含时间戳、随机数或MD5片段比如WAF_xxx.js?t1699999999999这种格式肉眼就能看出来它是动态生成的。响应头的Set-Cookie会先种一个临时Cookie比如__jsluid_s之类的初始标识真正的“通过凭证”要等JS运行后才产生。如果你用Node直接运行那段JS几乎一定会报错因为代码里大量使用了window、document、navigator等浏览器环境变量。这类防护的设计逻辑是普通用户用浏览器访问一切自然发生几乎感知不到额外步骤而自动化脚本拿不到浏览器环境就卡在了“如何生成合法Cookie”这一关。所以它本质上不是限制人而是限制“没有浏览器的程序”。理解了这一层也就理解了逆向它的核心切入点你不需要破解它的加密算法本身你需要做的是在服务端脚本里“模拟一个浏览器环境去执行它的JS”。1.2 为什么“通杀模版”能够成立很多人第一次逆向这类防护时习惯去找“当前站点专用的加密逻辑”把JS打出来一点点读试图逆出它的加密过程然后用Python重写一遍。这种做法最大的问题在于创宇盾的JS更新非常勤改一个变量名、换一次混淆方式你花一天重写的算法就失效了。通杀模版的思路完全相反我不去管你JS里具体用了AES还是RSA也不在乎你的混淆代码怎么换我就盯住一个固定流程——拿JS、执行JS、拿Cookie、重放请求。无论目标站点的加密算法怎么变只要它还在用“动态JS挑战”这套防护体系执行JS这一步就绕不开。所以通杀模版的核心资产不是某一段加密逻辑而是一个稳定的“虚拟浏览器环境”和一套可靠的调度流程。这个思路和做自动化测试很像你不需要理解页面里每个按钮的业务逻辑只需要模拟用户的操作路径。防护JS就像是用户登录时走的验证流程你只需要让它在合适的环境里跑完然后把结果带出来即可。1.3 适用场景与合规边界先说技术适用场景这套模版最擅长处理的是“纯静态JS挑战”类型的目标即首次请求返回JS、通过执行JS换取Cookie、后续请求带Cookie直连。它对数据接口是JSON还是HTML没有要求也不关心目标站点后端用什么语言因为所有对抗都发生在前端。再说合规边界这个更重要。JS逆向本身是一把双刃剑我在这里必须把话说清楚本案例拆解学习的是前端防护机制的通用对抗原理请不要把文中的思路用于未授权爬取他人网站数据。建议在实际操作前先确认以下几点目标站点是否有公开的API接口优先使用官方接口。如果要做数据采集最好先阅读网站的robots协议和用户协议并评估对服务器造成的压力。仅将本文方法用于CTF靶场、自己搭建的测试站点、或已经获得授权的安全测试项目。我自己平时做这类研究的习惯是本地搭建一个模拟环境或者找已经明确开放的测试靶场练手。这样既能完整走一遍逆向流程又不会给自己惹麻烦。技术没有立场但使用技术的人要主动给自己划好红线。2. 核心细节解析与逆向准备2.1 识别目标站点防护类型的几个硬指标在动手之前先确认目标站点用的是什么防护体系。这一步做错了后面全白费。我整理了一个快速识别表你可以直接拿去做参考特征创宇盾加速乐其他JS防护常见第三方厂商首次请求状态码多返回521多返回403、405或200验证动态JS命名含时间戳与随机数多为固定文件名或带版本号Cookie命名特征常见__jsl_clearance_s等带特征的前缀常见拼音缩写或业务相关名是否需要二次跳转通常一次JS挑战即可部分需要多次挑战或滑块互动混淆风格大段数组位移字符串拼接执行流复杂各有特色有些偏重OB混淆当然这张表只是经验总结不代表所有站点都严格符合。我在实际测试中遇到过某些站点第一次请求返回200但返回体是HTML“正在加载”页JS执行后才能拿到真实接口这种情况也需要留意。识别方法其实很简单打开浏览器的开发者工具切到Network面板清空记录后刷新页面看第一眼的请求列表和状态码。如果第一个请求是521并且Response里是一段带了巨型JS的HTML那基本可以断定是同类防护。把这一步做成习惯可以帮你少做很多无用功。2.2 工具链准备与虚拟执行环境选型逆向这类防护需要的工具并不花哨都是老面孔但选型和组合方式有讲究。我常用的组合是Chrome DevTools负责抓请求、看响应、打断点。用它确认JS加载流程和关键参数。Node.js vm模块核心执行环境。后续会细说为什么用vm而不是直接开浏览器。Python Requests负责发送请求、携带Cookie、处理重试逻辑。Fiddler/Charles可选如果需要抓HTTPS流量或做更细的请求分析可以配上。在“执行JS”这一步我见过很多朋友的第一反应是装上selenium或playwright直接用真实浏览器跑。这个方案不是不行但存在几个实际问题一是并发能力差浏览器实例吃内存二是运行速度慢三是目标站如果检测webdriver特征反而更容易被拦截。所以我的推荐方案是用Node.js的vm模块搭建一个补环境框架。vm.createContext可以创建独立的执行上下文我们在context里预先注入window、document、navigator等属性然后把目标JS丢进去执行。这样做的好处是不依赖真实浏览器可以并发运行多个实例速度接近原生执行。补环境的本质就是一个“轻量级浏览器壳”只在JS运行时所依赖的最小环境上做填充。2.3 算法定位不要急着读代码先看流程面对几百KB的混淆JS人类逐行阅读的效率约等于零。我拿到JS后的第一件事永远是先在浏览器里看完整流程而不是去读代码。具体操作路径是这样的清空Network记录刷新页面找到初次请求的响应HTML。把HTML里的script标签地址复制出来看它加载了几份JS。在Script Sources面板里找到这份JS美化格式后搜索关键词cookie、document.cookie、location、href、charAt、substring这些词汇能快速把我们引向设置Cookie的代码位置。在该位置打断点重新加载页面观察函数调用栈确认传入了什么参数、返回了什么结果。这个流程的核心逻辑是先确定“结果在哪里被赋值”再顺着调用链往前找“参数从哪里来”。就像你在运动场找终点线知道了终点位置反推起点和路线就轻松很多。这里还要说一个容易被忽略的点目标JS里往往存在大量条件分支和定时器。有些防护JS在执行过程中会检查开发者工具是否打开、检查浏览器窗口尺寸、检查document.hidden状态一旦发现异常就会走偏逻辑生成一个无效Cookie。所以你看到的每次挑战可能不止一套算法路径而是好几条路径交叉在一起。通杀模版的处理方式很朴素保证补过的环境足够“像浏览器”让JS的所有检测分支都认为自己在浏览器里运行从而走回正常路径。3. 实操过程与通杀模版实现3.1 先搭一个可用的补环境执行框架这一节我会给出一套可直接运行的完整框架它是我平时做同类防护分析时的“地基”。先说思路再看代码。Node的vm模块允许我们创建一个沙箱环境然后在里面执行任意JavaScript脚本。但目标JS本身不能直接运行因为它依赖window和document这些全局对象。所以我们给沙箱塞入一个“假的”浏览器环境。为了让这套环境尽量真实至少需要补齐window、self、top、parent互相引用。document.cookie读写逻辑。navigator.userAgent等指纹信息要和实际请求头保持一致。location.href用于JS里可能的跳转判断。必要的setTimeout、setInterval防止脚本报错。下面是核心执行代码const fs require(fs); const vm require(vm); // 读取目标防护JS这里我们用一个简单的演示脚本 const targetJs fs.readFileSync(./challenge.js, utf-8); // 构建补环境沙箱 const sandbox { navigator: { userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 }, location: { href: https://demo.example.com/test }, document: { cookie: , getElementById: function () { return null; }, createElement: function () { return {}; }, documentElement: { scrollTop: 0 } }, setTimeout: function (fn, ms) { return 0; }, setInterval: function (fn, ms) { return 0; } }; // 让window、self等互相指向沙箱本身 sandbox.window sandbox; sandbox.self sandbox; sandbox.top sandbox; sandbox.parent sandbox; sandbox.globalThis sandbox; vm.createContext(sandbox); // 执行JS统一设置超时防止死循环 try { vm.runInContext(targetJs, sandbox, { timeout: 5000 }); } catch (e) { console.error(JS执行报错:, e.message); } console.log(最终Cookie:, sandbox.document.cookie);这段代码的价值在于让你先跑通流程。真实目标JS跑起来后往往需要往里加更多属性但只要基本框架在后面的工作都是增量补环境不会推倒重来。3.2 理解加密过程一个简化版的Cookie生成模型真实创宇盾的JS混淆度很高但核心模型是可以抽象出来的。这里我写一个教学简化版方便解释原理实际逆向时你在JS里找到的逻辑通常也是这个套路// 演示脚本 challenge.js function generateToken() { const ts new Date().getTime(); const raw demo ts test; // 模拟一类哈希运算真实场景中可能是AES或更复杂的运算 let hash 0; for (let i 0; i raw.length; i) { hash ((hash 5) - hash) raw.charCodeAt(i); hash | 0; } return hash _ ts; } document.cookie jsl_clearance_s generateToken() ; path/;这段演示代码对应的真实场景是Cookie的值 时间戳 固定特征串通过某种不可逆计算生成。目标站点校验Cookie是否有效靠的是服务端进行同样的时间窗口和特征校验。这就是为什么直接硬编码Cookie不可行——时间戳过期就失效特征串和服务端配置强相关。很多刚入门的同学会纠结于“必须完全读懂加密算法”这是不必要的。只要我们用同一个执行环境把JS跑起来让JS自己生成Cookie就等于把加密过程完整复现了一遍。这就像你不会做菜但你把大厨请回家里让他用你家厨房做出菜来你不需要知道配方是什么。3.3 将执行产物接入Python请求流程JS侧跑通了下一步就是把它接进Python采集链路。常见的做法有两种我推荐简单直接的那种。第一种是直接用subprocess调用Node脚本把JS文件名和基础参数作为命令行参数传给NodeNode执行完输出CookiePython再带着Cookie发请求。这种方式实现最快适合原型验证。import subprocess import requests # 调用Node执行补环境脚本 proc subprocess.run( [node, run_challenge.js, https://demo.example.com/test], capture_outputTrue, textTrue, timeout10 ) cookie_value proc.stdout.strip() print(生成的Cookie:, cookie_value) # 使用生成的Cookie发起业务请求 session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 }) session.cookies.set(jsl_clearance_s, cookie_value, domaindemo.example.com) resp session.get(https://demo.example.com/api/data) print(resp.status_code, resp.text[:200])第二种是把Node封装成一个常驻服务比如通过HTTP接口暴露Python从服务获取Cookie。这适合并发量大的场景可以复用Node进程避免每次subprocess启动的开销。但如果只是个人研究或小规模采集第一种方案完全够用。实战中还有个细节生成Cookie后第一次发请求可能仍然会遇到521因为目标站有时会检查Cookie和请求的时间戳间隔。如果间隔太短它可能会怀疑是程序生成间隔太长可能又过期了。通常我会在拿到Cookie后做一个微小的延迟比如等待0.3到1秒再发请求这样更符合真实用户的访问节奏。3.4 配置化为不同站点定制Cookie名与执行参数通杀模版的“通杀”二字体现在代码框架不换、参数可配置。因为不同站点即使防护同源Cookie名也可能不同有些叫jsl_clearance_s有些可能换成别的前缀。处理这个问题的最好方式是把站点差异抽成配置。{ demo.example.com: { cookieName: jsl_clearance_s, requestUrl: https://demo.example.com/test, userAgent: Mozilla/5.0 ..., execJsName: challenge.js, delay: 0.5 }, another.example.com: { cookieName: custom_cookie_name, requestUrl: https://another.example.com/waf, userAgent: Mozilla/5.0 ..., execJsName: another_challenge.js, delay: 0.8 } }Python侧启动时读取这个JSON按配置构造并发任务就能实现“一个模版管一批站点”。我在项目里还用了一个小技巧把每次上传的JS内容哈希存到本地当目标站的JS文件名变化时优先对比哈希相同则直接复用上次的执行结果减少重复执行JS的开销。这个优化在目标站JS更新不频繁时效果非常明显。4. 常见问题与排查技巧实录4.1 JS执行报错window is not defined、document is not defined这是补环境初期最常遇到的报错原因说白了就是环境补得不够。很多朋友看到报错就去JS里搜“window”相关的行一个个补结果补了十个还有下一百个非常崩溃。我的建议是第一轮不要追求一次跑通先用一个“万能空壳”把所有对象补齐——给window、document、navigator、location都挂上最常见的属性和方法只要不报错就行。第一轮跑成功后再根据真实浏览器行为逐步精细化。比如sandbox.document.createElement function (tag) { return { style: {}, setAttribute: function () {}, appendChild: function () {}, getContext: function () { return {}; } }; }; sandbox.document.querySelector function () { return { style: {}, addEventListener: function () {} }; };这样做的原理很简单目标JS运行时只关心它调用的方法“存在且不报错”并不关心这些方法是否真的执行了完整的HTML渲染逻辑。4.2 执行成功但没有生成有效Cookie这种情况最头大因为程序没报错说明语法和函数都调用通了但Cookie没值。常见原因有两个第一个是JS走了其他分支。有些防护JS会在检测到环境异常时静默设置一个空Cookie或者根本不设置。比如它检查了navigator.languages、canvas.toDataURL等指纹发现和真实浏览器不一致就提前return了。对症的解决办法是把这些指纹字段补得和Chrome一致。我通常的做法是用真实浏览器打开同页面在Console里手动执行几个环境检查的JS片段把浏览器返回的真实值记录下来再把这些值写入补环境脚本。第二个是JS设置了定时器后才写入Cookie。防护JS可能先启动一个几秒的定时器时间到了才写Cookie。如果我们在setTimeout里返回0等于告诉JS“定时器不存在”它可能延迟逻辑就断了。针对这种情况我会把setTimeout和setInterval替换成在短时间内同步执行sandbox.setTimeout function (fn) { // 对于演示环境直接尝试同步执行 try { fn(); } catch (e) {} return 0; };当然真实场景中直接同步执行可能会引发死循环所以更稳妥的方式是做一个任务队列在设置Cookie的时机主动触发。4.3 请求重放仍然被拦截如果生成Cookie的流程没问题但重放请求还是521问题大概率出在请求一致性上。有次我排查一个站点折腾了一下午最后发现是JS里加密逻辑用到了navigator.userAgent而Python发请求时的User-Agent和补环境时用的不是同一个服务端校验时对不上导致Cookie被判非法。这类问题的排查我建议按两个方向走方向一核对指纹一致性。环境里的UA、头部参数、Cookie的Domain和Path是否和目标浏览器完全一致。方向二查看后续请求的行为特征。正常浏览器在Q第一次请求后可能会有附加的HTTP2请求或资源加载形成完整的请求特征序列纯Python直接发业务请求如果服务端对“生命周期”有校验仍然可能失败。在请求侧可以做的优化是把Cookie塞进请求头时使用和生成Cookie时完全一致的UA并且把Accept、Accept-Language、Referer等头部一并带上尽量模仿真实浏览器的会话形态。为了便于日常排查我习惯每次生成Cookie后先打印一行诊断信息[DEBUG] 文件: challenge.js [DEBUG] 执行耗时: 48ms [DEBUG] 生成Cookie: jsl_clearance_s20240301_xxxx [DEBUG] UserAgent: Chrome/120.0... (一致)这样即使被拦截也能快速确认到底哪一步和预期不符。4.4 常见问题速查表现象可能原因优先排查动作JS执行报xxx is not a function环境缺少对应方法在沙箱中补一个空实现执行无报错但Cookie为空JS走了异常分支或依赖定时器检查指纹字段同步定时器逻辑生成的Cookie用不了UA不一致统一Headers与补环境参数部分请求偶尔失败时间窗口太紧生成Cookie后延迟0.3~1秒再发网站更新后失效JS生成路径变化重新抓取JS比对哈希更新配置这张表解决的是“程序报错”和“业务失败”两类问题。在正式开始核对代码逻辑之前先过一遍这张表通常能省下大把时间。5. 通杀模版的维护与扩展思路5.1 对抗升级防护方也在更新这类防护有一个特点它是动态对抗的。今天你能用的补环境方案明天目标站点可能就在JS里加入了更精细的环境模拟检测比如Canvas指纹、WebGL渲染结果、音频上下文特征等等。这意味着“通杀模版”并不是一劳永逸的。我自己的处理策略是模版分层把执行框架层Node vm 沙箱、环境补全层navigator/document/指纹以及站点逻辑层JS名称、Cookie名称、延迟参数分开维护。框架层基本不动环境补全层根据最新出现的检测点定期扩充站点逻辑层则通过配置管理。这样即使某个站点失效改动也往往只是配置或环境层里的某一行不用推翻整体结构。5.2 思路迁移把模版能力扩展到其他JS防护攻克创宇盾加速乐之后你会发现这套“识别挑战-补环境-执行JS-携带凭据”的方法论对很多同类前端JS防护同样有效。不同厂商的防护可能在细节上有差异但“让浏览器自己生成凭据程序直接复用”这个核心逻辑是相通的。迁移时真正需要调整的地方通常只有三个识别挑战的触发条件和返回状态码补全目标JS所依赖的特殊浏览器API调整Cookie或Token在后续请求中的携带方式。比如有些防护会用两个Cookie分两次挑战第一次种__jsluid第二次设置真正的__jsl_clearance_s。遇到这种带两次挑战的目标只需要把流程改成循环执行JS而不是一次搞定。另外要提一嘴的是这套方法也不限于某一个品牌。只要你能抓到JS、能补环境、能模拟请求它就能扩展成通用的“前端对抗工具箱”。逆向学习到后期拼的不是读诗会某一个算法而是调试思路和工程抽象能力。5.3 性能与稳定性优化经验最后分享一些我在实际工程化过程中遇到的性能问题。当你要并发几十上百个请求时每次都用subprocess调Node显然会拖慢速度CPU开销也大。更优的方案是把Node执行逻辑封装成一个常驻服务启动时加载配置和补环境模板后续请求通过HTTP协议传入生成参数并发处理。我在一个实际项目中就做过这样的封装Node服务启动后占用约60MB内存配合一个简单的循环轮询Cookie生成接口每秒可以稳定生成几十个Cookie。Python端则通过线程池调度整体稳定性比启动独立浏览器高出一个数量级。要注意的内存点是Node vm沙箱虽然轻量但每个沙箱执行完目标JS后最好不要长期持有避免内存泄漏。我的处理方法是每次执行完成后把沙箱对象置空定期重启Node服务。这些经验听上去不起眼但都是实际跑出问题后又调回来的血泪教训。写到这这套通杀模版的核心思路和工程骨架就完整了。我个人在实际操作中最深的体会是JS逆向最大的门槛往往不是加密算法本身而是“环境还原”的耐心和对流程的全局把握。把补环境框架搭好、把流程跑通剩下的事就是不断在和防护方的动态对抗中打磨细节。最后还是要再啰嗦一句技术能力越强越要管住手把研究放在授权范围内放在正经的项目里这套模版才能真正发挥价值。
返回列表