逆向工程实战手册 第 100 章

第 100 章 SourceMap 与前端源码恢复(Bundle Recovery)

现代前端(webpack/Vite/Next.js)把代码打包混淆成 bundle。恢复源码的黄金途径:SourceMap(如果泄露=直接还原源码)、bundle 结构分析(chunk 注册表/模块映射)。这是第 13 章 JS 逆向的深度延伸——前端逆向的第一步往往是"源码恢复"而不是"硬读混淆代码"。

📍 知识点地图 | 主题:SourceMap恢复 | 前置:第13章 | 后续:— | 核心概念:SourceMap、webpack结构、webcrack、chunk

100.1 前端构建产物结构(先懂形态)

现代前端构建(webpack/Vite/Next.js)产物:
  bundle.js(打包后的主文件)
  chunk(代码分割: 路由级/组件级)
  manifest/chunk 注册表(模块→chunk 映射)
  source map(.js.map,源码映射文件)

构建流程: 源码 → 打包(合并/压缩/混淆)→ 产物
逆向目标: 从产物回到"接近源码"的形态

100.2 SourceMap(源码恢复的黄金途径)

什么是 SourceMap

.js.map 文件: 记录了"压缩代码 → 原始源码"的完整映射
  (每个 token 的位置映射 + 原始源码内容!)
如果网站把 .map 文件一起发布了 → 直接拿回源码

查找:
  URL: bundle.js.map / static/js/main.js.map
  bundle 尾注释: //# sourceMappingURL=xxx.map
  目录枚举(第 57 章)

验证: DevTools → Sources → 看到 .map 对应源码

SourceMap 恢复源码

# 1. 下载 bundle + .map
curl -O https://target.com/static/js/main.js
curl -O https://target.com/static/js/main.js.map

# 2. 用工具还原(sourcemap 还原器/DevTools)
#    DevTools: 直接打开 bundle(自动加载 .map → 源码视图)
#    或 node 脚本: source-map 库解析 .map → 输出源码文件树

# 3. 还原后 = 完整源码阅读(第 13 章方法直接可用)

SourceMap 泄露的危害

□ 完整源码(业务逻辑/算法/密钥常量)
□ 内部路径/模块结构(攻击面地图)
□ 注释(开发信息/遗留代码)
□ 未压缩的开发版本逻辑(比混淆版好读百倍)

100.3 无 SourceMap 的 bundle 恢复(硬核路线)

1. Bundle 结构分析(找模块边界)

webpack 格式特征:
  chunk 注册表: 模块 ID → 函数(大数组)
  模块函数: (module, exports, require) => { ... }
  入口: 数组第一个元素(runtime)

分析:
  1. 识别 webpack 包装(开头大数组+函数体)
  2. 定位模块边界(按模块函数分割)
  3. 提取每个模块的代码段
  4. 按模块重组 → 逐模块分析

2. 工具链

webcrack: 自动去混淆+模块拆解(现代神器)
  npx webcrack bundle.js -o output/   # 还原模块结构+美化
sourcify / jsnice: 变量命名美化
Babel/AST: 手动去混淆(第 13 章)
SourceMap 扫描器: 批量找 .map(目录/URL 扫描)

3. 手动提取(工具失效时)

□ 找 chunk 注册表(大数组)→ 模块边界
□ 找关键字符串(API 路径/错误信息)→ 所在模块
□ 单独分析目标模块(不需要全还原)
□ 记录: 模块 ID → 功能映射

100.4 框架专项(Next.js/Vue/React)

Next.js:
  _next/static/chunks/ 结构(页面 chunk 命名规律)
  buildManifest/ssgManifest(页面→chunk 映射)
  RSC payload(服务端组件数据在 HTML/脚本里)
  → 页面路由 → 对应 chunk → 目标逻辑

Vue:
  webpack chunk 按路由分割
  app.js + chunk-vendors.js(第三方库)
  runtime 结构类似 webpack

React SPA:
  路由 chunk(按页面加载)
  API 调用集中在服务层模块(第 13 章找接口)

100.5 完整恢复流程(端到端)

1. 发现 bundle(页面源码/Network 面板)
2. SourceMap 检查(尾注释/URL 探测)→ 有则直接还原
3. 无 map → 结构分析(webpack 识别 → 模块边界)
4. webcrack 自动处理(去混淆+拆模块)
5. 目标模块深入(API/密钥/算法)
6. 还原产物 → 第 13 章分析方法

100.6 检查清单

□ bundle 定位(主文件+chunk)
□ SourceMap 检查(尾注释/URL/目录枚举)
□ map 恢复源码(DevTools/工具)
□ 无 map: webpack 结构识别
□ 模块边界分割
□ webcrack 去混淆
□ 目标模块提取(API/密钥/逻辑)
□ 框架专项(Next/Vue 路由→chunk)
□ 报告: 源码恢复程度+发现

动手练习

  1. 用一个带 SourceMap 的测试站点(或自己 build 一个 webpack 项目带 map),完整恢复源码。
  2. 构建一个无 map 的混淆 bundle,用 webcrack 处理,对比恢复效果。
  3. 手工识别 webpack 的 chunk 注册表,找到 3 个模块的边界。
  4. 分析一个 Next.js 站点的 chunks 结构,画出"页面→chunk→逻辑"的映射。

深入阅读

  • 仓库:CTF-Sandbox-Orchestrator/competition-bundle-sourcemap-recovery/(bundle/sourcemap CTF 子技能)
  • 仓库:CTF-Sandbox-Orchestrator/competition-bundle-sourcemap-recovery/references/bundle-sourcemap-recovery.md
  • 仓库:skills/js-reverse/(第 13 章五阶段工作流)
  • 仓库:CTF-Sandbox-Orchestrator/competition-template-render-path/(SSR/水合数据)
  • 仓库:skills/reverse-engineering/languages-platforms.md(Electron ASAR 同思路)