asar/decoder.go

105 lines
2.7 KiB
Go
Raw Normal View History

2023-02-04 17:52:28 +08:00
package asar // import "b612.me/asar"
import (
"encoding/binary"
"errors"
2025-10-23 21:50:30 +08:00
"fmt"
2023-02-04 17:52:28 +08:00
"io"
)
var (
errMalformed = errors.New("asar: malformed archive")
)
2025-10-23 21:50:30 +08:00
// DecodeOptions 用于控制解码行为
type DecodeOptions struct {
// StrictValidation 如果为 false会忽略一些格式验证错误和尾部填充
StrictValidation bool
}
// DefaultDecodeOptions 默认选项(严格模式)
var DefaultDecodeOptions = DecodeOptions{
StrictValidation: true,
}
2023-02-04 17:52:28 +08:00
// 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) {
2025-10-23 21:50:30 +08:00
return DecodeWithOptions(ra, DefaultDecodeOptions)
}
// DecodeWithOptions 带选项的解码函数
func DecodeWithOptions(ra io.ReaderAt, opts DecodeOptions) (*Entry, error) {
2023-02-04 17:52:28 +08:00
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])
2025-10-23 21:50:30 +08:00
// 在宽松模式下放宽验证
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)
}
2023-02-04 17:52:28 +08:00
}
headerStringSize = binary.LittleEndian.Uint32(buff[4:8])
}
2025-10-23 21:50:30 +08:00
// 在宽松模式下,尝试找到实际的 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)
}
}
}
2023-02-04 17:52:28 +08:00
// read header string
2025-10-23 21:50:30 +08:00
headerSection := io.NewSectionReader(ra, 8+8, actualHeaderSize)
2023-02-04 17:52:28 +08:00
baseOffset := 8 + int64(headerSize)
baseOffset += baseOffset % 4 // pickle objects are uint32 aligned
2025-10-23 21:50:30 +08:00
root, err := decodeHeaderWithOptions(ra, headerSection, baseOffset, opts)
2023-02-04 17:52:28 +08:00
if err != nil {
return nil, err
}
return root, nil
}