ASAR 解包:提取并查看 Electron 应用里的文件
本页目录
Electron 应用把 JavaScript、HTML 和各种资源都打进一个文件:resources/app.asar。想调试自己应用的打包版本、确认某次发布到底装了什么,或者研究一个开源 Electron 项目,第一步都是打开这个文件。本文是一份快速上手指南:app.asar 在各个平台上放在哪里,怎样在浏览器或命令行里解包,打开之后先看什么。完整的分析流程(REA 的应用图、preload 桥接、IPC 配对)见 用 REA 逆向分析 Electron 应用。
适合谁读:分析自己的应用、开源应用或已获授权评估的应用的 Electron 开发者和审查者。命令行部分需要 Node.js,浏览器部分什么都不用装。
文中的例子是专门为本文写的一个迷你 Electron 应用 notes-shell:一个 main 进程、一个 preload 脚本、一个 renderer 页面,外加一个占位用的 .node 文件。打包时用 --unpack "*.node" 让它留在归档外面。
ASAR 是什么格式
ASAR 是一种很简单的归档格式:开头是一小段二进制前缀和一段描述目录树的 JSON 头,后面把文件内容不压缩地依次拼接起来。头里的每个文件条目有 size、offset(存成十进制字符串),新版 @electron/asar 写出的归档还带一条 integrity 记录,里面是 SHA-256 哈希。下面是示例里一个文件的真实头部条目:
{"size":300,"offset":"1219","integrity":{"algorithm":"SHA256","hash":"022ffed9…","blockSize":4194304,"blocks":["022ffed9…"]}}
有些条目标着 "unpacked": true,它们的字节根本不在 .asar 里,而是放在旁边的 app.asar.unpacked/ 目录中,因为这些文件必须以真实文件的形式存在于磁盘上。最常见的就是原生 Node 扩展(.node)。复制应用来分析时,记得把归档和这个目录放在一起。
app.asar 在哪里
归档位于应用的 resources 目录。常见位置:
- macOS:
/Applications/<App>.app/Contents/Resources/app.asar。在访达里右键应用,选显示包内容就能进去。 - Windows:按用户安装的一般在
%LOCALAPPDATA%\Programs\<App>\resources\app.asar,为所有用户安装的一般在C:\Program Files\<App>\resources\app.asar。 - Linux:安装包常放在
/opt/<App>/resources/app.asar。AppImage 要先解开:带--appimage-extract参数运行它,再到squashfs-root/resources/下找。
有的应用没有 app.asar,直接带一个普通的 resources/app/ 目录。那就没什么可解包的,直接打开目录即可。
开始之前,先把 app.asar 和 app.asar.unpacked/ 复制到一个工作目录。这样已安装的应用自动更新时,不会影响你的分析。
方法一:在浏览器里解包
打开 ASAR 解包与 Electron IPC 分析工具,把 app.asar 拖进去。归档在你电脑上的 Web Worker 里解析,不会上传。页面一开始只读前缀和头部,所以再大的归档也打开得很快。你会得到一棵可搜索的文件树,任何打包在里面的文件都能单独预览或下载,不用解开整个归档。
对 notes-shell,报告显示 7 个文件、3 个目录、1 个 unpacked 条目。除了文件树,工具还替你读出了 Electron 的结构:
| 报告部分 | notes-shell 的结果 |
|---|---|
| package.json | notes-shell 0.3.0,main: src/main.js |
| preload 脚本 | src/preload.js,在 src/main.js:10 配置 |
| renderer 入口 | renderer/renderer.js,来自 renderer/index.html |
| contextBridge | notes,键为 list、save、onSaved(src/preload.js:3) |
| 原生模块 | native/fast-hash.node,30 字节,unpacked |
IPC 通道表一共 6 行:src/main.js 第 18、19 行用 ipcMain.handle 处理的 notes:list 和 notes:save;第 21 行用 webContents.send 发出的 notes:saved;以及 src/preload.js 第 4 到 6 行里对应的 ipcRenderer.invoke 和 ipcRenderer.on 调用。每一行都标出了方向,以及发起调用的代码属于哪个进程(main 还是 preload)。
浏览器方式有两个限制:一次只能下载一个文件;unpacked 条目的字节不在归档里,所以拿不到。要完整解包,用命令行。
方法二:用 npx @electron/asar 解包
@electron/asar 是 Electron 项目维护的打包库,不用全局安装就能运行:
npx @electron/asar list app.asar
npx @electron/asar extract app.asar out
npx @electron/asar extract-file app.asar src/preload.js
list 列出归档里的所有路径。示例的输出(4.3.1 版):
/native
/native/fast-hash.node
/package.json
/renderer
/renderer/index.html
/renderer/renderer.js
/src
/src/main.js
/src/preload.js
/src/store.js
extract 把整棵树写到 out/,还会从 app.asar.unpacked/ 把 unpacked 条目复制过来,所以 out/native/fast-hash.node 也在。extract-file 把单个文件以它的文件名写到当前目录,这里就是 preload.js。
这几条命令在 Windows、macOS、Linux 上用法相同。Windows 下路径有空格要加引号,例如 npx @electron/asar extract "C:\Program Files\Notes\resources\app.asar" out。
缺了 .unpacked 目录会怎样:只把 app.asar 复制到另一个目录再运行 extract,4.3.1 版会报 Error: Unable to extract some files:,接着是 app.asar.unpacked/native/fast-hash.node 的 ENOENT: no such file or directory。打包在归档里的文件照样写出来了,只是缺了原生模块。把那个目录放回归档旁边,重新运行即可。
方法三:交给 REA
REA(morluto 维护的开源项目)可以直接读取 ASAR 归档。装好 REA 6.4.0(见 安装与接入)后:
rea inspect-artifact /absolute/path/work/app.asar --json > inventory.json
rea extract-artifact /absolute/path/work/app.asar --json
rea analyze-javascript-application /absolute/path/work/app.asar --json > app-evidence.json
inspect-artifact返回按内容寻址的清单:路径、大小、SHA-256 摘要,外加完整性检查结果和建议的下一步探查。它不解包任何文件。extract-artifact把所有普通文件写进 REA 新建的临时目录(系统临时目录下的rea-extracted-<uuid>),并在结果里告诉你位置。它会拒绝路径穿越和符号链接逃逸,也从不覆盖已有文件。analyze-javascript-application在不运行应用的情况下构建应用图:入口、模块、import、preload 桥接和 IPC。怎么读这张图,见 Electron 分析 一文。
REA 会拿每个条目去核对归档里声明的 SHA-256,对不上时默认直接失败,unpacked 的文件也会核对。如果某次发布在打包之后合理地改动过 unpacked 文件(比如给原生模块签了名),加上 --integrity-policy record-and-continue。REA 会把这些不一致明确记录为不可信,而不是悄悄忽略。
打开之后先看什么
文件到手后,按下面的顺序看,能最快回答大部分问题:
package.json。main指出 main 进程的入口,没写时按 Node 的默认规则用index.js。name和version告诉你手上是哪个版本。- main 入口。找
new BrowserWindow(...)和它的webPreferences:用的是哪个preload脚本,contextIsolation、sandbox、nodeIntegration的设置是否符合你对自己应用的预期。 - preload 脚本。
contextBridge.exposeInMainWorld精确规定了网页能调用什么。在示例里,网页只能调用window.notes.list、save和onSaved,别的都调不到。 - IPC 处理函数。每个
ipcMain.handle或ipcMain.on通道,都是 main 进程接受 renderer 输入的入口。逐个确认处理函数有没有校验收到的数据。示例里的store.save会把笔记内容转成字符串并截断长度。 - source map 和原生模块。归档里的
.map文件能把压缩代码还原成源码,浏览器工具会统计它们的数量。.node文件是编译后的代码,要用 Ghidra 这类原生工具分析(见 REA + Ghidra)。
如果 main 或 preload 代码被压缩过,浏览器工具的 IPC 表可能大半是空的,因为它靠字面名称 ipcMain、ipcRenderer、webContents 来匹配。REA 的分析基于语法树,不受这个限制。如果归档里还带了 .map 文件,可以用 source map 还原工具 重建原始文件;拿它来还原自家应用的崩溃堆栈,见 还原压缩后的报错堆栈。
重新打包自己的构建
调试自己的应用时,你可能想改一个文件,放回打包好的版本里试试:
npx @electron/asar pack out app.asar --unpack "*.node"
做本地快速实验没问题,但有两个 Electron 特性可能会拦住你。开启 EnableEmbeddedAsarIntegrityValidation 熔断开关(fuse)后,Electron 会拿归档头去比对打包时记录的哈希,重新打包的归档会被拒绝加载。开启 OnlyLoadAppFromAsar 后,零散的 resources/app/ 目录会被忽略。这两项都是发布版的加固设置,不要为了让补丁跑起来而关掉它们。正确的做法是改源码,再用你平时的打包工具(Electron Forge、electron-builder)重新构建。
常见问题
能在线解包 ASAR 吗?
可以。ASAR 解包工具 在浏览器里运行,不上传归档,你可以在 DevTools 的 Network 面板里自己验证。要连同 unpacked 文件一起完整解包,用 npx @electron/asar extract。
app.asar 是加密的吗?
不是。这个格式没有任何加密,内容按原始字节存放,头部就是 JSON。有些应用会在打包前把 JavaScript 混淆或压缩,那是另一回事。
为什么有的文件标为 unpacked,却下载不了?
它的字节在 app.asar.unpacked/ 里,不在归档中。直接打开那个目录就行。
该用哪个工具?
想快速看一眼、了解 IPC 全貌,用浏览器工具;要把文件完整解到磁盘上,用 @electron/asar;需要校验完整性、得到可以引用的 Evidence 和应用图,用 REA。
下一步
- 用 REA 做完整的 Electron 分析:/zh/reverse-engineer-electron-app/
- 从压缩后的 bundle 恢复可读模块:/zh/recover-javascript-source-from-bundle/
- 确认自己的发布没有误带 map:/zh/audit-source-map-leaks-before-publish/
- 本文用到的工具:ASAR 解包与 Electron IPC 分析