# Web Re Skill

> AI辅助Web逆向工程综合技能。用于Web前端加密/反混淆、浏览器环境补全(补环境)、wasm逆向分析(wasm2c/wasm2js)、视频播放链路解密(m3u8/ts/NAL/TEA)、VMP虚拟机保护分析、浏览器播放对齐(Chrome DevTools MCP先导、worker状态追踪、postMessage序列重放、零输出诊断)、AI自动插桩追踪调用链。触发词：补环境、wasm逆向、解密视频、播放链路、vmp分析、web逆向、ts帧解密、播放对齐、CCTV。

- Skill: `aresbit/web-re-skill` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add aresbit/web-re-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aresbit/web-re-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: aresbit (https://skillmd.com/u/aresbit)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aresbit/web-re-skill

---


# Web Reverse Engineering

AI辅助Web逆向工程。融合实战经验（CCTV wasm+vmp 播放链路逆向）与浏览器优先的播放对齐方法论。

## 核心原则

- **浏览器优先** — 以浏览器捕获的实证为唯一权威来源，不猜测任何环境字段
- **不写一行代码** — 让AI自动完成补环境、插桩、分析；人工聚焦在验证和决策
- **状态即数据** — postMessage 顺序、vmpTag 状态都是必须精确复现的数据
- **合理目标** — 从"完全黑盒"推进到"本地可用"，不追求完美复刻

---

## 决策树：选择逆向策略

```
用户提供了什么？
├── 完整 URL（如 tv.cctv.com/...shtml）
│   └── → 浏览器优先工作流（第2节）
│         CDP 打开页面 → 跟踪播放链路 → 保存环境证据 → 用户确认 → Node 复现
│
├── JS 代码文件
│   ├── 含 wasm（有 VMP） → 直接补源JS（策略3），跳过 wasm 转换
│   ├── 含 wasm（无 VMP） → wasm2js 后补环境，或 wasm2c 分析算法
│   └── 纯 JS 加密 → AI 自动插桩分析 + 补环境
│
├── wasm 二进制
│   ├── wasm2js 转 JS 调试
│   └── wasm2c 转 C 分析算法
│
└── m3u8/ts 文件（视频解密）
    └── → 视频解密工作流（第7节）
          检测加密类型 → 提取密钥 → 解密 ts → ffmpeg 验证
```

---

## 1. 浏览器优先工作流（四阶段法）

当处理具体网页（如 CCTV 视频播放）时，严格遵循以下四个阶段。

### 阶段1：浏览器发现（Browser Discovery）

用 Chrome DevTools MCP 打开目标页面，捕获：

```
□ 页面 URL
□ 脚本引导链（script bootstrap chain）
□ Worker 创建路径及每个 worker 的角色
□ Worker URL（原始脚本路径 / blob worker / host-worker）
□ 页面环境转储（全局变量、location、navigator 等）
□ Worker 环境转储
□ 网络请求顺序：
   - video info / H5player.json
   - playlist / m3u8
   - 各 ts fragment
   - 配置文件
□ 播放引导：
   - player init 调用
   - MediaSource / SourceBuffer 创建
   - codec 字符串
□ 主线程 → worker 消息顺序（postMessage 序列）
□ Worker → 主线程消息顺序
□ 解密发生在哪个阶段（moduleActive / moduleDecData）
□ transmux / mux / remux 发生在哪里
□ Wasm / module 初始化序列
```

**捕获的每一条都必须是浏览器实证，不能是推测。**

### 阶段2：用户确认（User Confirmation）

保存浏览器发现的完整流程记录，向用户确认：

- 这是正确的播放流程吗？
- 有遗漏的阶段吗？
- 是否按此流程进行 Node 复现？
- Node 环境是否基于该页面的浏览器环境构建？

### 阶段3：Node 复现（Node Recreation）

```
□ Node.js 是唯一的本地运行目标
□ jsdom 模拟被证实需要的浏览器字段
□ pageHref / workerHref / location 等值从浏览器捕获
□ 复用真实 worker 源码而非重写等价逻辑
□ 保持 postMessage 顺序与浏览器一致
□ 保持输入字节与浏览器一致
□ 不补浏览器未观测到的字段
```

### 阶段4：验证（Validation）

```
□ ffprobe 检查输出媒体信息
□ ffmpeg remux 或 decode-check
□ 输出验证结果报告
```

### 危险信号（停止并返回浏览器）

- 在没有浏览器证据的情况下模拟请求
- 凭记忆添加 location / navigator 字段
- 重排或省略 worker 消息
- 使用 HTML 而非 Node 做本地复现
- 跳过输出验证
- 浏览器流程未经用户确认就进入 Node 复现

---

## 2. 浏览器环境补全（补环境）

### 需要补的浏览器环境变量

| 变量 | 补全方式 | 来源场景 |
|------|----------|----------|
| `window` / `document` / `self` | jsdom | 几乎所有页面 |
| `location` / `location.origin` | 从浏览器捕获 | 环境检测 |
| `pageHref` / `workerHref` | 从浏览器捕获 | worker 环境 |
| `navigator.userAgent` | 从浏览器捕获 | 环境检测 |
| `mediaTagID` | 从页面元素提取 | 播放器 |
| `Date.now()` / `performance.now()` | 固定时间 | 时间相关算法 |
| `new Date()` | 固定时间 | 时间相关算法 |
| blob worker `href/pathname/protocol` | 从浏览器捕获 | worker 环境 |
| XHR/fetch | mock 返回 | 环境检测请求 |
| `indexedDB` | mock | 存储检测 |

### 补环境代码框架

```javascript
const { JSDOM } = require('jsdom');
const dom = new JSDOM('<!DOCTYPE html>', {
    url: 'https://example.com',
    referrer: 'https://example.com',
    contentType: 'text/html',
    pretendToBeVisual: true,
    runScripts: 'dangerously',
});
global.window = dom.window;
global.document = dom.window.document;
global.navigator = dom.window.navigator;
global.location = dom.window.location;
global.self = dom.window;

// 从浏览器捕获的固定时间
const FIXED_TIME = 1712345678000;
Date.now = () => FIXED_TIME;
global.performance = { now: () => 0 };
```

### AI 辅助补环境迭代

```
Round 1: 原始 JS 给 AI，告知入口函数名
       → AI 返回补环境后代码
       → 运行，记录错误

Round 2: 错误信息给 AI
       → AI 补充缺失变量/函数
       → 继续运行

Round N: 重复直到解密函数正常输出
```

详细参考：[references/browser-env.md](references/browser-env.md)

---

## 3. Wasm 逆向分析

### Wasm 转换工具

```bash
# wasm2js - wasm → JS（方便调试）
npm install -g wabt
wasm2js input.wasm -o output.js

# wasm2c - wasm → C（分析算法）
wasm2c input.wasm -o output.c

# 在线转换
# https://wasm2js.com/
```

### VMP（虚拟机保护）识别特征

- wasm 中存在中心分发函数（如 `f50`）
- 所有导出函数最终都进入该分发函数
- 包含超长字节码段（VMP 指令集）
- 同一输入在不同状态下输出可能不同

**检测到 VMP → 直接补源JS，跳过 wasm2c/wasm2js 深度分析。**

### 三种策略对比

| 策略 | 方法 | 适用场景 | 缺点 |
|------|------|----------|------|
| wasm2c | `wasm2c in.wasm -o out.c` | 无VMP，深究算法 | VMP下无意义 |
| wasm2js | `wasm2js in.wasm -o out.js` | 无VMP，JS调试 | 代码体积巨大 |
| 直接补源JS | JS不变，只补环境 | **含VMP的推荐方案** | 需处理超长字节码 |

### VMP 代码特征（wasm2js 输出）

```javascript
function $49($0_1, $1_1, $2_1, $3_1) {
    if ((global$4 | 0) != (2 | 0)) {
        HEAP32[(global$1 + 8 | 0) >> 2] = $0_1;
        // ...
        if ((global$4 | 0) == (1 | 0)) { global$4 = 3 }
    }
    $0(gimport$4 + 513548 | 0 | 0);  // → f50 分发器
    return HEAP32[global$1 >> 2] | 0 | 0;
}
```

---

## 4. 浏览器播放对齐

当浏览器播放正常但本地复现结果不同时，定位差异。

### 核心原则

- **Trust the browser** — 浏览器主 worker 是状态序列的权威来源
- **State over input** — 相同输入 + 不同状态序列 = Session 积累问题
- **Message order matters** — postMessage 顺序比内容更重要

### 最小浏览器插桩

```javascript
const origWorker = window.Worker;
const captured = { messages: [], workerUrl: null };

window.Worker = function(url, options) {
    captured.workerUrl = url;
    const w = new origWorker(url, options);
    const origPost = w.postMessage;
    w.postMessage = function(msg) {
        captured.messages.push({
            ordinal: captured.messages.length,
            time: Date.now(),
            type: msg?.constructor?.name,
        });
        return origPost.apply(this, arguments);
    };
    return w;
};
```

### 零输出分析

记录每个零输出调用：`segment_index`、`call_ordinal`、`timing_tag`、`state_tag`、`input_length`、`output_length`。

### 精简实验（按顺序尝试）

1. **Neighbor-window**: 比较 ordinal-1, ordinal, ordinal+1
2. **Alternate timing**: 同一单元换不同时间值
3. **Skip-ordinal**: 跳过前置单元看是否影响后续
4. **Browser-message replay**: 重放浏览器 postMessage 序列
5. **State-window capture**: 调用前后快照 worker 状态

详细参考：[references/playbook.md](references/playbook.md)

---

## 5. AI 自动插桩分析

让 AI 在关键路径自动插入日志追踪参数传递。

### 提示词模板

```
分析以下 JS 的 {函数名} 调用链：
1. 标注所有子函数及作用
2. 追踪参数从入口到最终使用的路径
3. 识别加密算法特征（TEA/AES/RC4/XOR）
4. 标记所有浏览器环境依赖
5. 自动插入 console.log 调试日志
```

---

## 6. 视频解密模式

### 常见加密方案

| 方案 | 识别特征 | 应对 |
|------|----------|------|
| 固定 KEY | m3u8 含 `#EXT-X-KEY:URI="..."` | 下载 KEY 解密 |
| TEA加密 | 密钥嵌入 NAL 数据 | NAL[15:31] 取16字节 |
| Wasm+VMP | wasm + vmptag 状态推进 | 补环境 + postMessage 重放 |
| Token/DRM | 需要会话 token | 模拟认证 |

### 算法参考

```python
# TEA 解密（Python）
from Crypto.Cipher import AES
key = nal_data[15:31]  # NAL 特定位置取16字节密钥
cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(encrypted_data)
```

- **LCG mediaid**: 以时间戳为种子，`X_{n+1} = (a*X_n + c) mod m`
- **VMPTag状态机**: 初始0，每次调用`[0~6]`触发，调用后→7

### 完整视频逆向流程示例（CCTV）

```
1. CDP 打开视频页面 → 捕获网络请求获取 m3u8
2. 提取播放器 JS → 定位解密入口函数 ua
3. 检测到 wasm+VMP → 直接补源JS
4. 捕获 postMessage 序列和环境变量
5. AI 补环境（jsdom + 固定时间 + location）
6. Node.js 运行，检查 NAL 输出
7. 修复缺失环境（XHR mock, workerHref 等）
8. 下载 ts 分段解密
9. ffmpeg 合成 mp4 验证
10. 检查花屏 → 继续补环境
```

---

## 7. 产出规范

### 本地文件保存

每次复现保存以下产物：

```
artifacts/{session_name}/
├── browser-flow-summary.md        # 浏览器流程记录
├── browser-environment.json       # 浏览器环境转储
├── worker-message-order.json      # postMessage 序列
├── request-list.json              # 网络请求列表
├── downloaded-sources/            # 下载的 JS 源码（原始+格式化）
│   ├── player-original.js
│   └── player-formatted.js
├── node-reproduction-summary.md   # Node 复现结果
└── validation-report.txt          # ffmpeg/ffprobe 验证报告
```

### 环境输出要求

每次追踪后，保存环境摘要说明 Node 正在模拟什么：

- 页面 URL
- worker URL
- 哪些全局变量存在/不存在
- 使用哪些网络/存储 API
- 从浏览器捕获并固定的值
- 仍未知的字段（不允许猜测）

---

## 参考文件

按需加载：

- [references/playbook.md](references/playbook.md) — 浏览器播放对齐操作手册（捕获、重放、精简、零输出分析）
- [references/browser-playback-flow.md](references/browser-playback-flow.md) — 浏览器优先的播放发现四阶段流程模板
- [references/browser-env.md](references/browser-env.md) — 浏览器环境补全完整指南

## 脚本工具

- [scripts/env-template.py](scripts/env-template.py) — 生成 Node.js 浏览器环境补全模板代码

