C++ 读取asar中的文件

本文介绍了asar档案格式,主要用于Electron项目的源码隐藏。asar支持随机访问并使用JSON存储文件信息。解析流程包括读取header_size、转换字节到uint32、获取head和json内容,以及遍历json获取文件信息。C++实现asar解析可以借助如rapidjson的库,适用于处理asar中的js、图片和配置文件等。
摘要由CSDN通过智能技术生成

什么是asar

官方说明

asar - Electron Archive
Asar is a simple extensive archive format, it works like tar that concatenates all files together without compression, while having random access support.

Features

Support random access

Use JSON to store files’ information

Very easy to write a parser

意思是说:asar类似于tar只是把所有文件合成一个文件而不进行压缩,所以理论上合成asar之后的总体积是要比原始文件要大的,毕竟还要加上存储文件信息
优点:

  • 支持随机访问
  • 使用JSON存储文件的信息
  • 很容易编写解析器

主要是electron中默认是使用这个来隐藏源码文件

asar文件结构

文件中的数据罗列

| UInt32: header_size | String: header | Bytes: file1 | ... | Bytes: file42 |

解析:
前8个字节表示信息头的长度,然后根据头的长度,去读文档中的结构json串,读取json串之后 按照文件中偏移位置和大小来读取指定文件的内容

文档信息结构json串

{
   
   "files": {
   
      "tmp": {
   
         "files": {
   }
      },
      "usr" : {
   
         "files": {
   
           "bin": {
   
             "files": {
   
               "ls": {
   
                 "offset": "0",
                 "size": 100,
                 "executable": true
               },
               "cd": {
   
                 "offset": "100",
                 "size": 100,
                 "executable": true
               }
             }
           }
         }
      },
      "etc": {
   
         "files": {
   
           "hosts": {
   
             "offset": "200",
             "size": 32
           }
         }
      }
   }
}

nodejs实现原理

//读取asar文件头部信息源码
module.exports.readArchiveHeaderSync = function (archive) {
  const fd = fs.openSync(archive, 'r')
  let size
  let headerBuf
  try {
    const sizeBuf = Buffer.alloc(8)
    //先分配了8个字节读取头部需要的大小
    if (fs.readSync(fd, sizeBuf, 0, 8, null) !== 8) {
      throw new Error('Unable to read header size')
    }

    const sizePickle = pickle.createFromBuffer(sizeBuf)
    //注意:这里是转换成uint32的方式
    size = sizePickle.createIterator().readUInt32()
    headerBuf = Buffer.alloc(size)
    //读取header
    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 { header: JSON.parse(header), headerSize: size }
}

在上面的代码有两个关键就是pickle这个模块的功能,已经怎么吧字节转换成headsize的,我们先看看 readUInt32的源码

//pickle 中代码
var SIZE_INT32 = 4
var SIZE_UINT32 = 4
var SIZE_INT64 = 8
var SIZE_UINT64 = 8
var SIZE_FLOAT = 4
var SIZE_DOUBLE = 8
PickleIterator.prototype.readUInt32 = function () {
    return this.readBytes(SIZE_UINT32, Buffer.prototype.readUInt32LE)
  }

再看看Buffer.prototype.readUInt32LE 说明

用指定的字节序格式(readUInt32BE() 返回大端序, readUInt32LE() 返回小端序)从 buf 中指定的 offset 读取一个无符号的 32 位整数值。

重点在这里是小端序

  • 大端序
    • 数据的高位字节存放在地址的低端 低位字节存放在地址高端
  • 小端序
    • 数据的高位字节存放在地址的高端 低位字节存放在地址低端
      什么意思:大端序是按照数字的书写顺序进行存储的,而小端序是颠倒书写顺序进行存储的。所以这里我们读取8位之后先倒叙,然后再转uint

再看两个源码

PickleIterator.prototype.readString = function ()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值