From 7976403b133aa22df3ffc39cd7638fea265200ee Mon Sep 17 00:00:00 2001 From: awgh Date: Fri, 19 Jun 2020 23:33:58 -0700 Subject: [PATCH] Added Exports() which returns an array of Export structs describing the exported functions, if there are any. --- pe/exports.go | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 pe/exports.go diff --git a/pe/exports.go b/pe/exports.go new file mode 100644 index 0000000..129d340 --- /dev/null +++ b/pe/exports.go @@ -0,0 +1,119 @@ +package pe + +import ( + "encoding/binary" +) + +// ExportDirectory - data directory definition for exported functions +type ExportDirectory struct { + ExportFlags uint32 // reserved, must be zero + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + NameRVA uint32 // pointer to the name of the DLL + OrdinalBase uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 // also Ordinal Table Len + AddressTableAddr uint32 // RVA of EAT, relative to image base + NameTableAddr uint32 // RVA of export name pointer table, relative to image base + OrdinalTableAddr uint32 // address of the ordinal table, relative to iamge base + + DllName string +} + +// Export - describes a single export entry +type Export struct { + Ordinal uint32 + Name string + VirtualAddress uint32 +} + +// Exports - gets exports +func (f *File) Exports() ([]Export, error) { + pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 + + // grab the number of data directory entries + var ddLength uint32 + if pe64 { + ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes + } else { + ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes + } + + // check that the length of data directory entries is large + // enough to include the exports directory. + if ddLength < IMAGE_DIRECTORY_ENTRY_EXPORT+1 { + return nil, nil + } + + // grab the export data directory entry + var edd DataDirectory + if pe64 { + edd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] + } else { + edd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] + } + + // figure out which section contains the export directory table + var ds *Section + ds = nil + for _, s := range f.Sections { + if s.VirtualAddress <= edd.VirtualAddress && edd.VirtualAddress < s.VirtualAddress+s.VirtualSize { + ds = s + break + } + } + + // didn't find a section, so no exports were found + if ds == nil { + return nil, nil + } + + d, err := ds.Data() + if err != nil { + return nil, err + } + + exportDirOffset := edd.VirtualAddress - ds.VirtualAddress + + // seek to the virtual address specified in the export data directory + dxd := d[exportDirOffset:] + + // deserialize export directory + var dt ExportDirectory + dt.ExportFlags = binary.LittleEndian.Uint32(dxd[0:4]) + dt.TimeDateStamp = binary.LittleEndian.Uint32(dxd[4:8]) + dt.MajorVersion = binary.LittleEndian.Uint16(dxd[8:10]) + dt.MinorVersion = binary.LittleEndian.Uint16(dxd[10:12]) + dt.NameRVA = binary.LittleEndian.Uint32(dxd[12:16]) + dt.OrdinalBase = binary.LittleEndian.Uint32(dxd[16:20]) + dt.NumberOfFunctions = binary.LittleEndian.Uint32(dxd[20:24]) + dt.NumberOfNames = binary.LittleEndian.Uint32(dxd[24:28]) + dt.AddressTableAddr = binary.LittleEndian.Uint32(dxd[28:32]) + dt.NameTableAddr = binary.LittleEndian.Uint32(dxd[32:36]) + dt.OrdinalTableAddr = binary.LittleEndian.Uint32(dxd[36:40]) + + dt.DllName, _ = getString(d, int(dt.NameRVA-ds.VirtualAddress)) + + // seek to address table + dna := d[dt.AddressTableAddr-ds.VirtualAddress:] + // seek to ordinal table + dno := d[dt.OrdinalTableAddr-ds.VirtualAddress:] + // seek to names table + dnn := d[dt.NameTableAddr-ds.VirtualAddress:] + + var exports []Export + var i uint32 + for i = 0; i < dt.NumberOfFunctions; i++ { + var export Export + export.Ordinal = dt.OrdinalBase + + uint32(binary.LittleEndian.Uint16(dno[i*2:(i*2)+2])) + + nameRVA := uint32(binary.LittleEndian.Uint32(dnn[i*4 : (i*4)+4])) + export.Name, _ = getString(d, int(nameRVA-ds.VirtualAddress)) + export.VirtualAddress = + binary.LittleEndian.Uint32(dna[i*4 : (i*4)+4]) + exports = append(exports, export) + } + return exports, nil +}