Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/soluwalana/pefile-go

go 1.14

require (
github.com/edsrzf/mmap-go v1.0.0
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 h1:X9xIZ1YU8bLZA3l6gqDUHSFiD0GFI9S548h6C8nDtOY=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
37 changes: 19 additions & 18 deletions pefile-go/pe/parse_export_directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ package pe

import (
"errors"
"../lib"
"fmt"

"github.com/soluwalana/pefile-go/pefile-go/lib"

//"reflect"
"log"
)

/* Parse the export directory.

Given the RVA of the export directory, it will process all
its entries.
Given the RVA of the export directory, it will process all
its entries.

The exports will be made available as a list of ExportData
instances in the ExportDescriptors PE attribute.
The exports will be made available as a list of ExportData
instances in the ExportDescriptors PE attribute.
*/
func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {

exportDir := lib.NewExportDirectory(self.getOffsetFromRva(rva))
start, _ := self.getDataBounds(rva, 0)
if err = self.parseHeader(&exportDir.Data, start, exportDir.Size); err != nil {
Expand All @@ -29,8 +31,7 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
startAddrOfNames, _ := self.getDataBounds(exportDir.Data.AddressOfNames, 0)
startAddrOfOrdinals, _ := self.getDataBounds(exportDir.Data.AddressOfNameOrdinals, 0)
startAddrOfFuncs, _ := self.getDataBounds(exportDir.Data.AddressOfFunctions, 0)



errMsg := "RVA %s in the export directory points to an invalid address: %x"
//maxErrors := 10

Expand All @@ -40,15 +41,15 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
return errors.New(fmt.Sprintf(errMsg, "AddressOfNames", exportDir.Data.AddressOfNames))

}

safetyBoundary := section.Data.VirtualAddress + section.Data.SizeOfRawData - exportDir.Data.AddressOfNames
numNames := Min(safetyBoundary / 4, exportDir.Data.NumberOfNames)
numNames := Min(safetyBoundary/4, exportDir.Data.NumberOfNames)

// A hash set for tracking seen ordinals
ordMap := make(map[uint16]bool)

fmt.Printf("Safety boundary %x, num names %d\n", safetyBoundary, numNames)
for i := uint32(0); i < numNames; i ++ {
for i := uint32(0); i < numNames; i++ {
sym := new(lib.ExportData)

// Name and name offset
Expand All @@ -69,7 +70,7 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
if err = self.parseHeader(&sym.Ordinal, sym.OrdinalOffset, 2); err != nil {
return err
}

// Address
sym.AddressOffset = startAddrOfFuncs + (uint32(sym.Ordinal) * 4)
if err = self.parseHeader(&sym.Address, sym.AddressOffset, 4); err != nil {
Expand All @@ -80,7 +81,7 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
}

// Forwarder if applicable
if sym.Address >= rva && sym.Address < rva + size {
if sym.Address >= rva && sym.Address < rva+size {
sym.Forwarder = self.getStringAtRva(sym.Address)
sym.ForwarderOffset = self.getOffsetFromRva(sym.Address)
}
Expand All @@ -98,11 +99,11 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
return errors.New(fmt.Sprintf(errMsg, "AddressOfFunctions", exportDir.Data.AddressOfFunctions))
}
safetyBoundary = section.Data.VirtualAddress + section.Data.SizeOfRawData - exportDir.Data.AddressOfFunctions
numNames = Min(safetyBoundary / 4, exportDir.Data.NumberOfFunctions)
numNames = Min(safetyBoundary/4, exportDir.Data.NumberOfFunctions)

fmt.Printf("Safety2 boundary %x, num names %d\n", safetyBoundary, numNames)
for i := uint32(0); i < numNames; i ++ {
if _, ok := ordMap[uint16(i + exportDir.Data.Base)]; ok {
for i := uint32(0); i < numNames; i++ {
if _, ok := ordMap[uint16(i+exportDir.Data.Base)]; ok {
continue
}

Expand All @@ -118,7 +119,7 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {
}

// Forwarder if applicable
if sym.Address >= rva && sym.Address < rva + size {
if sym.Address >= rva && sym.Address < rva+size {
sym.Forwarder = self.getStringAtRva(sym.Address)
sym.ForwarderOffset = self.getOffsetFromRva(sym.Address)
}
Expand All @@ -129,4 +130,4 @@ func (self *PEFile) parseExportDirectory(rva, size uint32) (err error) {

}
return nil
}
}
102 changes: 53 additions & 49 deletions pefile-go/pe/parse_import_directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@ package pe

import (
"errors"
"../lib"
"fmt"
"reflect"
"log"
"reflect"

"github.com/soluwalana/pefile-go/pefile-go/lib"
)

/* Parse the import directory.

Given the RVA of the export directory, it will process all
its entries.
Given the RVA of the export directory, it will process all
its entries.

The exports will be made available as a list of ImportData
instances in the ImportDescriptors PE attribute.
The exports will be made available as a list of ImportData
instances in the ImportDescriptors PE attribute.
*/
func (self *PEFile) parseImportDirectory(rva, size uint32) (err error) {
self.ImportDescriptors = make([]*lib.ImportDescriptor, 0)

for {

fileOffset := self.getOffsetFromRva(rva)
importDesc := lib.NewImportDescriptor(fileOffset)

Expand All @@ -38,8 +39,8 @@ func (self *PEFile) parseImportDirectory(rva, size uint32) (err error) {
if lib.EmptyStruct(importDesc.Data) {
break
}
rva += importDesc.Size

rva += importDesc.Size

importDesc.Dll = self.getStringAtRva(importDesc.Data.Name)
if !validDosFilename(importDesc.Dll) {
Expand Down Expand Up @@ -77,7 +78,7 @@ func (self *PEFile) parseImportDirectory(rva, size uint32) (err error) {
return nil
}

/*
/*
Parse the imported symbols.

It will fill a list, which will be available as the dictionary
Expand All @@ -87,14 +88,18 @@ func (self *PEFile) parseImportDirectory(rva, size uint32) (err error) {
func (self *PEFile) parseImports(importDesc *lib.ImportDescriptor) (err error) {
var table []*lib.ThunkData
ilt, err := self.getImportTable(importDesc.Data.Characteristics, importDesc)
if err != nil { return err }
if err != nil {
return err
}
iat, err := self.getImportTable(importDesc.Data.FirstThunk, importDesc)
if err != nil { return err }
if err != nil {
return err
}

if len(iat) == 0 && len(ilt) == 0 {
return errors.New("Invalid Import Table information. Both ILT and IAT appear to be broken.")
}

impOffset := uint32(0x4)
addressMask := uint32(0x7fffffff)
ordinalFlag := IMAGE_ORDINAL_FLAG
Expand All @@ -107,15 +112,15 @@ func (self *PEFile) parseImports(importDesc *lib.ImportDescriptor) (err error) {
table = iat
}

for idx := uint32(0); idx < uint32(len(table)); idx ++ {
for idx := uint32(0); idx < uint32(len(table)); idx++ {
imp := new(lib.ImportData)
imp.StructTable = table[idx]
imp.OrdinalOffset = table[idx].FileOffset

if table[idx].Data.AddressOfData > 0 {

// If imported by ordinal, we will append the ordinal numberx
if table[idx].Data.AddressOfData & ordinalFlag > 0 {
if table[idx].Data.AddressOfData&ordinalFlag > 0 {
imp.ImportByOrdinal = true
imp.Ordinal = table[idx].Data.AddressOfData & uint32(0xffff)
} else {
Expand All @@ -131,42 +136,42 @@ func (self *PEFile) parseImports(importDesc *lib.ImportDescriptor) (err error) {
if !validFuncName(imp.Name) {
imp.Name = INVALID_IMP_NAME
}
imp.NameOffset = self.getOffsetFromRva(table[idx].Data.AddressOfData + 2)
imp.NameOffset = self.getOffsetFromRva(table[idx].Data.AddressOfData + 2)
}
imp.ThunkOffset = table[idx].FileOffset
imp.ThunkRva = self.getRvaFromOffset(imp.ThunkOffset)
}
}

imp.Address = importDesc.Data.FirstThunk + self.OptionalHeader.Data.ImageBase + (idx * impOffset)

if len(iat) > 0 && len(ilt) > 0 && ilt[idx].Data.AddressOfData != iat[idx].Data.AddressOfData {
imp.Bound = iat[idx].Data.AddressOfData
imp.StructIat = iat[idx]
}
hasName := len(imp.Name) > 0
if len(iat) > 0 && len(ilt) > 0 && ilt[idx].Data.AddressOfData != iat[idx].Data.AddressOfData {
imp.Bound = iat[idx].Data.AddressOfData
imp.StructIat = iat[idx]
}

hasName := len(imp.Name) > 0

// The file with hashe:
// SHA256: 3d22f8b001423cb460811ab4f4789f277b35838d45c62ec0454c877e7c82c7f5
// has an invalid table built in a way that it's parseable but contains
// invalid entries
if imp.Ordinal == 0 && !hasName {
return errors.New("Must have either an ordinal or a name in an import")
}
// Some PEs appear to interleave valid and invalid imports. Instead of
// aborting the parsing altogether we will simply skip the invalid entries.
// Although if we see 1000 invalid entries and no legit ones, we abort.
if reflect.DeepEqual(imp.Name, INVALID_IMP_NAME) {
if numInvalid > 1000 && numInvalid == idx {
return errors.New("Too many invalid names, aborting parsing")
}
numInvalid += 1
continue
}

if imp.Ordinal > 0 || hasName {
importDesc.Imports = append(importDesc.Imports, imp)
}
if imp.Ordinal == 0 && !hasName {
return errors.New("Must have either an ordinal or a name in an import")
}
// Some PEs appear to interleave valid and invalid imports. Instead of
// aborting the parsing altogether we will simply skip the invalid entries.
// Although if we see 1000 invalid entries and no legit ones, we abort.
if reflect.DeepEqual(imp.Name, INVALID_IMP_NAME) {
if numInvalid > 1000 && numInvalid == idx {
return errors.New("Too many invalid names, aborting parsing")
}
numInvalid += 1
continue
}

if imp.Ordinal > 0 || hasName {
importDesc.Imports = append(importDesc.Imports, imp)
}
}

return nil
Expand All @@ -189,7 +194,7 @@ func (self *PEFile) getImportTable(rva uint32, importDesc *lib.ImportDescriptor)

maxLen := self.dataLen - importDesc.FileOffset
if rva > importDesc.Data.Characteristics || rva > importDesc.Data.FirstThunk {
maxLen = Max(rva - importDesc.Data.Characteristics, rva - importDesc.Data.FirstThunk)
maxLen = Max(rva-importDesc.Data.Characteristics, rva-importDesc.Data.FirstThunk)
}
lastAddr := rva + maxLen

Expand All @@ -208,7 +213,7 @@ func (self *PEFile) getImportTable(rva uint32, importDesc *lib.ImportDescriptor)
// if the addresses point somewhere but the difference between the highest
// and lowest address is larger than MAX_ADDRESS_SPREAD we assume a bogus
// table as the addresses should be contained within a module
if maxAddressOfData - minAddressOfData > MAX_ADDRESS_SPREAD {
if maxAddressOfData-minAddressOfData > MAX_ADDRESS_SPREAD {
return []*lib.ThunkData{}, errors.New("data addresses too spread out")
}

Expand All @@ -229,23 +234,23 @@ func (self *PEFile) getImportTable(rva uint32, importDesc *lib.ImportDescriptor)
// Seen in PE with SHA256:
// 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c
if thunk.Data.AddressOfData >= startRva && thunk.Data.AddressOfData <= rva {
log.Printf("Error parsing the import table. " +
"AddressOfData overlaps with THUNK_DATA for THUNK at:\n " +
log.Printf("Error parsing the import table. "+
"AddressOfData overlaps with THUNK_DATA for THUNK at:\n "+
"RVA 0x%x", rva)
break
}

if thunk.Data.AddressOfData > 0 {
// If the entry looks like could be an ordinal...
if thunk.Data.AddressOfData & ordinalFlag > 0 {
if thunk.Data.AddressOfData&ordinalFlag > 0 {
// but its value is beyond 2^16, we will assume it's a
// corrupted and ignore it altogether
if thunk.Data.AddressOfData & uint32(0x7fffffff) > uint32(0xffff) {
if thunk.Data.AddressOfData&uint32(0x7fffffff) > uint32(0xffff) {
msg := fmt.Sprintf("Corruption detected in thunk data at 0x%x", rva)
log.Printf(msg)
return []*lib.ThunkData{}, errors.New(msg)
}
// and if it looks like it should be an RVA
// and if it looks like it should be an RVA
} else {
// keep track of the RVAs seen and store them to study their
// properties. When certain non-standard features are detected
Expand Down Expand Up @@ -278,4 +283,3 @@ func (self *PEFile) getImportTable64(rva uint64) []*lib.ThunkData64 {
// todo not implemeted yet
return []*lib.ThunkData64{}
}

Loading