From 07bf488657abb6c6d4c42c11e779305bd9fc59f9 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 15 Aug 2024 22:01:04 +0200 Subject: [PATCH] Added DLL imports on Windows --- src/exe/pe/DLLImport.go | 11 ++++ src/exe/pe/EXE.go | 116 ++++++++++++++++++++++++++-------- src/exe/pe/ImportDirectory.go | 9 --- src/exe/pe/pe.md | 6 +- 4 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 src/exe/pe/DLLImport.go delete mode 100644 src/exe/pe/ImportDirectory.go diff --git a/src/exe/pe/DLLImport.go b/src/exe/pe/DLLImport.go new file mode 100644 index 0000000..d769dd5 --- /dev/null +++ b/src/exe/pe/DLLImport.go @@ -0,0 +1,11 @@ +package pe + +const DLLImportSize = 20 + +type DLLImport struct { + RvaFunctionNameList uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + RvaModuleName uint32 + RvaFunctionAddressList uint32 +} diff --git a/src/exe/pe/EXE.go b/src/exe/pe/EXE.go index 09f96f3..5e520c3 100644 --- a/src/exe/pe/EXE.go +++ b/src/exe/pe/EXE.go @@ -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(dataPadding))) + writer.Write(data) + writer.Write(bytes.Repeat([]byte{0x00}, int(importPadding))) + binary.Write(writer, binary.LittleEndian, &dllImports) + binary.Write(writer, binary.LittleEndian, &dllAddresses) } diff --git a/src/exe/pe/ImportDirectory.go b/src/exe/pe/ImportDirectory.go deleted file mode 100644 index 4857781..0000000 --- a/src/exe/pe/ImportDirectory.go +++ /dev/null @@ -1,9 +0,0 @@ -package pe - -type ImportDirectory struct { - OriginalFirstThunk uint32 - TimeDateStamp uint32 - ForwarderChain uint32 - Name uint32 - FirstThunk uint32 -} diff --git a/src/exe/pe/pe.md b/src/exe/pe/pe.md index 7297fbb..5fc034e 100644 --- a/src/exe/pe/pe.md +++ b/src/exe/pe/pe.md @@ -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