rea-reverse

ASAR 解包与 Electron IPC 分析

打开 Electron 应用的 app.asar,浏览和下载其中文件,查看主进程、preload 和渲染进程入口,并列出应用在进程之间暴露的 IPC 通道和 contextBridge 接口。

归档不会离开你的浏览器. 归档在一个禁止联网的 Web Worker 中本地读取,不上传任何内容。 如何自己验证

把 app.asar 拖到这里

或

通常位于 Electron 应用的 resources/app.asar(macOS 在 Contents/Resources 下),大小以浏览器能打开为限。

IPC 识别的局限

  • 调用是通过扫描 JavaScript 词法单元找出的,并不执行代码。计算属性(ipcRenderer[name](…))和别名调用(const send = ipcRenderer.send; send(…),或把 ipcRenderer 解构成别的名字)识别不了。
  • 每个 JS 文件只扫描前 4 MiB,总共最多 128 MiB;先扫描应用自身文件,最后才扫描 node_modules。

大多数 Electron 应用把 JavaScript、HTML 和各种资源打进一个叫 app.asar 的文件里,放在应用的 resources 目录下。ASAR 是一种很简单的归档格式:开头是一段描述目录树的 JSON 头,后面把各个文件的内容不压缩地依次拼接起来。里面装着决定 Electron 应用安全模型的三部分:拥有完整 Node.js 能力的 main 进程代码;决定网页能碰到什么的 preload 脚本;以及 renderer 页面。三者之间通过 IPC 通道通信。

这个工具在浏览器里打开 app.asar,列出全部文件,可以单独下载或预览其中任何一个已打包的文件,并且把代码里注册和调用的 IPC 通道整理成一张表。归档文件自始至终不会上传。

能做什么

输入。 一个 ASAR 归档,通常是 resources/app.asar。页面一开始只读 16 字节的前缀和 JSON 头,文件内容用到时才读,所以再大的归档,开销也差不多只是读一个头。上限:头部最大 32 MiB,最多 100 万个条目,目录最多嵌套 128 层,分析超时时间 2 分钟。

归档内容。 你会看到:

  • 文件树:每个文件、目录和符号链接,以可折叠、可搜索的树形展示,带大小。导出的 JSON 里还有每个文件的偏移、可执行标志;归档里有逐文件的 integrity 记录时,也包含它的算法和哈希。
  • 统计:文件、目录、链接、unpacked 文件各有多少,总大小,按扩展名的分布,以及最大的 20 个文件。
  • 下载和预览:任何存放在归档里面的文件,都可以单独下载或以文本方式查看,不用把整个归档解开。符号链接会自动跟随。
  • app.asar.unpacked 条目:标为 unpacked 的文件会列出来,但它们的字节并不在 .asar 文件里,Electron 把它们放在旁边的 app.asar.unpacked/ 目录中。原生模块通常就放在那里。

Electron 分析。 报告还包括:

  • package.json:name、version 和 main。main 入口按 Node 的规则解析,默认是 index.js。
  • preload 脚本:赋给 preload 属性的字符串,例如 webPreferences: { preload: path.join(__dirname, 'preload.js') },解析成归档里的文件,并注明它是在哪个文件的哪一行配置的。
  • renderer 入口:归档中 HTML 文件里的 <script src>,解析成归档内的路径;远程 URL 会被忽略。
  • IPC 通道表:所有对 ipcMain.handle、ipcMain.on、ipcMain.once、ipcRenderer.invoke、ipcRenderer.send、ipcRenderer.sendSync、ipcRenderer.on 和 webContents.send 的调用(win.webContents.send 这种写法也算)。每一行给出通道名、API、方向(renderer 到 main,或 main 到 renderer)、所属进程,以及 文件:行号。通道不是单纯的字符串字面量时,这一行标为动态,并显示原始表达式。
  • contextBridge 暴露:每个 contextBridge.exposeInMainWorld(name, { ... }),列出暴露的名字和 API 对象的键。这正是网页能调用的全部接口。
  • 原生模块:.node 文件(编译成 Mach-O、ELF 或 PE 的 Node 扩展),带大小和是否 unpacked。
  • source map:归档里的 .map 文件,以及被扫描的 JavaScript 文件中有多少个以 sourceMappingURL 注释结尾。
  • 警告:名字可疑的条目(含 ..、斜杠或绝对路径)、指向归档外部的链接、格式不对的条目、超出归档末尾的文件,以及只扫描了一部分的 JavaScript 文件。

原理

归档开头是两个 Chromium 的 “pickle” 记录:第一个给出头部的大小,第二个把头部存成一个 JSON 字符串。页面先检查这些长度是否互相吻合,再解析 JSON,遍历其中的 files 树。已打包文件的位置是“数据区起点 + offset”,而 ASAR 把 offset 存成十进制字符串。格式处理对照 @electron/asar 4.3.1 核对过,前缀检查参考了 REA(morluto 的开源项目,MIT 许可)。

IPC 表的来源是:用一个轻量的 JavaScript 词法分析器(tokenizer)扫描归档里所有 .js、.cjs、.mjs 文件,先扫应用自己的文件,node_modules 放在最后。词法分析会跳过注释、字符串内容和正则表达式,所以字符串里出现的 "ipcMain.handle('x')" 这种文字不会被误报。然后匹配 ipcRenderer.invoke( 这样的成员调用,读取第一个参数。只有当这个参数是单个字符串,或者不含插值的模板字符串时,通道才算字面量。

判断一个文件属于哪个进程:页面从 main 入口、已解析的 preload 脚本和 renderer 脚本出发,沿着相对路径的 require 和 import 往下走。没被这样走到的文件,就根据它用的 API 推断:ipcMain 和 webContents 只存在于 main 进程;调用了 contextBridge 的文件当作 preload。都不满足的,进程显示为未知。

隐私是怎么保证的

归档文件不会被上传:

  • 解析、扫描和每一次文件下载,都在你设备上的 Web Worker 里进行。Worker 通过浏览器的文件 API,只读取头部和需要的那几段字节。
  • 统计事件只有粗粒度信息:成功或错误码、文件数量区间(例如 11-100)、文件大小区间、耗时区间,绝不包含文件名、文件路径、通道名或任何其他内容。完整清单见隐私政策。
  • 工具页不展示广告。

如何自己验证

  1. 在选择文件之前,打开浏览器开发者工具,切到 Network(网络) 面板。分析一个归档,再从中下载一个文件。出现的请求只有页面资源和 worker 脚本 /_astro/asar-worker-*.js,没有任何一条带着你的归档。
  2. 或者先打开本页,然后断开网络(关闭 Wi-Fi,或在 Network 面板勾选 Offline),再分析文件,照样能完成。
  3. Worker 运行在 default-src 'none'; script-src 'self'; connect-src 'none' 这条内容安全策略之下,浏览器根本不允许它发出网络请求。

局限

  • 是词法扫描,不是完整的语法分析。 只认 ipcMain、ipcRenderer、webContents 这几个名字后面直接跟方法调用的写法。别名不会被追踪:const { ipcRenderer: r } = require('electron'); r.send(...),或者 const r = ipcRenderer,都识别不了;ipc[name](...)、ipcMain['handle'](...) 这种计算属性调用也识别不了。把这些对象改了名的压缩(minify)代码,大部分行都会丢失。
  • 只覆盖上面列出的 API。 ipcRenderer.once、ipcRenderer.postMessage、ipcRenderer.sendToHost、ipcMain.removeHandler,以及基于 MessagePort 的通道,都不在表里。
  • 动态通道不会被求值。 由变量拼出来的通道只显示表达式,不会算出具体的值。
  • 扫描预算。 每个 JavaScript 文件只扫描前 4 MiB,整个归档总共最多扫描 128 MiB。超出预算的文件(通常在 node_modules 里)会在警告里列出。
  • 不扫描的内容。 unpacked 的 JavaScript(字节不在归档里)、HTML 里的内联 <script> 代码块、TypeScript 源文件,以及超过 1 MiB 的 package.json 或 HTML 文件。
  • integrity 只显示,不校验。 导出的 JSON 里带有头部记录的每个文件的哈希,但不会重新计算核对,也不检查 Electron 的 ASAR integrity fuse 或应用包里保存的哈希。
  • 进程归属是推断的。 不会追踪裸包名的导入,所以只能通过某个 npm 包到达的代码,可能显示为未知进程。
  • 原生代码不在范围内。 .node 文件只列出来,请用 Ghidra 这类原生工具分析。

常见问题

能把整个归档解出来吗?

页面一次下载一个文件。要完整解包,请在自己的电脑上运行 npx @electron/asar extract app.asar out/。

标为 unpacked 的文件为什么下载不了?

它的字节不在 .asar 文件里,请到旁边的 app.asar.unpacked/ 目录里找。

为什么某个通道显示为动态?

第一个参数不是单个字符串字面量,例如 `user:${id}`,或者是在别处定义的常量。打开对应文件的那一行,看它最终是什么值。

应用明明用了 IPC,表却是空的?

多半是代码被压缩、对象改了名,或者 IPC 调用位于 unpacked 的文件或超大的文件里。看一下警告和扫描统计。

它能直接找出安全漏洞吗?

它画出的是攻击面:暴露了哪些接口、有哪些通道和对应的处理函数。处理函数有没有校验输入,需要你自己去读代码判断。

合规说明

请只分析你自己开发的应用、你获得授权评估的应用,或者开源应用。请遵守所分析软件的许可协议(包括其中限制逆向工程的条款)以及适用于你的法律法规。本工具不提供任何绕过授权或其他保护措施的建议。另见免责声明。

相关教程