AI辅助JS逆向:猿人学第18题动态Cookie破解

📅 发布时间:2026/9/13 14:20:39
AI辅助JS逆向:猿人学第18题动态Cookie破解
说实话第一次刷到猿人学第18题的时候我压根没想着能在半天内把它啃下来。题名看着就俩关键字AI、逆向。但等你真正把页面打开看到那串每次刷新都会变、还带着一堆混淆逻辑的__jsl_clearance_sCookie 时才会意识到这道题真正考的不是你会不会看 JS而是你能不能在一个“被虚拟化保护”的代码迷宫里用最短路径找到出口。这篇文章我打算完全按我实际操作的过程来写不绕弯子不整虚的。我会从最开始的抓包、定位入口讲起到怎么把混淆代码喂给 AI 辅助分析再到最后补环境、跑通数据接口。整个过程里踩了不少坑尤其是 AI 因为幻觉给我编了几个不存在的函数名差点把我带沟里去。这些我都会在文末整理成速查表给大家省点时间。1. 这题到底在考什么先说清楚目标1.1 题目链路和核心机关猿人学平台上的题目分类很明确Web 方向的题目主要练的是“动态身份验证”和“反爬对抗思路”。第18题从页面形态上看并不复杂你需要访问某个分页数据接口比如api/match/18/getData?page1拿到 JSON 数据就算过关。但问题在于你直接在浏览器里访问这个接口返回的并不是数据而是一段 HTML里面塞着一段被高度混淆的 JS这段 JS 会重新生成一个新的 Cookie然后你需要带着这个 Cookie 再去请求才能真正拿到数据。顺带一提如果你用删 Cookie 的“裸请求”去撞服务器会直接给你一个 412 状态码。这就是题目里最核心的关卡动态 Cookie 校验。我在初判阶段就确定了几件必须做的事找到生成__jsl_clearance_s的那段 JS 文件或内联脚本。理解它的生成逻辑包括种子值、时间戳、编码方式、循环次数。找到一种稳定的方式复现这个 Cookie可以是纯 Python 算法还原也可以是补环境执行。但难点在于18题里的 JS 并不是普通的 ob 混淆而是上了一个“虚拟机”级别的保护。代码里有一堆固定的操作码opcode、一个解释器循环、以及真正逻辑被打散的字节码数组。手动一行行看下去基本等于在迷宫里找一根线头非常痛苦。所以我当时决定换条路先用 AI 做初步的“语义翻译”再结合补环境的方式动态执行跳过完全还原。1.2 为什么选 AI 辅助而不是纯手撕早几年做逆向遇到混淆代码基本只能靠硬看加console.log插桩再不行就动态调试断点。现在不一样了像 GPT-4 这类模型对代码语义的理解已经很强尤其是处理“一段代码到底干了什么”这种粗粒度任务时效率远超人工。不过我要先说清楚一个观点AI 不是用来替你逆向的它是用来帮你把“问题域”缩小的。比如面对 1000 行混淆代码你直接问 AI“请逐行解释”它大概率会给你一堆正确的废话反而浪费时间。但如果你问“这段代码的输入是什么、输出是什么、主流程能不能概括”它能很快给出一个可验证的方向。我做了一个比较朴素的分工我负责抓包、定位关键 JS、控制补环境时的浏览器指纹、验证结果。AI 负责解释混淆代码的行为、给出可能的主流程伪代码、在报错时提醒我漏掉了哪个全局对象。这个分工非常有用。特别是在第18题里核心代码被虚拟化之后纯逻辑看不懂但行为却能通过“输入输出”的黑盒方式观察出来。AI 在这时候相当于一个经验丰富的翻译官它不直接给你钥匙但能告诉你钥匙可能藏在哪几个抽屉里。2. 抓包与定位所有逆向的第一步都是网络面板2.1 从 Preview 到 Request Headers锁定动态 Cookie不管什么题目逆向的第一步永远是打开 DevTools 的 Network 面板老老实实看请求。我当时的操作路径是这样打开开发者工具切到Network选项卡勾选Preserve log。在页面上正常翻页找到数据接口的请求。点开这个请求先看Headers最下面找到Request Headers里的Cookie字段。然后清掉__jsl_clearance_s刷新页面直接改请求再发一次。重点就在第4步。删掉 Cookie 后再请求数据接口返回内容会变成一段带 script 标签的 HTML。这就是整个过程的关键入口。我当时看到响应内容里有一个动态生成的脚本片段脚本指向了一个 JS 文件文件路径还带了一段类似时间戳的随机参数。也就是说服务器在你没有合法 Cookie 的时候会把这个“生成 Cookie 的 JS”当作挑战下发给你。这个套路非常常见在业界通常被称为“JS 挑战”核心思路就是服务器不是不让你访问而是先丢给你一个需要执行特定 JS 才能解出的 Cookie只有带着 Cookie 回来的人才被认作“真实浏览器”。我在这一步其实没花太多时间真正的小坑在于脚本加载路径不是固定的每次刷新都会变如果你以为它是一个静态 JS 然后直接保存那必然失败。正确做法是先把当前请求返回的完整 HTML 保存下来再从里面提取脚本地址保持“一次性使用”的思路。2.2 用 AI 辅助快速梳理脚本加载链当我拿到这个动态 JS 文件后第一感觉就是代码很“碎”变量名全是_0xabc这种而且里边还套了数组位移函数最外层是一个大 IIFE。说实话这种情况下直接硬看代码容易看吐。我的策略是先把 JS 文件保存成独立文件然后分段喂给 AI但注意不是全量喂而是有选择性地喂。AI 上下文窗口虽然大但现在挤进去太多无效代码它容易抓不住重点。我先做了这几步预处理用工具格式化混淆代码网上现成的 JS Beautifier 就行。先找最外层的 IIFE看它调用了哪些函数。把“看起来像入口”的那几个函数截出来发给 AI。我这里发的提示词大概长这样下面是一段前端 JS 混淆代码我需要了解它的核心功能。请忽略反调试和格式化函数只告诉我1这段代码最终会在浏览器环境里设置哪个 Cookie2Cookie 的 value 是怎么生成的3有没有使用固定的盐值或时间戳4有没有使用标准加密库。AI 给的答案很直接它指出代码里存在一个document.cookie赋值语句value 的核心由一个函数生成这个函数接受时间戳和固定字符串作为参数返回结果会拼接成xxx|xxx|xxx的格式。虽然它没直接告诉我每一步的算法细节但我已经知道下一步该去断点验证哪些位置了。所以我的经验是给 AI 的任务优先级永远是“概括行为”大于“逐行翻译”。逐行翻译你早晚会做但那应该是你自己为了写算法还原才去做的而不是让 AI 在一开始就铺开所有细节。3. 核心逻辑拆解当我在逆向一个“虚拟化”的 JS 时我在看什么3.1 把混淆代码丢给 AI先定主流程拿到 AI 的“主流程预测”后我并没有立刻相信。我不知道你有没有遇到过 AI 一本正经地说“这里是一个 AES 加密密钥是 xxx”结果你继续往下查发现密钥本身也是动态生成的。所以我的做法是用 AI 的结论当线索再去代码里做关键点验证。在代码里找关键词通常不是靠肉眼一行行扫而是靠“结构化搜索”。我一般会用CtrlShiftF全局搜索下面几个特征document.cookienew DategetTimecharCodeAtfromCharCode0x开头的十六进制数组eval或者Function构造函数第18题里这些关键词几乎都能命中。尤其是charCodeAt和fromCharCode同时出现时往往说明核心逻辑在做字符串编码或者数字运算。我把这些关键代码段再丢给 AI让它输出一个“类似行为伪代码”的版本AI 给出的结果类似这样seed 固定字符串 当前时间戳 循环1对 seed 做某种变换生成临时数组 循环2按 opcode 指令表逐条解释执行更新寄存器和内存 最终值 寄存器中某个位置的结果 cookie seed . 最终值我没有把 AI 的这版伪代码当终极答案但它清楚地告诉我这个 VM 的核心在于解释器循环而我根本不需要完全看懂每条 opcode 的含义只需要让这段 JS 在 Node 环境里跑起来让它自己把最终值算出来。这部分思路基本就是“补环境”的雏形。3.2 字节码、opcode 与“VM”的还原很多初学者听到“JSVMP”这个词就头皮发麻觉得一定是超级难的算法。实际上它的本质很简单把原本可以直接执行的逻辑转换成一张指令表和一个执行引擎。指令表里的每个数字代表一种操作比如加、减、跳转、取属性执行引擎就是个大的 switch-case读到什么 opcode 就执行什么操作。第18题里的虚拟化代码本质上就是这样。它把真正要执行的代码先“编译”成一系列数字数组然后由一个解释器逐条读取。这意味着你看到的代码根本不是最终执行逻辑只是一台“虚拟机”的骨架。那怎么破行业里其实有几种共识性的思路硬还原 opcode即把指令表里的每个数字对应的操作都梳理出来最后用 Python 或 JavaScript 重写整个 VM。工作量非常大适合拿来做研究不适合快速刷题。Hook 执行环境在浏览器里给document.cookie的 setter 挂上钩子或者直接在本地用 jsdom 模拟环境执行然后等它算出结果。补环境直跑把混淆 JS 原封不动放到 Node.js 里遇到报错就逐个补齐缺失的浏览器对象和函数让代码认为自己在浏览器里正常执行。我选的是第3种。理由也很简单这道题的核心目标是拿到合法的 cookie 值而不是成为 VMP 逆向专家。如果直接执行比完整还原成本低一个数量级就应该选择更经济的路线。3.3 从算法还原到“行为对齐”的选择我承认在刷题的时候如果遇到算法不复杂、但是代码做了多层混淆的情况直接还原算法是更优雅的方案。但在第18题里VM 的指令数组是动态生成的而且会跟随时间戳和会话种子变化。你要是想把 opcode 完全静态还原等于给自己升级了一下难度。所以我在决策时做了一个取舍保留 JS 原始执行能力用补环境的方式让 VM 自己计算出 cookie。在 Python 侧只做“调度”——先请求拿 JS再用 JS 引擎执行最后带着生成的 cookie 请求真实数据接口。对 AI 的使用重点放在“帮助我认识缺失的浏览器环境对象”而不是“帮我翻译所有 opcode”。事实证明这条路走得通。最终我甚至没有写一行算法还原代码却拿到了全部页面数据。这不丢人逆向的本质就是用最小成本拿到目标结果能跑通比“看起来很硬核”重要得多。4. 动手实现从 JS 到 Python 调用链4.1 环境准备与工具选型整个项目我用的主力语言是 PythonJS 执行引擎走的是 Node.js 方案。原因很简单Node.js 对 ES6 语法支持完整而且第18题里的 JS 只有浏览器环境依赖没有用到 req 之类的 Node 模块所以只要能补上浏览器全局变量跑起来几乎零障碍。Python 这一侧我用了最经典的execjs库来调用 Node。虽然它有一些性能损耗但对于这种低频次生成 Cookie 的场景完全够用。如果你要高频调用建议直接用subprocess起长驻 Node 进程或者用js2py但兼容性会差一些。安装其实就两条命令pip install PyExecJS npm install -g jsdom为什么提前装 jsdom因为第18题的代码里会用到document对象尤其是document.cookie。没有这个对象脚本直接报ReferenceError: document is not defined。虽然我也可以手写一个很简化的 stub但 jsdom 会更接近真实环境能减少很多手指头。另外提醒一句PyExecJS 默认运行时在 Windows 上可能是JScript这会带来一堆莫名其妙的兼容问题。建议在代码开头先显式指定 Node 运行时import execjs import subprocess node_path rC:\Program Files\nodejs\node.exe # 改成你自己的路径 ctx execjs.compile(js_code, cwdrC:\Program Files\nodejs)这个细节不处理的话后续所有 ES6 语法都可能解析失败。4.2 补环境实操把缺失的对象和函数补齐把 JS 第一次丢进 Node 跑的时候我碰到了连续四五个报错基本都属于同一个大类环境缺失。具体报错顺序我记不清了但大体有这几个document is not definednavigator is not definedwindow is not definedlocation is not defined处理方式不复杂直接在 JS 文件的最顶部也就是所有代码执行之前注入一段“环境准备代码”。我这里用的不完全是最简 stub而是带一点真实性的伪对象因为有些混淆代码会读取对象的属性如果属性不存在同样会报错。const jsdom require(jsdom); const { JSDOM } jsdom; const dom new JSDOM(!DOCTYPE htmlhtmlbody/body/html, { url: https://match.yuanrenxue.cn/, referrer: https://match.yuanrenxue.cn/, userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 }); global.window dom.window; global.document dom.window.document; global.navigator dom.window.navigator; global.location dom.window.location;这里有一个坑而且是我实际踩过的直接const document dom.window.document不行因为在 Node 的 CommonJS 模块作用域里const声明的变量不会挂到global上JS 解释器执行到document.cookie时依然找不到它。必须用global.document ...让对象成为全局属性。还有一个细微的点jsdom 默认的userAgent是node.js很多反爬代码会检测这个。所以我创建JSDOM的时候显式把userAgent设置成了 Chrome 的 UA 串避免后端通过 UA 识别出这是个假浏览器。这一步真的挺重要我在初版跑通后发现有些请求返回值异常排查来排查去最后怀疑就是 UA 导致的行为不一致。4.3 跑通第一个请求校验顺序和时间戳环境补齐后我再跑一次脚本document.cookie的位置终于不再报错。但问题又来了生成的 cookie 只用一次过几分钟再试就失效。这说明生成逻辑里带了时间戳服务器在校验时会比对 Cookie 里的时间字段与当前时间的差值。所以完整的请求链路必须是这样顺序不能乱import requests import execjs import time session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://match.yuanrenxue.cn/ }) # 1. 先访问任意一个需要挑战的页面拿到包含生成逻辑的 JS resp session.get(https://match.yuanrenxue.cn/api/match/18/getData?page1) html_text resp.text # 2. 从 HTML 里提取 JS 代码这里的提取逻辑要看你拿到的实际页面 js_code extract_js_from_html(html_text) # 3. 用 Node 执行 JS 生成 cookie node_code const jsdom require(jsdom); const { JSDOM } jsdom; const dom new JSDOM(!DOCTYPE html..., {...}); // ... 补环境代码 // ... 这里把题目 JS 塞进来 ctx execjs.compile(node_code) cookie ctx.eval(generateCookie()) # 4. 带上新 Cookie 请求真实数据接口 session.cookies.set(__jsl_clearance_s, cookie, domainmatch.yuanrenxue.cn) data_resp session.get(https://match.yuanrenxue.cn/api/match/18/getData?page1) print(data_resp.json())有一点必须提醒不同时刻获取到的主 JS 内容可能因为挑战参数不同而略有变化。稳妥的做法是“每次需要 Cookie 时都现做一次完整的 请求-提取-执行 流程”而不是把 JS 缓存下来反复用。我第一次就是把 JS 当静态文件缓存了结果过了十几分钟再跑服务器直接给我返回一个全新的挑战脚本旧的不管怎么执行都算不出合法 Cookie白白排查了半天。5. 踩坑记录与排查技巧实录5.1 AI 给错代码时的“抠底”方法我前面提到AI 在分析混淆代码时给过我错误线索这里展开说一下因为这是 AI 辅助编程绕不开的坎。有一次我问 AI“这段代码的入口函数是什么”AI 回复了一个函数名比如getClearance()。我去代码里全局搜索根本找不到这个函数。原因很简单代码里的函数是动态注册到window对象上的函数名在每次请求时都可能变化AI 是根据它见过的训练数据推测出来的并不是代码里真实存在。这种时候不要慌也不要继续跟 AI 纠缠“你错了”。正确的做法是换个角度提问比如请搜索代码片段中出现的document.cookie赋值位置并告诉我这个赋值语句位于哪个函数的作用域内以及这个函数何时被执行。把 AI 的注意力从“函数名”转移到“位置关系”上它能更快给出可用信息。如果还是不对那就老老实实自己在浏览器里下断点看调用栈这一步在什么时候都不能省。5.2 时间戳不一致导致的 412我在第一次跑通流程后连续踩了几次 412。后来发现原因并不在 Cookie 格式而是时间偏差。Cookie 的值里包含了一段Date.now()生成的时间戳而服务器会校验这个时间戳跟它本地接收请求的时间差。如果差太多直接拒绝。我本地电脑的系统时间做了同步矫正按理说不会差很多但由于脚本执行有时间开销从生成 Cookie 到带着 Cookie 发请求之间隔着几秒甚至更久一旦超过了服务器的允许范围就会失败。解决办法是把“生成 Cookie”和“请求数据”的间隔尽量压缩最好在同一个函数里完成cookie ctx.eval(generateCookie()) # 生成后立即使用 session.cookies.set(__jsl_clearance_s, cookie, domainmatch.yuanrenxue.cn) resp session.get(target_url)不需要每次调用时都重新请求挑战页但生成完 Cookie 后不能有太多网络延迟和调试耗时。如果你是手动在断点里看结果手里复制粘贴之后再去请求大概率已经过期了。5.3 常见报错速查表我把实际过程中遇到的报错整理成了一张表方便你排查时直接对照报错信息可能原因解决思路document is not defined没有注入浏览器环境对象用 jsdom 初始化并挂到全局navigator is not defined只注入了 document 没注入 navigator把window的属性能挂的尽量都挂上Cannot read properties of undefined (reading xxx)JS 读取了某个缺失的属性查看调用栈给对应对象补 stub 属性ReferenceError: window is not definedNode 全局中没有 windowglobal.window dom.window生成了 Cookie 但请求返回 412时间戳偏差或 Cookie 过期压缩生成-请求间隔统一使用服务器时间直接用 JS 文件缓存执行失败挑战脚本每次动态变化每次请求现拉取再执行不要长期缓存execjs 报语法错误 ES6跑了默认 JScript 运行时显式指定 node.exe 路径这张表看起来简单但背后都是实打实的教训。尤其是最后一条很多人在 execjs 上栽跟头就是没注意运行时切换导致明明代码没问题却一直报语法错误。6. 如果用 AI 辅助逆向我的工作流是什么6.1 一个“题-人-AI”的分工模型做完第18题之后我把自己这套“AI 辅助逆向”的打法总结成了几个固定步骤出题阶段先抓包确定 Cookie 名、校验方式、JS 入口这一步绝对不交给 AI因为 AI 没有网络请求上下文。解题阶段把定位到的混淆代码丢给 AI让它输出行为层面的伪代码帮我快速理解“输入-输出”的关系。验证阶段把 AI 给的提示作为线索自己在代码里搜索关键词验证不能盲信。执行阶段优先用补环境直接跑 JS遇到报错时把报错信息发给 AI让它猜可能缺了哪些浏览器对象。固化阶段跑通后把经验整理成可用脚本和速查表方便以后复用。这个模型的好处是AI 被限制在“辅助分析”的边界内不会出现让它自由发挥、结果带着我们南辕北辙的情况。而且即使 AI 给的信息是碎片化的组合起来也能让我们少走很多弯路。6.2 关于 AI 辅助我最想提醒的一个点我自己用过 AI 写各种各样的代码但在逆向这个场景里它的作用是“放大你的判断力”而不是“替代你的判断力”。比如 AI 能快速告诉你这段代码可能用了 MD5、AES、Base64但它不能替你决定该用“动态执行”还是“算法还原”也无法替你做反调试绕过。最后做决策的还是得靠人对整个请求链路的理解。所以我特别建议刚开始接触 JS 逆向的人不要一上来就想着“AI 能帮我搞定一切”。你应该自己做一遍定位、抓包、断点调试、补环境把基本功打得差不多之后再引入 AI 来提速。否则当 AI 给出一个错误判断时你连“它在胡说”的感觉都没有那就只能被动浪费时间了。第18题给我最大的收获不是“我会算这个 Cookie 了”而是让我找到了一种在复杂混淆环境下快速逼近真相的方法抓网络请求、看行为输入输出、让 AI 缩小范围、用环境直跑绕过算法细节。这套流程学完之后我再去抓别的站的动态 Cookie明显比以前快很多。最后再分享一个小技巧如果你卡在某个断点调不出来别只顾着看代码逻辑先试试点一下页面上的翻页按钮看 Network 面板里有没有新的脚本被加载进来。很多挑战脚本是异步加载的你不操作页面脚本就不会出现自然也就找不到入口。这个习惯真的能帮你省掉很多白费功夫的时间。