增加解码宽松模式
This commit is contained in:
		
							parent
							
								
									f3062b422e
								
							
						
					
					
						commit
						1d3db9278d
					
				@ -35,7 +35,9 @@ func main() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer file.Close()
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	root, err := asar.Decode(file)
 | 
						root, err := asar.DecodeWithOptions(file, asar.DecodeOptions{
 | 
				
			||||||
 | 
							StrictValidation: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Fprintf(os.Stderr, "asar: %s\n", err)
 | 
							fmt.Fprintf(os.Stderr, "asar: %s\n", err)
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								decoder.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								decoder.go
									
									
									
									
									
								
							@ -3,6 +3,7 @@ package asar // import "b612.me/asar"
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,11 +11,27 @@ var (
 | 
				
			|||||||
	errMalformed = errors.New("asar: malformed archive")
 | 
						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.
 | 
					// Decode decodes the ASAR archive in ra.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Returns the root element and nil on success. nil and an error is returned on
 | 
					// Returns the root element and nil on success. nil and an error is returned on
 | 
				
			||||||
// failure.
 | 
					// failure.
 | 
				
			||||||
func Decode(ra io.ReaderAt) (*Entry, error) {
 | 
					func Decode(ra io.ReaderAt) (*Entry, error) {
 | 
				
			||||||
 | 
						return DecodeWithOptions(ra, DefaultDecodeOptions)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DecodeWithOptions 带选项的解码函数
 | 
				
			||||||
 | 
					func DecodeWithOptions(ra io.ReaderAt, opts DecodeOptions) (*Entry, error) {
 | 
				
			||||||
	headerSize := uint32(0)
 | 
						headerSize := uint32(0)
 | 
				
			||||||
	headerStringSize := uint32(0)
 | 
						headerStringSize := uint32(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,19 +60,42 @@ func Decode(ra io.ReaderAt) (*Entry, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		headerObjectSize := binary.LittleEndian.Uint32(buff[:4])
 | 
							headerObjectSize := binary.LittleEndian.Uint32(buff[:4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 在宽松模式下放宽验证
 | 
				
			||||||
 | 
							if opts.StrictValidation {
 | 
				
			||||||
			if headerObjectSize != headerSize-4 {
 | 
								if headerObjectSize != headerSize-4 {
 | 
				
			||||||
				return nil, errMalformed
 | 
									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])
 | 
							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
 | 
						// read header string
 | 
				
			||||||
	headerSection := io.NewSectionReader(ra, 8+8, int64(headerStringSize))
 | 
						headerSection := io.NewSectionReader(ra, 8+8, actualHeaderSize)
 | 
				
			||||||
	baseOffset := 8 + int64(headerSize)
 | 
						baseOffset := 8 + int64(headerSize)
 | 
				
			||||||
	baseOffset += baseOffset % 4 // pickle objects are uint32 aligned
 | 
						baseOffset += baseOffset % 4 // pickle objects are uint32 aligned
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	root, err := decodeHeader(ra, headerSection, baseOffset)
 | 
						root, err := decodeHeaderWithOptions(ra, headerSection, baseOffset, opts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -89,3 +89,87 @@ func TestExtractThis(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestExtractThisWithOpt(t *testing.T) {
 | 
				
			||||||
 | 
						f, err := os.Open("testdata/extractthis.asar")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						root, err := DecodeWithOptions(f, DecodeOptions{
 | 
				
			||||||
 | 
							StrictValidation: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if root.Flags&FlagDir == 0 {
 | 
				
			||||||
 | 
							t.Fatal("expecting root directory to have FlagDir")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							f1 := root.Find("dir1", "file1.txt")
 | 
				
			||||||
 | 
							if f1 == nil {
 | 
				
			||||||
 | 
								t.Fatal("could not find dir1/file1.txt")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if f1.Path() != "dir1/file1.txt" {
 | 
				
			||||||
 | 
								t.Fatal("unexpected path")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							body, err := ioutil.ReadAll(f1.Open())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !bytes.Equal(body, []byte(`file one.`)) {
 | 
				
			||||||
 | 
								t.Fatalf("dir1/file1.txt body is incorrect (got %s)", body)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							f2 := root.Find("dir2").Find("file3.txt")
 | 
				
			||||||
 | 
							if f2 == nil {
 | 
				
			||||||
 | 
								t.Fatal("could not find dir2/file3.txt")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s := f2.String()
 | 
				
			||||||
 | 
							if s != `123` {
 | 
				
			||||||
 | 
								t.Fatalf("dir2/file3.txt body is incorrect (got %s)", s)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							empty := root.Find("emptyfile.txt")
 | 
				
			||||||
 | 
							if empty == nil {
 | 
				
			||||||
 | 
								t.Fatal("could not find emptyfile.txt")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(empty.Bytes()) != 0 {
 | 
				
			||||||
 | 
								t.Fatal("expecting emptyfile.txt to be empty")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var i int
 | 
				
			||||||
 | 
							root.Walk(func(_ string, _ os.FileInfo, _ error) error {
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if i != 7 {
 | 
				
			||||||
 | 
								t.Fatalf("expected to walk over 7 items, got %d", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							var i int
 | 
				
			||||||
 | 
							root.Walk(func(_ string, fi os.FileInfo, _ error) error {
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								if fi.IsDir() {
 | 
				
			||||||
 | 
									return filepath.SkipDir
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if i != 4 {
 | 
				
			||||||
 | 
								t.Fatalf("expected to walk over 4 items, got %d", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										54
									
								
								header.go
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								header.go
									
									
									
									
									
								
							@ -15,6 +15,7 @@ type jsonReader struct {
 | 
				
			|||||||
	BaseOffset int64
 | 
						BaseOffset int64
 | 
				
			||||||
	D          *json.Decoder
 | 
						D          *json.Decoder
 | 
				
			||||||
	Token      json.Token
 | 
						Token      json.Token
 | 
				
			||||||
 | 
						Options    DecodeOptions // 新增字段
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (j *jsonReader) Peek() json.Token {
 | 
					func (j *jsonReader) Peek() json.Token {
 | 
				
			||||||
@ -128,9 +129,26 @@ func parseRoot(r *jsonReader) *Entry {
 | 
				
			|||||||
	r.ExpectStringVal("files")
 | 
						r.ExpectStringVal("files")
 | 
				
			||||||
	parseFiles(r, entry)
 | 
						parseFiles(r, entry)
 | 
				
			||||||
	r.ExpectDelim('}')
 | 
						r.ExpectDelim('}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 关键修改:根据选项决定是否检查剩余内容
 | 
				
			||||||
 | 
						if r.Options.StrictValidation {
 | 
				
			||||||
 | 
							// 严格模式:不允许有剩余数据
 | 
				
			||||||
		if r.Next() != nil {
 | 
							if r.Next() != nil {
 | 
				
			||||||
			panic(errHeader)
 | 
								panic(errHeader)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 宽松模式:消费并忽略剩余的 token
 | 
				
			||||||
 | 
							// 这些可能是 NUL 填充或其他无关数据
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								token := r.Next()
 | 
				
			||||||
 | 
								if token == nil {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// 可选:如果需要,可以在这里记录日志
 | 
				
			||||||
 | 
								// log.Printf("Ignoring trailing token in ASAR header: %v", token)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return entry
 | 
						return entry
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -159,7 +177,8 @@ func parseEntry(r *jsonReader, parent *Entry) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for !r.HasDelimRune('}') {
 | 
						for !r.HasDelimRune('}') {
 | 
				
			||||||
		switch r.ExpectString() {
 | 
							key := r.ExpectString()
 | 
				
			||||||
 | 
							switch key {
 | 
				
			||||||
		case "files":
 | 
							case "files":
 | 
				
			||||||
			child.Flags |= FlagDir
 | 
								child.Flags |= FlagDir
 | 
				
			||||||
			parseFiles(r, child)
 | 
								parseFiles(r, child)
 | 
				
			||||||
@ -176,7 +195,18 @@ func parseEntry(r *jsonReader, parent *Entry) {
 | 
				
			|||||||
				child.Flags |= FlagExecutable
 | 
									child.Flags |= FlagExecutable
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
 | 
								if r.Options.StrictValidation {
 | 
				
			||||||
 | 
									// 严格模式:未知字段导致错误
 | 
				
			||||||
				panic(errHeader)
 | 
									panic(errHeader)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// 宽松模式:跳过未知字段
 | 
				
			||||||
 | 
									// 需要消费这个未知字段的值
 | 
				
			||||||
 | 
									if token := r.Next(); token == nil {
 | 
				
			||||||
 | 
										panic(errHeader)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// 可选:记录日志
 | 
				
			||||||
 | 
									// log.Printf("Ignoring unknown field '%s' in ASAR entry", key)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -189,13 +219,20 @@ func parseEntry(r *jsonReader, parent *Entry) {
 | 
				
			|||||||
	r.ExpectDelim('}')
 | 
						r.ExpectDelim('}')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 保留原有的 decodeHeader
 | 
				
			||||||
func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (entry *Entry, err error) {
 | 
					func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (entry *Entry, err error) {
 | 
				
			||||||
 | 
						return decodeHeaderWithOptions(asar, header, offset, DefaultDecodeOptions)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 新增带选项的版本
 | 
				
			||||||
 | 
					func decodeHeaderWithOptions(asar io.ReaderAt, header *io.SectionReader, offset int64, opts DecodeOptions) (entry *Entry, err error) {
 | 
				
			||||||
	decoder := json.NewDecoder(header)
 | 
						decoder := json.NewDecoder(header)
 | 
				
			||||||
	decoder.UseNumber()
 | 
						decoder.UseNumber()
 | 
				
			||||||
	reader := jsonReader{
 | 
						reader := jsonReader{
 | 
				
			||||||
		ASAR:       asar,
 | 
							ASAR:       asar,
 | 
				
			||||||
		BaseOffset: offset,
 | 
							BaseOffset: offset,
 | 
				
			||||||
		D:          decoder,
 | 
							D:          decoder,
 | 
				
			||||||
 | 
							Options:    opts, // 传递选项
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@ -205,8 +242,21 @@ func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (ent
 | 
				
			|||||||
				panic(r)
 | 
									panic(r)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	entry = parseRoot(&reader)
 | 
						entry = parseRoot(&reader)
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// findJSONEnd 找到 JSON 的实际结束位置(最后一个有意义的字符)
 | 
				
			||||||
 | 
					func findJSONEnd(data []byte) int {
 | 
				
			||||||
 | 
						// 从后往前找,跳过所有的 NUL 和空白字符
 | 
				
			||||||
 | 
						for i := len(data) - 1; i >= 0; i-- {
 | 
				
			||||||
 | 
							b := data[i]
 | 
				
			||||||
 | 
							// 跳过 NUL 和空白字符
 | 
				
			||||||
 | 
							if b != 0 && b != ' ' && b != '\n' && b != '\r' && b != '\t' {
 | 
				
			||||||
 | 
								// 找到了非空白字符,这应该是 JSON 的结束
 | 
				
			||||||
 | 
								return i + 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return len(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user