Added DLL imports on Windows

This commit is contained in:
Eduard Urbach 2024-08-15 22:01:04 +02:00
parent 0e7c66e44f
commit 07bf488657
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
4 changed files with 106 additions and 36 deletions

11
src/exe/pe/DLLImport.go Normal file
View File

@ -0,0 +1,11 @@
package pe
const DLLImportSize = 20
type DLLImport struct {
RvaFunctionNameList uint32
TimeDateStamp uint32
ForwarderChain uint32
RvaModuleName uint32
RvaFunctionAddressList uint32
}

View File

@ -17,27 +17,73 @@ type EXE struct {
Sections []SectionHeader
}
type DLL struct {
Name string
Functions []string
}
// Write writes the EXE file to the given writer.
func Write(writer io.Writer, code []byte, data []byte) {
NumSections := 1
if len(data) != 0 {
NumSections += 1
}
NumSections := 3
HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections
codeStart, codePadding := exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding := exe.Align(codeStart+len(code), config.Align)
var (
codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
)
imageSize := codeStart + len(code)
if len(data) != 0 {
imageSize = dataStart + len(data)
dlls := []DLL{
{
Name: "kernel32.dll",
Functions: []string{
"ExitProcess",
"GetStdHandle",
"WriteFile",
},
},
}
dllAddresses := []uint64{}
dllImports := []DLLImport{}
dllName := len(data)
data = append(data, dlls[0].Name...)
data = append(data, 0x00)
for _, f := range dlls[0].Functions {
pos := len(data)
data = append(data, 0x00, 0x00)
data = append(data, f...)
data = append(data, 0x00)
if len(data)&1 != 0 {
data = append(data, 0x00) // align the next entry on an even boundary
}
dllAddresses = append(dllAddresses, uint64(dataStart+pos))
}
dllAddresses = append(dllAddresses, 0)
importStart, importPadding := exe.Align(dataStart+len(data), config.Align)
itblSize := DLLImportSize*len(dlls) + DLLImportSize
iatblSize := 8 * len(dllAddresses)
itblStart := importStart
iatblStart := itblStart + itblSize
dllImports = append(dllImports, DLLImport{
RvaFunctionNameList: uint32(iatblStart),
TimeDateStamp: 0,
ForwarderChain: 0,
RvaModuleName: uint32(dataStart + dllName),
RvaFunctionAddressList: uint32(iatblStart),
})
dllImports = append(dllImports, DLLImport{
RvaFunctionNameList: 0,
TimeDateStamp: 0,
ForwarderChain: 0,
RvaModuleName: 0, // must be zero
RvaFunctionAddressList: 0,
})
imageSize := iatblStart + iatblSize
imageSize, _ = exe.Align(imageSize, config.Align)
pe := &EXE{
@ -49,6 +95,9 @@ func Write(writer io.Writer, code []byte, data []byte) {
Signature: [4]byte{'P', 'E', 0, 0},
Machine: IMAGE_FILE_MACHINE_AMD64,
NumberOfSections: uint16(NumSections),
TimeDateStamp: 0,
PointerToSymbolTable: 0,
NumberOfSymbols: 0,
SizeOfOptionalHeader: OptionalHeader64Size,
Characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE,
},
@ -57,24 +106,34 @@ func Write(writer io.Writer, code []byte, data []byte) {
MajorLinkerVersion: 0x0E,
MinorLinkerVersion: 0x16,
SizeOfCode: uint32(len(code)),
SizeOfInitializedData: 0,
SizeOfUninitializedData: 0,
AddressOfEntryPoint: config.CodeOffset,
BaseOfCode: config.CodeOffset,
ImageBase: config.BaseAddress,
SectionAlignment: config.Align, // power of 2, must be greater than or equal to FileAlignment
FileAlignment: config.Align, // power of 2
MajorOperatingSystemVersion: 0x06,
MinorOperatingSystemVersion: 0,
MajorImageVersion: 0,
MinorImageVersion: 0,
MajorSubsystemVersion: 0x06,
MinorSubsystemVersion: 0,
Win32VersionValue: 0,
SizeOfImage: uint32(imageSize),
SizeOfHeaders: config.CodeOffset, // section bodies begin here
CheckSum: 0,
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI,
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE,
SizeOfStackReserve: 0x100000,
SizeOfStackCommit: 0x1000,
SizeOfHeapReserve: 0x100000,
SizeOfHeapCommit: 0x1000,
LoaderFlags: 0,
NumberOfRvaAndSizes: 16,
DataDirectory: [16]DataDirectory{
{VirtualAddress: 0, Size: 0},
{VirtualAddress: uint32(itblStart), Size: uint32(itblSize)}, // RVA of the imported function table
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
@ -85,8 +144,7 @@ func Write(writer io.Writer, code []byte, data []byte) {
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: uint32(iatblStart), Size: uint32(iatblSize)}, // RVA of the import address table
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
@ -105,23 +163,31 @@ func Write(writer io.Writer, code []byte, data []byte) {
Name: [8]byte{'.', 'r', 'd', 'a', 't', 'a'},
VirtualSize: uint32(len(data)),
VirtualAddress: uint32(dataStart),
RawSize: uint32(len(data)), // must be a multiple of FileAlignment
RawAddress: uint32(dataStart), // must be a multiple of FileAlignment
RawSize: uint32(len(data)),
RawAddress: uint32(dataStart),
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
},
{
Name: [8]byte{'.', 'i', 'd', 'a', 't', 'a'},
VirtualSize: uint32(iatblSize + itblSize),
VirtualAddress: uint32(importStart),
RawSize: uint32(iatblSize + itblSize),
RawAddress: uint32(importStart),
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
},
},
}
binary.Write(writer, binary.LittleEndian, &pe.DOSHeader)
binary.Write(writer, binary.LittleEndian, &pe.NTHeader)
binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64)
binary.Write(writer, binary.LittleEndian, pe.Sections[:NumSections])
binary.Write(writer, binary.LittleEndian, &pe.Sections)
writer.Write(bytes.Repeat([]byte{0x00}, int(codePadding)))
writer.Write(code)
if len(data) != 0 {
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data)
}
writer.Write(bytes.Repeat([]byte{0x00}, int(importPadding)))
binary.Write(writer, binary.LittleEndian, &dllImports)
binary.Write(writer, binary.LittleEndian, &dllAddresses)
}

View File

@ -1,9 +0,0 @@
package pe
type ImportDirectory struct {
OriginalFirstThunk uint32
TimeDateStamp uint32
ForwarderChain uint32
Name uint32
FirstThunk uint32
}

View File

@ -2,11 +2,13 @@
## Notes
Unlike Linux, Windows does not ignore zero-length sections and will fail loading them because they don't exist within the file.
Adding a single byte to the section can fix this problem, but it's easier to just remove the section header entirely.
Unlike Linux, Windows does not ignore zero-length sections at the end of a file and will
fail loading them because they don't exist within the file. Adding a single byte to the
section can fix this problem, but it's easier to just remove the section header entirely.
## Links
- https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
- https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)
- https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detail
- https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2