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 }