224 lines
5.5 KiB
Go
224 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"debug/pe"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"unsafe"
|
|
)
|
|
|
|
func CreateDefinitionFile(content []byte, tempDir string) (string, error) {
|
|
reader := bytes.NewReader(content)
|
|
file, err := pe.NewFile(reader)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to open DLL: %v", err)
|
|
}
|
|
defer func(file *pe.File) {
|
|
_ = file.Close()
|
|
}(file)
|
|
|
|
exports, dllName, err := getExports(file)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to read exports: %v", err)
|
|
}
|
|
if len(exports) == 0 {
|
|
return "", fmt.Errorf("No exports found in DLL")
|
|
}
|
|
|
|
baseName := strings.TrimSuffix(dllName, filepath.Ext(dllName))
|
|
defFileName := tempDir + "/" + baseName + ".def"
|
|
defFile, err := os.Create(defFileName)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to create def file: %v", err)
|
|
}
|
|
defer func(defFile *os.File) {
|
|
_ = defFile.Close()
|
|
}(defFile)
|
|
|
|
_, _ = fmt.Fprintf(defFile, "EXPORTS\n")
|
|
for i, exportName := range exports {
|
|
_, _ = fmt.Fprintf(defFile, "%s=%s.%s @%v\n", exportName, baseName, exportName, i+1)
|
|
}
|
|
|
|
return defFileName, nil
|
|
}
|
|
|
|
type ExportDirectory struct {
|
|
ExportFlags uint32
|
|
TimeDateStamp uint32
|
|
MajorVersion uint16
|
|
MinorVersion uint16
|
|
NameRVA uint32
|
|
OrdinalBase uint32
|
|
NumberOfFunctions uint32
|
|
NumberOfNames uint32
|
|
AddressTableRVA uint32
|
|
NamePointerRVA uint32
|
|
OrdinalTableRVA uint32
|
|
}
|
|
|
|
func getDLLName(file *pe.File, exportDir *ExportDirectory) (string, error) {
|
|
nameSection := findSectionByRVA(file, exportDir.NameRVA)
|
|
if nameSection == nil {
|
|
return "", fmt.Errorf("dll name section not found")
|
|
}
|
|
|
|
data, err := nameSection.Data()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read dll name section: %v", err)
|
|
}
|
|
|
|
offset := exportDir.NameRVA - nameSection.VirtualAddress
|
|
if offset >= uint32(len(data)) {
|
|
return "", fmt.Errorf("dll name offset out of bounds")
|
|
}
|
|
|
|
nameBytes := data[offset:]
|
|
for i, b := range nameBytes {
|
|
if b == 0 {
|
|
return string(nameBytes[:i]), nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("dll name not null-terminated")
|
|
}
|
|
|
|
func getExports(file *pe.File) ([]string, string, error) {
|
|
var exports []string
|
|
|
|
// Get the export directory from the data directories
|
|
if file.OptionalHeader == nil {
|
|
return nil, "", fmt.Errorf("no optional header found")
|
|
}
|
|
|
|
var exportDirRVA, exportDirSize uint32
|
|
|
|
switch oh := file.OptionalHeader.(type) {
|
|
case *pe.OptionalHeader32:
|
|
if len(oh.DataDirectory) > 0 {
|
|
exportDirRVA = oh.DataDirectory[0].VirtualAddress
|
|
exportDirSize = oh.DataDirectory[0].Size
|
|
}
|
|
case *pe.OptionalHeader64:
|
|
if len(oh.DataDirectory) > 0 {
|
|
exportDirRVA = oh.DataDirectory[0].VirtualAddress
|
|
exportDirSize = oh.DataDirectory[0].Size
|
|
}
|
|
default:
|
|
return nil, "", fmt.Errorf("unsupported optional header type")
|
|
}
|
|
|
|
if exportDirRVA == 0 || exportDirSize == 0 {
|
|
return nil, "", fmt.Errorf("no export directory found")
|
|
}
|
|
|
|
// Find the section containing the export directory
|
|
var section *pe.Section
|
|
for _, s := range file.Sections {
|
|
if exportDirRVA >= s.VirtualAddress && exportDirRVA < s.VirtualAddress+s.VirtualSize {
|
|
section = s
|
|
break
|
|
}
|
|
}
|
|
|
|
if section == nil {
|
|
return nil, "", fmt.Errorf("export directory not found in any section")
|
|
}
|
|
|
|
// Read section data
|
|
data, err := section.Data()
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("failed to read section data: %v", err)
|
|
}
|
|
|
|
// Calculate offset within section
|
|
offset := exportDirRVA - section.VirtualAddress
|
|
if offset >= uint32(len(data)) {
|
|
return nil, "", fmt.Errorf("export directory offset out of bounds")
|
|
}
|
|
|
|
// Parse export directory
|
|
if len(data[offset:]) < int(unsafe.Sizeof(ExportDirectory{})) {
|
|
return nil, "", fmt.Errorf("insufficient data for export directory")
|
|
}
|
|
|
|
exportDir := (*ExportDirectory)(unsafe.Pointer(&data[offset]))
|
|
|
|
dllName, err := getDLLName(file, exportDir)
|
|
if err != nil {
|
|
return nil, dllName, fmt.Errorf("DLL name not found")
|
|
}
|
|
|
|
if exportDir.NumberOfNames == 0 {
|
|
return nil, dllName, fmt.Errorf("no named exports found")
|
|
}
|
|
|
|
// Read name pointer table
|
|
nameTableRVA := exportDir.NamePointerRVA
|
|
nameTableSection := findSectionByRVA(file, nameTableRVA)
|
|
if nameTableSection == nil {
|
|
return nil, dllName, fmt.Errorf("name table section not found")
|
|
}
|
|
|
|
nameTableData, err := nameTableSection.Data()
|
|
if err != nil {
|
|
return nil, dllName, fmt.Errorf("failed to read name table section: %v", err)
|
|
}
|
|
|
|
nameTableOffset := nameTableRVA - nameTableSection.VirtualAddress
|
|
|
|
// Read each name RVA and then the actual name
|
|
for i := uint32(0); i < exportDir.NumberOfNames; i++ {
|
|
nameRVAOffset := nameTableOffset + i*4
|
|
if nameRVAOffset+4 > uint32(len(nameTableData)) {
|
|
break
|
|
}
|
|
|
|
nameRVA := binary.LittleEndian.Uint32(nameTableData[nameRVAOffset:])
|
|
|
|
// Find section containing the name
|
|
nameSection := findSectionByRVA(file, nameRVA)
|
|
if nameSection == nil {
|
|
continue
|
|
}
|
|
|
|
nameSectionData, err := nameSection.Data()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
nameOffset := nameRVA - nameSection.VirtualAddress
|
|
if nameOffset >= uint32(len(nameSectionData)) {
|
|
continue
|
|
}
|
|
|
|
// Read null-terminated string
|
|
nameBytes := nameSectionData[nameOffset:]
|
|
var name string
|
|
for j, b := range nameBytes {
|
|
if b == 0 {
|
|
name = string(nameBytes[:j])
|
|
break
|
|
}
|
|
}
|
|
|
|
if name != "" {
|
|
exports = append(exports, name)
|
|
}
|
|
}
|
|
|
|
return exports, dllName, nil
|
|
}
|
|
|
|
func findSectionByRVA(file *pe.File, rva uint32) *pe.Section {
|
|
for _, section := range file.Sections {
|
|
if rva >= section.VirtualAddress && rva < section.VirtualAddress+section.VirtualSize {
|
|
return section
|
|
}
|
|
}
|
|
return nil
|
|
}
|