From 18266f54af20d1bc213b6b896d855f0910b50772 Mon Sep 17 00:00:00 2001 From: Stephen Eckels Date: Tue, 25 Oct 2022 14:47:24 -0400 Subject: [PATCH] Fix sec size checks failing for themida unpacked binaries --- main.go | 76 ++++++++++++++++++++++++++-------------------- objfile/disasm.go | 4 +-- objfile/elf.go | 13 +++++--- objfile/macho.go | 14 ++++++--- objfile/objfile.go | 53 ++++++++++++++------------------ objfile/pe.go | 18 ++++++----- 6 files changed, 95 insertions(+), 83 deletions(-) diff --git a/main.go b/main.go index 61fe9f8..387d10a 100644 --- a/main.go +++ b/main.go @@ -168,49 +168,59 @@ func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTyp } } - tab, tabva, err := file.PCLineTable(versionOverride) + tabs, err := file.PCLineTable(versionOverride) if err != nil { return ExtractMetadata{}, fmt.Errorf("failed to read pclntab: %w", err) } - if tab.Go12line == nil { - log.Fatalf("pclntab read, but is nil") - return ExtractMetadata{}, fmt.Errorf("read pclntab, but parsing failed. The file may not be fully unpacked or corrupted: %w", err) + if len(tabs) == 0 { + return ExtractMetadata{}, fmt.Errorf("No pclntab candidates found") } - if len(versionOverride) > 0 { - extractMetadata.Version = versionOverride - } + var moduleData *objfile.ModuleData = nil + var finalTab *objfile.PclntabCandidate = &tabs[0] + for _, tab := range tabs { + if len(versionOverride) > 0 { + extractMetadata.Version = versionOverride + } - // numeric only, go1.17 -> 1.17 - goVersionIdx := strings.Index(extractMetadata.Version, "go") - if goVersionIdx != -1 { - // "devel go1.18-2d1d548 Tue Dec 21 03:55:43 2021 +0000" - extractMetadata.Version = strings.Split(extractMetadata.Version[goVersionIdx+2:]+" ", " ")[0] + // numeric only, go1.17 -> 1.17 + goVersionIdx := strings.Index(extractMetadata.Version, "go") + if goVersionIdx != -1 { + // "devel go1.18-2d1d548 Tue Dec 21 03:55:43 2021 +0000" + extractMetadata.Version = strings.Split(extractMetadata.Version[goVersionIdx+2:]+" ", " ")[0] - // go1.18-2d1d548 - extractMetadata.Version = strings.Split(extractMetadata.Version+"-", "-")[0] - } + // go1.18-2d1d548 + extractMetadata.Version = strings.Split(extractMetadata.Version+"-", "-")[0] + } - extractMetadata.TabMeta.CpuQuantum = tab.Go12line.Quantum + extractMetadata.TabMeta.CpuQuantum = tab.ParsedPclntab.Go12line.Quantum - // quantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). - // 386: 1, amd64: 1, arm: 4, arm64: 4, mips: 4, mips/64/64le/64be: 4, ppc64/64le: 4, riscv64: 4, s390x: 2, wasm: 1 - extractMetadata.TabMeta.CpuQuantumStr = "x86/x64/wasm" - if extractMetadata.TabMeta.CpuQuantum == 2 { - extractMetadata.TabMeta.CpuQuantumStr = "s390x" - } else if extractMetadata.TabMeta.CpuQuantum == 4 { - extractMetadata.TabMeta.CpuQuantumStr = "arm/mips/ppc/riscv" - } + // quantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). + // 386: 1, amd64: 1, arm: 4, arm64: 4, mips: 4, mips/64/64le/64be: 4, ppc64/64le: 4, riscv64: 4, s390x: 2, wasm: 1 + extractMetadata.TabMeta.CpuQuantumStr = "x86/x64/wasm" + if extractMetadata.TabMeta.CpuQuantum == 2 { + extractMetadata.TabMeta.CpuQuantumStr = "s390x" + } else if extractMetadata.TabMeta.CpuQuantum == 4 { + extractMetadata.TabMeta.CpuQuantumStr = "arm/mips/ppc/riscv" + } - extractMetadata.TabMeta.VA = tabva - extractMetadata.TabMeta.Version = tab.Go12line.Version.String() - extractMetadata.TabMeta.Endianess = tab.Go12line.Binary.String() - extractMetadata.TabMeta.PointerSize = tab.Go12line.Ptrsize + extractMetadata.TabMeta.VA = tab.PclntabVA + extractMetadata.TabMeta.Version = tab.ParsedPclntab.Go12line.Version.String() + extractMetadata.TabMeta.Endianess = tab.ParsedPclntab.Go12line.Binary.String() + extractMetadata.TabMeta.PointerSize = tab.ParsedPclntab.Go12line.Ptrsize + + // this can be a little tricky to locate and parse properly across all go versions + // since moduledata holds a pointer to the pclntab, we can (hopefully) find the right candidate by using it to find the moduledata. + // if that location works, then we must have given it the correct pclntab VA. At least in theory... + _, tmpModData, err := file.ModuleDataTable(tab.PclntabVA, extractMetadata.Version, extractMetadata.TabMeta.Version, extractMetadata.TabMeta.PointerSize == 8, extractMetadata.TabMeta.Endianess == "LittleEndian") + if err == nil && tmpModData != nil { + finalTab = &tab + moduleData = tmpModData + } + } - // this can be a little tricky to locate and parse properly across all go versions - _, moduleData, err := file.ModuleDataTable(tabva, extractMetadata.Version, extractMetadata.TabMeta.Version, extractMetadata.TabMeta.PointerSize == 8, extractMetadata.TabMeta.Endianess == "LittleEndian") - if err == nil { + if moduleData != nil { extractMetadata.ModuleMeta = *moduleData if printTypes && manualTypeAddress == 0 { types, err := file.ParseTypeLinks(extractMetadata.Version, moduleData, extractMetadata.TabMeta.PointerSize == 8, extractMetadata.TabMeta.Endianess == "LittleEndian") @@ -231,12 +241,12 @@ func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTyp } if printFilePaths { - for k := range tab.Files { + for k := range finalTab.ParsedPclntab.Files { extractMetadata.Files = append(extractMetadata.Files, k) } } - for _, elem := range tab.Funcs { + for _, elem := range finalTab.ParsedPclntab.Funcs { if isStdPackage(elem.PackageName()) { if printStdPkgs { extractMetadata.StdFunctions = append(extractMetadata.StdFunctions, FuncMetadata{ diff --git a/objfile/disasm.go b/objfile/disasm.go index f0cb6be..1c081a6 100644 --- a/objfile/disasm.go +++ b/objfile/disasm.go @@ -49,7 +49,7 @@ func (e *Entry) Disasm() (*Disasm, error) { return nil, err } - pcln, _, err := e.PCLineTable("") + pclns, err := e.PCLineTable("") if err != nil { return nil, err } @@ -79,7 +79,7 @@ func (e *Entry) Disasm() (*Disasm, error) { syms = keep d := &Disasm{ syms: syms, - pcln: pcln, + pcln: pclns[0].ParsedPclntab, text: textBytes, textStart: textStart, textEnd: textStart + uint64(len(textBytes)), diff --git a/objfile/elf.go b/objfile/elf.go index ef2ebac..62f7c80 100644 --- a/objfile/elf.go +++ b/objfile/elf.go @@ -116,8 +116,10 @@ ExitScan: pclntab = data[pclntab_idx:] var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = uint64(sec.Addr) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + + candidate.SecStart = uint64(sec.Addr) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) // we must scan all signature for all sections. DO NOT BREAK @@ -128,8 +130,9 @@ ExitScan: pclntab_idx := bytes.Index(data, pclntab) if pclntab_idx != -1 && pclntab_idx < int(sec.Size) { var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = uint64(sec.Addr) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + candidate.SecStart = uint64(sec.Addr) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) break ExitScan @@ -154,7 +157,7 @@ func (f *elfFile) pcln() (candidates []PclntabCandidate, err error) { if err == nil { for _, c := range candidates { - c.symtab = symtab + c.Symtab = symtab } } diff --git a/objfile/macho.go b/objfile/macho.go index 1633675..3e755c4 100644 --- a/objfile/macho.go +++ b/objfile/macho.go @@ -134,8 +134,10 @@ ExitScan: pclntab = data[pclntab_idx:] var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = uint64(sec.Addr) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + + candidate.SecStart = uint64(sec.Addr) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) // we must scan all signature for all sections. DO NOT BREAK @@ -146,8 +148,10 @@ ExitScan: pclntab_idx := bytes.Index(data, pclntab) if pclntab_idx != -1 { var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = uint64(sec.Addr) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + + candidate.SecStart = uint64(sec.Addr) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) @@ -173,7 +177,7 @@ func (f *machoFile) pcln() (candidates []PclntabCandidate, err error) { if err == nil { for _, c := range candidates { - c.symtab = symtab + c.Symtab = symtab } } diff --git a/objfile/objfile.go b/objfile/objfile.go index a011781..723606e 100644 --- a/objfile/objfile.go +++ b/objfile/objfile.go @@ -23,9 +23,11 @@ import ( ) type PclntabCandidate struct { - pclntabVA uint64 - pclntab []byte - symtab []byte // optional + SecStart uint64 + PclntabVA uint64 + Pclntab []byte + Symtab []byte // optional + ParsedPclntab *gosym.Table } type rawFile interface { @@ -111,7 +113,7 @@ func (f *File) Symbols() ([]Sym, error) { } // previously : func (f *File) PCLineTable() (Liner, error) { -func (f *File) PCLineTable(versionOverride string) (*gosym.Table, uint64, error) { +func (f *File) PCLineTable(versionOverride string) ([]PclntabCandidate, error) { return f.entries[0].PCLineTable(versionOverride) } @@ -188,11 +190,11 @@ func findAllOccurrences(data []byte, searches [][]byte) []int { } // previously: func (e *Entry) PCLineTable() (Liner, error) -func (e *Entry) PCLineTable(versionOverride string) (*gosym.Table, uint64, error) { +func (e *Entry) PCLineTable(versionOverride string) ([]PclntabCandidate, error) { // If the raw file implements Liner directly, use that. // Currently, only Go intermediate objects and archives (goobj) use this path. - // PATCH: DISABLED, We want to gopclntab table 95% of the time + // FEYE PATCH: DISABLED, We want to gopclntab table 95% of the time // if pcln, ok := e.raw.(Liner); ok { // return pcln, nil // } @@ -202,39 +204,28 @@ func (e *Entry) PCLineTable(versionOverride string) (*gosym.Table, uint64, error // https://github.com/golang/go/issues/42954 candidates, err := e.raw.pcln() if err != nil { - return nil, 0, err - } - - // try to resolve via symbol - textStart := uint64(0) - syms, err := e.raw.symbols() - if err == nil { - for _, s := range syms { - if s.Name == "runtime.text" { - textStart = s.Addr - break - } - } - } - - // that may have failed, use section base directly - if textStart == 0 { - secBase, _, err := e.Text() - if err == nil { - textStart = secBase - } + return nil, err } + var finalCandidates []PclntabCandidate + var atLeastOneGood bool = false for _, candidate := range candidates { - table, err := gosym.NewTable(candidate.symtab, gosym.NewLineTable(candidate.pclntab, textStart), versionOverride) - if err != nil || table.Go12line == nil { + parsedTable, err := gosym.NewTable(candidate.Symtab, gosym.NewLineTable(candidate.Pclntab, candidate.SecStart), versionOverride) + if err != nil || parsedTable.Go12line == nil { continue } - return table, candidate.pclntabVA, nil + // the first good one happens to be correct more often than the last + candidate.ParsedPclntab = parsedTable + finalCandidates = append(finalCandidates, candidate) + atLeastOneGood = true + } + + if atLeastOneGood { + return finalCandidates, nil } - return nil, 0, fmt.Errorf("failed to locate pclntab") + return finalCandidates, fmt.Errorf("failed to locate pclntab") } func (e *Entry) ModuleDataTable(pclntabVA uint64, runtimeVersion string, version string, is64bit bool, littleendian bool) (secStart uint64, moduleData *ModuleData, err error) { diff --git a/objfile/pe.go b/objfile/pe.go index b40d58a..8e90736 100644 --- a/objfile/pe.go +++ b/objfile/pe.go @@ -165,12 +165,14 @@ ExitScan: []byte("\xFF\xFF\xFF\xFB\x00\x00"), []byte("\xFF\xFF\xFF\xFA\x00\x00"), []byte("\xFF\xFF\xFF\xF0\x00\x00")} matches := findAllOccurrences(data, pclntab_sigs) for _, pclntab_idx := range matches { - if pclntab_idx != -1 && pclntab_idx < int(sec.Size) { + if pclntab_idx != -1 { pclntab = data[pclntab_idx:] var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = imageBase + uint64(sec.VirtualAddress) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + + candidate.SecStart = imageBase + uint64(sec.VirtualAddress) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) // we must scan all signature for all sections. DO NOT BREAK @@ -179,10 +181,12 @@ ExitScan: } else { // 3) if we found it earlier, figure out which section base to return (might be wrong for packed things) pclntab_idx := bytes.Index(data, pclntab) - if pclntab_idx != -1 && pclntab_idx < int(sec.Size) { + if pclntab_idx != -1 { var candidate PclntabCandidate - candidate.pclntab = pclntab - candidate.pclntabVA = imageBase + uint64(sec.VirtualAddress) + uint64(pclntab_idx) + candidate.Pclntab = pclntab + + candidate.SecStart = imageBase + uint64(sec.VirtualAddress) + candidate.PclntabVA = candidate.SecStart + uint64(pclntab_idx) candidates = append(candidates, candidate) break ExitScan @@ -207,7 +211,7 @@ func (f *peFile) pcln() (candidates []PclntabCandidate, err error) { if err == nil { for _, c := range candidates { - c.symtab = symtab + c.Symtab = symtab } }