Skip to content

Commit

Permalink
Fix sec size checks failing for themida unpacked binaries
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Eckels committed Oct 25, 2022
1 parent a8ca5aa commit 18266f5
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 83 deletions.
76 changes: 43 additions & 33 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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{
Expand Down
4 changes: 2 additions & 2 deletions objfile/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)),
Expand Down
13 changes: 8 additions & 5 deletions objfile/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
}
}

Expand Down
14 changes: 9 additions & 5 deletions objfile/macho.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
}
}

Expand Down
53 changes: 22 additions & 31 deletions objfile/objfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
// }
Expand All @@ -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) {
Expand Down
18 changes: 11 additions & 7 deletions objfile/pe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
}
}

Expand Down

0 comments on commit 18266f5

Please sign in to comment.