什么是SourceMap
Source Map(源映射)是 JavaScript 开发中用于调试压缩/混淆代码的重要技术。以下是核心要点:
一、核心作用
- 代码映射:将压缩后的代码(如
main.min.js)映射回原始源代码(.js/.ts) - 调试支持:在浏览器开发者工具中直接调试未压缩的原始代码
- 错误追踪:生产环境报错时,通过 source map 定位原始代码位置
二、生成方式
通过构建工具配置:
// webpack.config.js
module.exports = {
devtool: 'source-map' // 常见选项:eval | inline-source-map | hidden-source-map
}
三、文件结构
典型 source map 包含:
{
"version": 3,
"file": "app.min.js",
"mappings": "AAEIA,QECcE...",
"sources": ["src/app.js"],
"sourcesContent": ["原始代码内容"],
"names": ["originalFunction"],
"sourceRoot": "/src"
}
四、关键特性
- 映射算法:使用 VLQ 编码存储位置映射信息
- 性能优化:仅在开发者工具打开时加载 source map
- 安全策略:可通过 HTTP Header 控制访问权限
X-SourceMap: /path/to/file.map
五、开发建议
- 开发环境:使用
eval-source-map获得最佳调试体验 - 生产环境:建议使用
hidden-source-map配合错误监控系统 - 注意事项:避免公开敏感源码的 source map
逆向过程
获取js文件
获取js文件的方法有很多,这里以document.head.querySelectorAll('script[src]')为例,获取所有的js文件。
const fullAnalysis = () => {
return [
...document.head.querySelectorAll(`
script[src],
link[rel="modulepreload"][as="script"][href]
`)
].map(el => ({
url: el.src || el.href,
type: el.tagName === 'SCRIPT' ? 'classic' : 'modulepreload',
crossOrigin: el.crossOrigin || null,
async: el.hasAttribute('async'), // 仅对script有效
defer: el.hasAttribute('defer') // 仅对script有效
}));
}
运行上面的代码,你可以得到一个类似于下面的json,需要将其保存到一个scripts.json文件中。
[
{
"url": "http://chat.zzu.edu.cn/static/loader.js",
"type": "classic",
"crossOrigin": null,
"async": false,
"defer": true
},
{
"url": "http://chat.zzu.edu.cn/_app/immutable/nodes/0.ChEw188q.js",
"type": "modulepreload",
"crossOrigin": "anonymous",
"async": false,
"defer": false
}
]
注意:这里只是获取了js文件的url,还没有获取js文件的sourcemap。
随便打开一个js文件,查看其最后一行是否有sourcemap,如果有,那么就可以继续下一步。

上面的js文件有sourcemap,且sourcemap的url为js文件的URL+.map,
获取sourcemap
这里写一个简单的函数,获取sourcemap。
import json
import os
import requests
from urllib.parse import urlparse
def download_file(url, save_path, headers):
"""通用文件下载函数"""
try:
response = requests.get(url, headers=headers, timeout=15)
response.raise_for_status()
with open(save_path, 'wb') as f:
f.write(response.content)
return True
except Exception as e:
print(f"❌ 下载失败 [{url}]: {str(e)}")
return False
def download_assets(json_path, output_dir="downloaded"):
"""
下载JS文件及其对应的Sourcemap文件
参数:
json_path: 输入的JSON文件路径
output_dir: 保存文件的目录(默认downloaded)
"""
os.makedirs(output_dir, exist_ok=True)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept-Encoding': 'gzip, deflate'
}
with open(json_path, 'r') as f:
scripts = json.load(f)
for index, script in enumerate(scripts, 1):
js_url = script.get('url', '')
if not js_url:
continue
# 解析URL信息
parsed = urlparse(js_url)
domain_part = parsed.netloc.replace(':', '_')
path_part = parsed.path.lstrip('/')
# 构建保存路径
save_dir = os.path.join(
output_dir,
domain_part,
os.path.dirname(path_part)
)
os.makedirs(save_dir, exist_ok=True)
# 下载原始JS文件
js_filename = os.path.basename(path_part)
js_save_path = os.path.join(save_dir, js_filename)
if download_file(js_url, js_save_path, headers):
print(f"✅ [{index}/{len(scripts)}] JS下载成功: {js_filename}")
# 自动添加.map后缀下载sourcemap
if js_url.endswith('.js'):
sourcemap_url = f"{js_url}.map"
map_filename = f"{js_filename}.map"
map_save_path = os.path.join(save_dir, map_filename)
if download_file(sourcemap_url, map_save_path, headers):
print(f"🗺️ [{index}/{len(scripts)}] MAP下载成功: {map_filename}")
if __name__ == '__main__':
download_assets("scripts.json", "web_assets")
运行上面的代码,你可以得到一个web_assets文件夹,里面存放了js文件及其对应的sourcemap文件。

逆向工具
这里使用shuji这个工具来逆向js文件。
安装shuji
npm install -g shuji
逆向js文件
shuji web_assets -p
运行之后获得逆向后的js文件。如下图的src和node_modules文件夹。
