Web JS Reverse Master Flow
角色
这个 skill 是复杂 Web/JS 逆向项目的总控入口。
它不替代专项 skill,而是负责:
- 归一化当前任务阶段
- 选择下一步最合适的专项 skill
- 约束切换顺序,避免“还没坐实请求链就去补环境”这类误操作
- 输出当前阶段、阻塞点、下一份必须更新的工具
默认情况下,复杂网站还原一律从这个 skill 起手,而不是直接并行触发多个总控 skill。
职责边界:
- 这个 skill 始终是宏观总控,不下沉成某个厂商、某个站点或某种壳的案例库。
- 即使出现
Akamai/Kasada/PX/reese84/同盾/a_bogus/腾讯滑块/阿里滑块/JSVMP/227/226/wasm/protobuf/rid/fuid/fs/bx-pp/run_js/storage.estimate/animationend这类强线索,也只是帮助预路由,不改变它“先判阶段、后选专项”的总控角色。 - 具体站点、厂商和技术经验,应交给
$1997-pro-web-reverse-casebook或其他专项 skill 提供。
如果现场线索已经明显命中某类公开案例,应同步加载 $1997-pro-web-reverse-casebook 做案例预路由,但阶段选择仍由本 skill 决定。
MCP 栈
这个 skill 默认采用三 MCP 协同,不把它们视作可选项:
chrome-devtools-mcp负责浏览器接管、页面级调试、Network/DOM/Runtime/CDP 断点与调用栈观察,是最直接的现场取证面。js-reverse负责逆向工作流级别的源码搜索、Hook、断点、代码采集、运行时证据沉淀、WebSocket/Storage/Session 状态分析。jshook负责更重的 JS 逆向分析、反混淆、Stealth、Hook、浏览器自动化、调试器与代码理解增强。
使用规则:
- 先用
chrome-devtools-mcp和js-reverse拿真实样本与现场证据。 - 进入重混淆、恢复语义层或高级 Hook/Stealth 阶段时,再显式引入
jshook。 - 不允许只凭静态阅读跳过浏览器现场证据。
- 不允许在没有浏览器基线样本时直接讨论 Node 迁移。
输入块
从以下输入开始:
URL 或目标页面:
目标请求 / 字段 / cookie / 消息:
触发动作:
当前现象:
已有证据:
目标:
约束:
先补齐输入,再决定阶段。
总原则
- Hook-first, Breakpoint-second, Full-dump-last。
- 先证明真实请求链,再讨论纯算、补环境或重放。
- 先恢复语义层,再迁运行时,不要反过来。
- 运行时差分优先于盲补环境。
- 每次只扩大一个变量:一个 hook 点、一个样本、一个补丁、一个分支判断。
- 任何结论都要附带运行时证据或中间态检查点。
- 案例锚点只能帮助预路由,不能代替证据门与阶段判定。
Anti-Spiral Protocol
这个 skill 的首要目标之一,是防止在难站点里陷入“长时间没有证据增量的深挖漩涡”。
以下任一情况出现时,视为已经开始打转:
- 连续多轮都在同一层做相似操作,但没有新增请求链、写入边界、状态载体或检查点
- 不断追加 Hook / 断点 / patch,却解释不了第一个真实分叉
- 花大量时间 beautify / AST 变换,但仍然没把逻辑绑回真实 sink
- 本地补环境越补越大,却说不清到底是哪一个对象或状态在影响结果
- 只盯最终值,不再比较中间态
强制规则:
- 同一阶段连续两轮没有新增证据,必须切换动作,不允许第三次重复同类操作。
- 同一问题连续六轮仍无收敛,必须停止当前打法,升级到下一层策略。
- 任何升级都必须写明:为什么当前打法失败、下一步准备验证什么、放弃了哪些假设。
允许的升级方向只有这些:
locate卡住:扩大真实触发范围、改抓 initiator、改用断点栈或浏览器侧调用recover卡住:降低恢复深度、退回locate重新坐实边界,或改 black-box reuseruntime卡住:回退到第一个稳定浏览器快照,重建分叉表,再只 patch 一个状态面env-patch卡住:停止继续补对象,改走浏览器辅助执行、远程调用或最小代理方案
禁止:
- 在没有新证据时继续加大 dump 范围
- 在没证明 builder / writer 边界前做全量反编译
- 在没证明首个分叉点前做全量浏览器环境迁移
- 用“这个站很难”代替阶段判断和停止深度判断
案例锚点预路由
以下线索出现时,不要先猜算法;先把它们映射到更可能的阶段:
- 厂商 / 产品锚点:
Akamai、Kasada、PX、PX3、reese84、Incapsula、同盾、BlackBox、a_bogus- 默认怀疑:
Phase 1的请求链 +Phase 3的风控 / 指纹 / 挑战分叉
- 字段 / 协议锚点:
rid、protobuf、x-s3-s4e、desc、bx-pp- 默认怀疑:
Phase 1的 builder / writer 边界,必要时进入Phase 2看wasm/ 封装桥
- 验证码 / 业务锚点:
腾讯滑块、阿里滑块、_rand、fuid、fs- 默认怀疑:位置链与加密链双线并存,通常会同时触发
Phase 1、Phase 3
- 壳层锚点:
JSVMP、227、226、while(true)+switch、dispatcher、basearr- 默认直接怀疑
Phase 2
- 运行时锚点:
run_js、storage.estimate、animationend、postMessage、worker- 默认先做
Phase 3的运行时差分,再决定是否进入Phase 4
如果这些线索同时出现,优先顺序固定为:
证据门 -> 写入边界 -> 壳层恢复(必要时) -> 运行时差分 -> Node 迁移
主流程
Phase 0: 证据门
目标:
- 证明目标请求、字段和触发动作都来自真实样本,而不是猜测。
要做:
- 先按
$mcp-js-reverse-playbook跑最小链路。 - 强制使用
chrome-devtools-mcp或$js-reverse建立浏览器基线样本。 - 创建或刷新
reverse-records/总览.md。 - 如果请求链、状态链、样本链还不完整,立即创建或刷新
reverse-records/请求链路.md。
退出条件:
- 至少有一条真实样本链。
- 目标字段或消息已由真实请求证明。
禁止:
- 目标请求还没坐实就开始补环境。
- 目标字段还没坐实就直接做算法还原。
卡住信号:
- 只能说“怀疑是某个参数 / 某个脚本”,但拿不出真实请求样本
- 触发动作不稳定,重复操作拿不到同一条链
必切动作:
- 回到最小触发动作,重新抓一条干净样本
- 若页面脚本太乱,优先用
chrome-devtools-mcp的 Network / 调用栈现场,而不是先读源码
Phase 1: 写入边界与请求链
主 skill:
$find-crypto-entry$jsr-locate- 搜不到时可加
$js-reverse-trace-hook
MCP 优先级:
chrome-devtools-mcp:Network、DOM 触发点、调用栈与断点现场js-reverse:请求 initiator、Hook、代码采集、storage/session/websocketjshook:当普通搜索/断点不足以稳定定位时,补充更重的 Hook 与逆向分析
目标:
- 找到真实 sink、
entry -> builder -> writer关系和上游依赖链。
适用:
- 参数名明确。
- 请求链还没完全闭合。
- 怀疑有
Set-Cookie、挑战、session、response-driven 依赖。
退出条件:
- 最终写入边界已坐实。
- 当前阻塞已经从“找链”变成“代码读不懂/壳太重/运行时分叉”。
切换到 Phase 2 的信号:
while(true) + switch- 大量花指令、真假分支
JSVMP227/226/basearr/opcode/ip/gworker/wasm/webpack runtime_0x字符串表、动态eval/FunctionLL()[XX(XX)]()、多套字符串解密器、动态回填字符串Akamai/Kasada/PX类动态字符串与桥接壳bx-pp、rid、protobuf builder的真实 builder 被 dispatcher 遮住- 只能看到 dispatcher,看不到真实 builder
卡住信号:
- 能看到目标值,但无法说明是谁最后写入
- Hook 命中很多函数,但没有一个能稳定回到真实请求
- 反复在同一 bundle 搜关键词,没有把字段绑到请求边界
必切动作:
- 改抓请求 initiator 与调用栈,不继续盲搜源码
- 把问题收缩到
entry -> builder -> writer三段,不再讨论全链路 - 若 builder 被壳遮蔽,立即切
Phase 2
Phase 2: 壳层恢复与重混淆压缩
主 skill:
$jsr-recover
补充 skill:
$js-controlflow-truth-sampling-prune$js-ast-binding-alias-deobf
MCP 优先级:
js-reverse:collect_code、trace、断点、Hook、源码级证据jshook:反混淆、Stealth、复杂 Hook、调试器控制、脚本级语义压缩chrome-devtools-mcp:保留浏览器现场,验证 dispatcher/bridge 在真实页面中的执行路径
目标:
- 把壳压到“足以继续 locate / runtime / replay”的程度,而不是一次性全还原。
要做:
- 识别 container、dispatcher、state carrier、bridge、core operator、write-back layer。
- 对平坦流或 dispatcher 做真实样本裁剪。
- 只恢复当前任务需要的最小 slice。
- 对
JSVMP/227/226至少补齐:入口、状态变量、关键opcode家族、桥接合同。 - 对
wasm至少补齐:下载点、实例化方式、imports、exports、JS 到 wasm 的调用桥。 - 对
Akamai/Kasada/PX类重混淆,先冻结样本,再提字符串解密映射与最小执行片段。
退出条件:
- 下游工作不需要重新扒开同一层壳。
- 已能回到
$jsr-locate坐实 sink,或进入$jsr-runtime处理运行时分叉。
铁律:
- 先 recover,再 runtime。
- 不要在 dispatcher 和 state carrier 还没搞清时就盲补
window/document/navigator。
卡住信号:
- 代码越来越可读,但仍然解释不了目标字段
- 一直在补 case / opcode,却没有把它们绑到目标写回路径
- 反混淆产物越来越大,但没有新增可验证检查点
必切动作:
- 降低恢复深度,先只保留关键
opcode家族或 bridge - 若 sink 关联性变弱,退回
Phase 1 - 若桥接合同已经清楚但结果仍不一致,切
Phase 3
停止纪律:
- 默认只允许做到“足以继续”的深度,不允许因为代码脏就自动升级到最小解释器
worker/wasm/JSVMP/227/226复合壳场景里,若 black-box reuse 已能支撑下游,不再继续全量 lifting
Phase 3: 浏览器与本地差分诊断
主 skill:
$jsr-runtime
补充 skill:
$js-runtime-diff-env-patching- 有反调试时加
$js-reverse-env-antidebug
MCP 优先级:
chrome-devtools-mcp:建立浏览器正常路径与运行时现场js-reverse:抓 storage/session/network/websocket/hook 中间态jshook:反调试、Stealth、环境对抗、运行时行为补充验证
目标:
- 解释为什么浏览器能跑、本地跑不通,并输出最小运行时依赖清单。
优先分类:
- 缺对象
- 缺状态
- 反调试
- 指纹/风控分支
- 时间/随机数/seed 漂移
sign lane与token lane混杂- 浏览器特性通道:
storage.estimate、animationend、postMessage、worker - 自动化摩擦:
run_js、格式化检测、DevTools 开启即漂
退出条件:
- 已找到第一个真实失配检查点。
- 已说清哪些依赖必须迁、哪些依赖不能迁。
铁律:
- 先比中间态,再比最终值。
- 先 patch state,再 patch object。
- 先证明浏览器特性通道被真实消费,再 patch 浏览器对象表面。
卡住信号:
- 一直在补对象,但首个分叉点仍不清楚
- 最终值持续不一致,但没有中间态对照表
- 每次改动都影响很多面,无法知道是哪一项生效
必切动作:
- 回到浏览器正常态,重建“首个分叉点对比表”
- 每次只 patch 一个状态面:
cookie/storage/channel/style/time/random - 如果六轮内还不能解释分叉,停止补环境,改走浏览器辅助执行或代理复用
Phase 4: Node 最小迁移
主 skill:
$env-patch
MCP 优先级:
chrome-devtools-mcp:冻结浏览器基线样本js-reverse:提取可迁移入口、中间态与依赖jshook:在需要更重的 Hook/Stealth/分析时辅助本地迁移判断
目标:
- 把浏览器真实需要的依赖迁到 Node,只迁最小集合。
进入条件:
- 入口已知。
- dispatcher / builder / bridge 基本清楚。
- 浏览器与本地差分已经归因。
退出条件:
- 本地能稳定走到目标 builder。
- 中间态可与浏览器对齐。
铁律:
env-patch是迁运行时,不是替代recover。- 不要一次性搬完整浏览器环境。
卡住信号:
- 本地补丁清单越来越长,但中间态没有更接近浏览器
- 只能靠堆对象让代码不报错,却仍进不了真实 builder
- 每次修一个对象都会引出更多对象,但没有证据证明它们被消费
必切动作:
- 停止补对象,回到
Phase 3只看状态与分叉 - 若入口已知但本地仍跑不稳,改用浏览器远程调用 / browser-assisted replay
- 若 Node 迁移成本明显高于收益,保留 browser-side black-box reuse,不强行本地化
Phase 5: 稳定复现与回归
主 skill:
$js-reverse-sign-replay
MCP 优先级:
chrome-devtools-mcp:对照真实浏览器结果js-reverse:验证中间态与状态依赖是否闭合jshook:对复杂混淆或高级算法段做补充分析
目标:
- 把链路固化成可维护、可回归的本地复现脚本。
要做:
- 切分
normalize -> concat -> hash/encrypt -> encode - 固化输入契约与阶段检查点
- 提供 baseline 与 regression
退出条件:
- 固定输入稳定一致
- 变体输入行为可解释
- 后续版本漂移时能快速定位断点
卡住信号:
- 最终值能对上,但无法解释哪些中间态必须一致
- 输入略变就漂,但没有记录哪个阶段先漂
必切动作:
- 把回归拆回阶段检查点,不再只看最终值
- 若漂移来自运行时依赖,退回
Phase 3/4
复杂场景分支
分支 A:普通参数站
特征:
- 参数名明确
- 没有明显 VM 或 dispatcher 壳
- 代码仍可直接追
流程:
- Phase 0
- Phase 1
- Phase 5
分支 B:重混淆 / 花指令 / 平坦流 / JSVMP
特征:
- 大
switch - dispatcher
JSVMP227/226basearr/opcode/ip/g- 大量真假分支
worker/wasm/runtime壳- 厂商壳:
Akamai/Kasada/PX/reese84
流程:
- Phase 0
- Phase 1
- Phase 2
- 回到 Phase 1 坐实 sink
- Phase 3
- Phase 4
- Phase 5
分支 D:验证码 / 浏览器特性通道题
特征:
腾讯滑块、阿里滑块_rand、fuid、fsanimationend、CSS 动画、样式终态取值run_js、自动化检测、浏览器与本地行为差异
流程:
- Phase 0
- Phase 1
- Phase 3
- 必要时回 Phase 2 恢复桥接层
- Phase 4
- Phase 5
分支 E:风控指纹 / 厂商挑战题
特征:
Akamai、Kasada、PX、reese84、Incapsula同盾、BlackBox、a_bogusabck、sensor_data、bm_sz、TLS/JA4- 浏览器可过,本地
403或 challenge 持续跳
流程:
- Phase 0
- Phase 1
- Phase 3
- 必要时 Phase 2
- Phase 4
- Phase 5
分支 F:协议封装 / Wasm / Builder 题
特征:
protobuf、jspbrid、x-s3-s4e、desc、bx-ppwasm、compileStreaming、imports/exports- builder / writer 被 VM 或 wasm 桥遮蔽
流程:
- Phase 0
- Phase 1
- 必要时 Phase 2
- Phase 3
- Phase 4
- Phase 5
分支 G:Hard Site 复合场景
特征:
- 同时出现
JSVMP/平坦流/worker/wasm - 同时存在
run_js/storage.estimate/animationend一类浏览器特性通道 - 同时有
Akamai/Kasada/PX/reese84/同盾/a_bogus一类风控分支 - Node 与浏览器差异巨大,且本地 patch 快速发散
流程:
- Phase 0
- Phase 1
- Phase 2,只恢复最小桥接切片
- 回到 Phase 1 重新坐实 sink
- Phase 3,先找第一个分叉点
- Phase 4,只在依赖清单已稳定时进入
- Phase 5
额外纪律:
- 这种场景禁止直接从
Phase 2跳到“全量本地还原” - 任何时候一旦失去边界锚点,优先退回
Phase 1
Hard-Site Escalation Ladder
困难站点统一按这个升级梯子推进,不允许乱跳:
- 浏览器最小样本
- 坐实真实请求、触发动作、目标字段
- 写入边界
- 坐实
entry -> builder -> writer
- 坐实
- 最小壳层恢复
- 只恢复挡路的 dispatcher / bridge / imports / opcode
- 首个分叉点诊断
- 做浏览器 vs 本地对比表
- 最小运行时迁移
- 只迁被证明会影响结果的依赖
- 浏览器辅助执行或 black-box reuse
- 当本地化成本过高时接受 browser-side 方案
- 稳定回归
- 再做纯算、代理或回放固化
升级条件:
- 上一层已经产出稳定工具,但仍不能支撑下一步
禁止跨级:
- 没有第 2 步就做第 5 步
- 没有第 4 步就宣称本地环境已经足够
- 没有第 6 步评估就强行追求“必须纯本地”
分支 C:浏览器与 Node 差异很大
特征:
- 浏览器正常,本地失败
- 本地能生成值,但服务端持续
403 - 调试一开就漂
- 指纹、时间、随机数、风控敏感
流程:
- Phase 0
- Phase 1
- Phase 3
- Phase 4
- Phase 5
切换规则速查
- 参数名明确且未重混淆:先走 Phase 1。
- 看到
JSVMP/227/226/平坦流/basearr/opcode:先切 Phase 2。 - 看到
wasm/bx-pp/protobuf builder/动态字符串壳:通常先走 Phase 1,再视遮蔽程度切 Phase 2。 - 看到
run_js/storage.estimate/animationend/postMessage/worker:优先做 Phase 3。 - 看到
Akamai/Kasada/PX/reese84/同盾/a_bogus:优先检查 Phase 1 与 Phase 3 的混合阻塞。 - 看到
腾讯滑块/阿里滑块/_rand/fuid/fs:优先按验证码双链与浏览器特性通道处理。 - 浏览器和 Node 出现首个真实分叉:切 Phase 3。
- 差分已归因、准备本地执行:切 Phase 4。
- 链路稳定、准备交付:切 Phase 5。
MCP 执行纪律
chrome-devtools-mcp负责“真实页面现场”,没有它就没有可靠浏览器基线。js-reverse负责“逆向工作流证据”,没有它就没有稳定的 Hook/代码/状态记录。jshook负责“高级逆向与对抗补强”,在重混淆、反调试、Stealth、复杂 Hook 时默认加入。- 任何阶段如果三者之一没有提供增量证据,要明确说明为什么此阶段暂不需要它,而不是静默跳过。
交付物
每轮至少维护这些文件:
reverse-records/总览.mdreverse-records/请求链路.mdreverse-records/恢复记录.mdreverse-records/运行时依赖.mdreverse-records/验证记录.mdreverse-records/状态载体卡.mdreverse-records/环境依赖清单.mdreverse-records/升级记录.md
没有这些工具时,不要长时间继续深挖。
最少要写清:
- 当前 phase
- 当前阻塞
- 当前最强锚点
- 最近一轮新增证据
- 若无新增证据,下一步准备切换到哪一层
一句话版本
复杂网站还原统一流程:
证据门 -> locate -> recover(必要时) -> runtime diff -> env-patch -> sign-replay
其中:
- 壳重就提前 recover
- 分叉明显就先 runtime diff
- Node 迁移只用 env-patch 收尾
- 最终交付再进 sign-replay