浅析electron的asar文件格式

4,114 阅读4分钟

ASAR文件

什么是ASAR文件

ASAR 文件是一种专有格式,常用于 Electron 应用程序中。ASAR 文件的全称是 "Atom Shell Archive",是一个档案文件格式,类似于 ZIP 或 TAR,用于将多个文件打包成一个文件。

使用electron-builder或者electron-forge打包的electron程序,一般在程序安装目录的resources文件夹下能找到ASAR文件。

特点和用途

  1. 文件打包:ASAR 文件可以将一个应用程序的多个文件打包成一个单一的文件,以简化分发和部署。
  2. 快速读取:虽然 ASAR 文件是一个档案文件,但它可以直接从档案中读取文件,而无需先解压缩。这提高了文件读取的速度。
  3. 应用安全性:通过将文件打包成 ASAR 格式,可以一定程度上防止用户轻易修改应用程序的源文件,提高了应用的安全性(:asar文件并没有加密,只是在一定程度上增加了篡改源文件的复杂度)。

生成或提取ASAR文件

要生成或者提取ASAR文件,可以借助 @electron/asar 来实现;

使用方式:

Usage:

asar [options] [command]

Commands:

pack|p <dir> <output>
   create asar archive

list|l <archive>
   list files of asar archive

extract-file|ef <archive> <filename>
   extract one file from archive

extract|e <archive> <dest>
   extract archive

Options:

-h, --help     output usage information
-V, --version  output the version number

使用asar extract 命令可以提取ASAR文件; 使用asar pack 命令可以生成ASAR文件;

探索ASAR文件格式

前文提到,ASAR就是档案文件格式,是将多个文件打包放到一个文件里面的;因此可以很合理的猜测:ASAR文件一定有固定的文件结构,用于后续的文件提取。

ASAR整体结构

用记事本打开ASAR文件,可以看见:

  1. 文件头有几个乱码,暂时还不知什么意思;
  2. 之后的文本看起来像是JSON,记录了每个被打进ASAR的文件的文件名,大小和偏移量;
  3. 再之后就每个文件的内容。

image.png

JSON内容作用

将代表JSON内容的n内容复制出来格式化后,可以看到JSON的结构如下:

image-1.png

通过分析,总结这段JSON有以下两个作用:

  1. JSON特殊的树形结构记录了源文件的目录结构;
  2. ASAR对每个资源文件都是扁平化存储的,所以在JSON中记录了每个文件的大小和ASAR中的偏移量,用于后续文件读取;

文件头内容

现在我们只知道在ASAR中存储了一个用于描述源文件的JSON,但是想正确的解析出JSON内容,还必须知道它在ASAR中的偏移量和JSON stringify后的大小。

根据上文对整个ASAR结构的分析,这部分内容一定存储在文件头中。

在 @electron/asar 的源码中,是这么来处理文件头的:

module.exports.readArchiveHeaderSync = function (archive) {
  const fd = fs.openSync(archive, 'r')
  let size
  let headerBuf
  try {
    const sizeBuf = Buffer.alloc(8)
    if (fs.readSync(fd, sizeBuf, 0, 8, null) !== 8) {
      throw new Error('Unable to read header size')
    }

    const sizePickle = pickle.createFromBuffer(sizeBuf)
    size = sizePickle.createIterator().readUInt32()
    headerBuf = Buffer.alloc(size)
    if (fs.readSync(fd, headerBuf, 0, size, null) !== size) {
      throw new Error('Unable to read header')
    }
  } finally {
    fs.closeSync(fd)
  }

  const headerPickle = pickle.createFromBuffer(headerBuf)
  const header = headerPickle.createIterator().readString()
  return { headerString: header, header: JSON.parse(header), headerSize: size }
}

这段代码大概做了:

  1. 读取前8个字节,通过pickle计算出JSON stringify后的大小;
  2. 再根据JSON大小和偏移量读取JSON实际内容;

也就是说:ASAR的前8个字节以一种固定的形式记录JSON的大小; 至于pickle是什么,请移步这里:chromium-pickle-js

最后

至此,ASAR的文件格式已经很清楚了,如下图:

asar.png

ASAR可分为三个部分:文件头,描述JSON,所以源文件内容;其中文件头固定8字节,以pickle形式记录了描述JSON的长度;描述JSON记录了每个源文件的大小和ASAR中的偏移量以及原始文件结构。

到这里,就很好解释为什么ASAR文件并不安全了,它的文件格式是固定的,按照指定格式就能读取源文件内容;

但是将源文件打包成ASAR有个好处就是:能有效解决windows下资源路径的长度限制问题,避免在资源路径过长导致的报错问题。

ASAR文件提取及生成,可以查看源码: @electron/asar