pe.go (149 lines of code) (raw):

// Copyright ©2022 Elastic N.V. All rights reserved. // Copyright ©2021 Dan Kortschak. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package toutoumomoma import ( "bytes" "debug/gosym" "debug/pe" "fmt" "io" "path" "strings" ) type peFile struct { r io.ReaderAt objFile *pe.File } func openPE(r io.ReaderAt) (*peFile, error) { objFile, err := pe.NewFile(r) if err != nil { return nil, err } return &peFile{r: r, objFile: objFile}, nil } func (f *peFile) Close() error { f.objFile = nil if c, ok := f.r.(io.Closer); ok { return c.Close() } return nil } func (f *peFile) isGoExecutable() (ok bool, err error) { // TODO(kortschak): Investigate whether there is a better // heuristic or a definitive test for this case. sect := f.objFile.Section(".rdata") if sect == nil { return false, nil } rdata, err := sect.Data() if err != nil { return false, err } return bytes.Contains(rdata, []byte("runtime.g")), nil } func (f *peFile) hasBuildID() (ok bool, err error) { for _, s := range f.objFile.Symbols { if s.Name == "go.buildid" || s.Name == "go:buildid" { return true, nil } } return false, nil } func (f *peFile) hasRealFiles() (ok bool, err error) { tab, err := f.pclnTable() if err != nil { return false, err } if len(f.objFile.Symbols) == 0 { return false, nil } for _, sym := range f.objFile.Symbols { if sym.Name != "main.main" { continue } file, _, _ := tab.PCToLine(uint64(sym.Value)) if file == "??" { return false, nil } } return true, nil } func (f *peFile) importedSymbols() ([]string, error) { imports, err := f.objFile.ImportedSymbols() if err != nil { return nil, err } for i, imp := range imports { imports[i], err = canonicaliseImport(imp) if err != nil { return nil, err } } return imports, nil } func canonicaliseImport(imp string) (string, error) { parts := strings.SplitN(strings.ToLower(imp), ":", 3) if len(parts) != 2 { return "", fmt.Errorf("invalid import symbol: %q", imp) } lib := strings.TrimSuffix(parts[1], path.Ext(parts[1])) fn := parts[0] return lib + "." + fn, nil } func (f *peFile) goSymbols(stdlib bool) ([]string, error) { tab, err := f.pclnTable() if err != nil { return nil, err } imports := make([]string, 0, len(f.objFile.Symbols)) for _, sym := range f.objFile.Symbols { // https://wiki.osdev.org/COFF#Symbol_Table const ( N_UNDEF = 0 N_ABS = -1 N_DEBUG = -2 ) switch sym.SectionNumber { case N_UNDEF, N_ABS, N_DEBUG: continue } if sym.SectionNumber < 0 || len(f.objFile.Sections) < int(sym.SectionNumber) { return nil, fmt.Errorf("invalid section number in symbol table") } const STYP_TEXT = 0x20 // https://wiki.osdev.org/COFF#Section_Header if f.objFile.Sections[sym.SectionNumber-1].Characteristics&STYP_TEXT == 0 { continue } if strings.HasPrefix(sym.Name, "type..") { continue } if !stdlib && isStdlib(sym.Name, uint64(sym.Value), tab) { continue } imports = append(imports, sym.Name) } if len(imports) == 0 { imports = nil } return imports, nil } func (f *peFile) pclnTable() (*gosym.Table, error) { textStart, symtab, pclntab, err := f.pcln() if err != nil { return nil, nil } return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart)) } func (f *peFile) sectionStats() ([]Section, error) { s := make([]Section, len(f.objFile.Sections)) for i, sect := range f.objFile.Sections { h, sigma, err := streamEntropy(sect.Open()) if err != nil { return nil, err } s[i] = Section{ Name: sect.Name, Size: uint64(sect.Size), FileSize: uint64(sect.Size), Entropy: h, VarEntropy: sigma, Flags: sect.Characteristics, } } return s, nil }