asar/decoder.go

105 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package asar // import "b612.me/asar"
import (
"encoding/binary"
"errors"
"fmt"
"io"
)
var (
errMalformed = errors.New("asar: malformed archive")
)
// DecodeOptions 用于控制解码行为
type DecodeOptions struct {
// StrictValidation 如果为 false会忽略一些格式验证错误和尾部填充
StrictValidation bool
}
// DefaultDecodeOptions 默认选项(严格模式)
var DefaultDecodeOptions = DecodeOptions{
StrictValidation: true,
}
// Decode decodes the ASAR archive in ra.
//
// Returns the root element and nil on success. nil and an error is returned on
// failure.
func Decode(ra io.ReaderAt) (*Entry, error) {
return DecodeWithOptions(ra, DefaultDecodeOptions)
}
// DecodeWithOptions 带选项的解码函数
func DecodeWithOptions(ra io.ReaderAt, opts DecodeOptions) (*Entry, error) {
headerSize := uint32(0)
headerStringSize := uint32(0)
// [pickle object header (4 bytes) == 4]
// [pickle uint32 = $header_object_size]
{
var buff [8]byte
if n, _ := ra.ReadAt(buff[:], 0); n != 8 {
return nil, errMalformed
}
dataSize := binary.LittleEndian.Uint32(buff[:4])
if dataSize != 4 {
return nil, errMalformed
}
headerSize = binary.LittleEndian.Uint32(buff[4:8])
}
// [pickle object header (4 bytes)]
// [pickle data header (4 bytes) == $string_size]
// [pickle string ($string_size bytes)]
{
var buff [8]byte
if n, _ := ra.ReadAt(buff[:], 8); n != 8 {
return nil, errMalformed
}
headerObjectSize := binary.LittleEndian.Uint32(buff[:4])
// 在宽松模式下放宽验证
if opts.StrictValidation {
if headerObjectSize != headerSize-4 {
return nil, errMalformed
}
} else {
// 宽松模式:允许一定的偏差
if headerObjectSize > headerSize || headerObjectSize < headerSize-20 {
return nil, fmt.Errorf("asar: header object size %d is out of reasonable range (header size: %d)",
headerObjectSize, headerSize)
}
}
headerStringSize = binary.LittleEndian.Uint32(buff[4:8])
}
// 在宽松模式下,尝试找到实际的 JSON 结束位置
actualHeaderSize := int64(headerStringSize)
if !opts.StrictValidation && headerStringSize > 0 {
// 读取 header 数据来检测实际的 JSON 结束
tempBuf := make([]byte, headerStringSize)
if n, _ := ra.ReadAt(tempBuf, 16); n > 0 {
// 找到最后一个有效的 JSON 结束位置
if endPos := findJSONEnd(tempBuf); endPos > 0 {
actualHeaderSize = int64(endPos)
}
}
}
// read header string
headerSection := io.NewSectionReader(ra, 8+8, actualHeaderSize)
baseOffset := 8 + int64(headerSize)
baseOffset += baseOffset % 4 // pickle objects are uint32 aligned
root, err := decodeHeaderWithOptions(ra, headerSection, baseOffset, opts)
if err != nil {
return nil, err
}
return root, nil
}