From 7b1a293cd065360382b6c6c20a13570a6fd97960 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Tue, 13 Aug 2024 14:07:40 +0200 Subject: [PATCH] Added Windows PE support --- src/asm/Finalize.go | 4 +- src/compiler/Result.go | 39 +++++-- src/os/{linux/elf => common}/Padding.go | 4 +- src/os/linux/elf/Constants.go | 55 +++++++++ src/os/linux/elf/ELF.go | 3 +- src/os/linux/elf/Header.go | 8 +- src/os/linux/elf/ProgramHeader.go | 21 ---- src/os/linux/elf/SectionHeader.go | 27 ----- src/os/mac/macho/CPU.go | 10 -- .../macho/{HeaderFlags.go => Constants.go} | 28 +++++ src/os/mac/macho/HeaderType.go | 12 -- src/os/mac/macho/MachO.go | 4 +- src/os/mac/macho/Prot.go | 9 -- src/os/windows/pe/Constants.go | 72 ++++++++++++ src/os/windows/pe/DOSHeader.go | 9 ++ src/os/windows/pe/DataDirectory.go | 6 + src/os/windows/pe/EXE.go | 105 ++++++++++++++++++ src/os/windows/pe/NTHeader.go | 12 ++ src/os/windows/pe/OptionalHeader64.go | 34 ++++++ src/os/windows/pe/SectionHeader.go | 14 +++ 20 files changed, 373 insertions(+), 103 deletions(-) rename src/os/{linux/elf => common}/Padding.go (57%) create mode 100644 src/os/linux/elf/Constants.go delete mode 100644 src/os/mac/macho/CPU.go rename src/os/mac/macho/{HeaderFlags.go => Constants.go} (73%) delete mode 100644 src/os/mac/macho/HeaderType.go delete mode 100644 src/os/mac/macho/Prot.go create mode 100644 src/os/windows/pe/Constants.go create mode 100644 src/os/windows/pe/DOSHeader.go create mode 100644 src/os/windows/pe/DataDirectory.go create mode 100644 src/os/windows/pe/EXE.go create mode 100644 src/os/windows/pe/NTHeader.go create mode 100644 src/os/windows/pe/OptionalHeader64.go create mode 100644 src/os/windows/pe/SectionHeader.go diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index c59ed54..c32b2fb 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -8,7 +8,7 @@ import ( "git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/config" - "git.akyoto.dev/cli/q/src/os/linux/elf" + "git.akyoto.dev/cli/q/src/os/common" "git.akyoto.dev/cli/q/src/sizeof" ) @@ -338,7 +338,7 @@ restart: data, dataLabels = a.Data.Finalize() dataStart := config.BaseAddress + config.CodeOffset + Address(len(code)) - dataStart += int32(elf.Padding(int64(dataStart), config.Align)) + dataStart += int32(common.Padding(int64(dataStart), config.Align)) for _, pointer := range dataPointers { address := dataStart + pointer.Resolve() diff --git a/src/compiler/Result.go b/src/compiler/Result.go index 18880ae..f21ea29 100644 --- a/src/compiler/Result.go +++ b/src/compiler/Result.go @@ -14,6 +14,7 @@ import ( "git.akyoto.dev/cli/q/src/os/linux/elf" "git.akyoto.dev/cli/q/src/os/mac" "git.akyoto.dev/cli/q/src/os/mac/macho" + "git.akyoto.dev/cli/q/src/os/windows/pe" ) // Result contains all the compiled functions in a build. @@ -35,20 +36,22 @@ func (r *Result) finalize() ([]byte, []byte) { Data: make(map[string][]byte, r.DataCount), } - sysExit := 0 + final.Call("main.main") switch config.TargetOS { case "linux": - sysExit = linux.Exit + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], linux.Exit) + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0) + final.Syscall() case "mac": - sysExit = mac.Exit + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], mac.Exit) + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0) + final.Syscall() + case "windows": + final.RegisterNumber(asm.MOVE, x64.RAX, 0) + final.Return() } - final.Call("main.main") - final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], sysExit) - final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0) - final.Syscall() - // This will place the main function immediately after the entry point // and also add everything the main function calls recursively. r.eachFunction(r.Main, map[*core.Function]bool{}, func(f *core.Function) { @@ -56,9 +59,20 @@ func (r *Result) finalize() ([]byte, []byte) { }) final.Label(asm.LABEL, "_crash") - final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], sysExit) - final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1) - final.Syscall() + + switch config.TargetOS { + case "linux": + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], linux.Exit) + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1) + final.Syscall() + case "mac": + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[0], mac.Exit) + final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1) + final.Syscall() + case "windows": + final.RegisterNumber(asm.MOVE, x64.RAX, 1) + final.Return() + } code, data := final.Finalize() return code, data @@ -138,6 +152,9 @@ func write(writer io.Writer, code []byte, data []byte) error { case "mac": exe := macho.New(code, data) exe.Write(buffer) + case "windows": + exe := pe.New(code, data) + exe.Write(buffer) default: return fmt.Errorf("unsupported platform '%s'", config.TargetOS) } diff --git a/src/os/linux/elf/Padding.go b/src/os/common/Padding.go similarity index 57% rename from src/os/linux/elf/Padding.go rename to src/os/common/Padding.go index e56666a..da40c13 100644 --- a/src/os/linux/elf/Padding.go +++ b/src/os/common/Padding.go @@ -1,6 +1,6 @@ -package elf +package common // Padding calculates the padding needed to align `n` bytes with the specified alignment. -func Padding[T int | int32 | int64 | uint | uint32 | uint64](n T, align T) T { +func Padding[T int64 | uint64 | int32 | uint32](n T, align T) T { return align - (n % align) } diff --git a/src/os/linux/elf/Constants.go b/src/os/linux/elf/Constants.go new file mode 100644 index 0000000..bd4600f --- /dev/null +++ b/src/os/linux/elf/Constants.go @@ -0,0 +1,55 @@ +package elf + +const ( + LittleEndian = 1 + TypeExecutable = 2 + ArchitectureAMD64 = 0x3E +) + +type ProgramType int32 + +const ( + ProgramTypeNULL ProgramType = 0 + ProgramTypeLOAD ProgramType = 1 + ProgramTypeDYNAMIC ProgramType = 2 + ProgramTypeINTERP ProgramType = 3 + ProgramTypeNOTE ProgramType = 4 + ProgramTypeSHLIB ProgramType = 5 + ProgramTypePHDR ProgramType = 6 + ProgramTypeTLS ProgramType = 7 +) + +type ProgramFlags int32 + +const ( + ProgramFlagsExecutable ProgramFlags = 0x1 + ProgramFlagsWritable ProgramFlags = 0x2 + ProgramFlagsReadable ProgramFlags = 0x4 +) + +type SectionType int32 + +const ( + SectionTypeNULL SectionType = 0 + SectionTypePROGBITS SectionType = 1 + SectionTypeSYMTAB SectionType = 2 + SectionTypeSTRTAB SectionType = 3 + SectionTypeRELA SectionType = 4 + SectionTypeHASH SectionType = 5 + SectionTypeDYNAMIC SectionType = 6 + SectionTypeNOTE SectionType = 7 + SectionTypeNOBITS SectionType = 8 + SectionTypeREL SectionType = 9 + SectionTypeSHLIB SectionType = 10 + SectionTypeDYNSYM SectionType = 11 +) + +type SectionFlags int64 + +const ( + SectionFlagsWritable SectionFlags = 1 << 0 + SectionFlagsAllocate SectionFlags = 1 << 1 + SectionFlagsExecutable SectionFlags = 1 << 2 + SectionFlagsStrings SectionFlags = 1 << 5 + SectionFlagsTLS SectionFlags = 1 << 10 +) diff --git a/src/os/linux/elf/ELF.go b/src/os/linux/elf/ELF.go index 2556d1e..fbe2370 100644 --- a/src/os/linux/elf/ELF.go +++ b/src/os/linux/elf/ELF.go @@ -6,6 +6,7 @@ import ( "io" "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/os/common" ) // ELF represents an ELF file. @@ -22,7 +23,7 @@ type ELF struct { // New creates a new ELF binary. func New(code []byte, data []byte) *ELF { dataOffset := config.CodeOffset + int64(len(code)) - dataPadding := Padding(dataOffset, config.Align) + dataPadding := common.Padding(dataOffset, config.Align) dataOffset += dataPadding return &ELF{ diff --git a/src/os/linux/elf/Header.go b/src/os/linux/elf/Header.go index 672f065..9bc7af5 100644 --- a/src/os/linux/elf/Header.go +++ b/src/os/linux/elf/Header.go @@ -1,11 +1,7 @@ package elf -const ( - LittleEndian = 1 - TypeExecutable = 2 - ArchitectureAMD64 = 0x3E - HeaderSize = 64 -) +// HeaderSize is equal to the size of a header in bytes. +const HeaderSize = 64 // Header contains general information. type Header struct { diff --git a/src/os/linux/elf/ProgramHeader.go b/src/os/linux/elf/ProgramHeader.go index cef76c7..7405790 100644 --- a/src/os/linux/elf/ProgramHeader.go +++ b/src/os/linux/elf/ProgramHeader.go @@ -14,24 +14,3 @@ type ProgramHeader struct { SizeInMemory int64 Align int64 } - -type ProgramType int32 - -const ( - ProgramTypeNULL ProgramType = 0 - ProgramTypeLOAD ProgramType = 1 - ProgramTypeDYNAMIC ProgramType = 2 - ProgramTypeINTERP ProgramType = 3 - ProgramTypeNOTE ProgramType = 4 - ProgramTypeSHLIB ProgramType = 5 - ProgramTypePHDR ProgramType = 6 - ProgramTypeTLS ProgramType = 7 -) - -type ProgramFlags int32 - -const ( - ProgramFlagsExecutable ProgramFlags = 0x1 - ProgramFlagsWritable ProgramFlags = 0x2 - ProgramFlagsReadable ProgramFlags = 0x4 -) diff --git a/src/os/linux/elf/SectionHeader.go b/src/os/linux/elf/SectionHeader.go index 4745796..106ab93 100644 --- a/src/os/linux/elf/SectionHeader.go +++ b/src/os/linux/elf/SectionHeader.go @@ -16,30 +16,3 @@ type SectionHeader struct { Align int64 EntrySize int64 } - -type SectionType int32 - -const ( - SectionTypeNULL SectionType = 0 - SectionTypePROGBITS SectionType = 1 - SectionTypeSYMTAB SectionType = 2 - SectionTypeSTRTAB SectionType = 3 - SectionTypeRELA SectionType = 4 - SectionTypeHASH SectionType = 5 - SectionTypeDYNAMIC SectionType = 6 - SectionTypeNOTE SectionType = 7 - SectionTypeNOBITS SectionType = 8 - SectionTypeREL SectionType = 9 - SectionTypeSHLIB SectionType = 10 - SectionTypeDYNSYM SectionType = 11 -) - -type SectionFlags int64 - -const ( - SectionFlagsWritable SectionFlags = 1 << 0 - SectionFlagsAllocate SectionFlags = 1 << 1 - SectionFlagsExecutable SectionFlags = 1 << 2 - SectionFlagsStrings SectionFlags = 1 << 5 - SectionFlagsTLS SectionFlags = 1 << 10 -) diff --git a/src/os/mac/macho/CPU.go b/src/os/mac/macho/CPU.go deleted file mode 100644 index 3888aaa..0000000 --- a/src/os/mac/macho/CPU.go +++ /dev/null @@ -1,10 +0,0 @@ -package macho - -type CPU uint32 - -const ( - CPU_X86 CPU = 7 - CPU_X86_64 CPU = CPU_X86 | 0x01000000 - CPU_ARM CPU = 12 - CPU_ARM_64 CPU = CPU_ARM | 0x01000000 -) diff --git a/src/os/mac/macho/HeaderFlags.go b/src/os/mac/macho/Constants.go similarity index 73% rename from src/os/mac/macho/HeaderFlags.go rename to src/os/mac/macho/Constants.go index 4b17f50..eb9e320 100644 --- a/src/os/mac/macho/HeaderFlags.go +++ b/src/os/mac/macho/Constants.go @@ -1,5 +1,22 @@ package macho +type CPU uint32 + +const ( + CPU_X86 CPU = 7 + CPU_X86_64 CPU = CPU_X86 | 0x01000000 + CPU_ARM CPU = 12 + CPU_ARM_64 CPU = CPU_ARM | 0x01000000 +) + +type Prot uint32 + +const ( + ProtReadable Prot = 0x1 + ProtWritable Prot = 0x2 + ProtExecutable Prot = 0x4 +) + type HeaderFlags uint32 const ( @@ -30,3 +47,14 @@ const ( FlagNoHeapExecution HeaderFlags = 0x1000000 FlagAppExtensionSafe HeaderFlags = 0x2000000 ) + +type HeaderType uint32 + +const ( + TypeObject HeaderType = 0x1 + TypeExecute HeaderType = 0x2 + TypeCore HeaderType = 0x4 + TypeDylib HeaderType = 0x6 + TypeBundle HeaderType = 0x8 + TypeDsym HeaderType = 0xA +) diff --git a/src/os/mac/macho/HeaderType.go b/src/os/mac/macho/HeaderType.go deleted file mode 100644 index f106b97..0000000 --- a/src/os/mac/macho/HeaderType.go +++ /dev/null @@ -1,12 +0,0 @@ -package macho - -type HeaderType uint32 - -const ( - TypeObject HeaderType = 0x1 - TypeExecute HeaderType = 0x2 - TypeCore HeaderType = 0x4 - TypeDylib HeaderType = 0x6 - TypeBundle HeaderType = 0x8 - TypeDsym HeaderType = 0xA -) diff --git a/src/os/mac/macho/MachO.go b/src/os/mac/macho/MachO.go index 41b47ce..806c96d 100644 --- a/src/os/mac/macho/MachO.go +++ b/src/os/mac/macho/MachO.go @@ -6,7 +6,7 @@ import ( "io" "git.akyoto.dev/cli/q/src/config" - "git.akyoto.dev/cli/q/src/os/linux/elf" + "git.akyoto.dev/cli/q/src/os/common" ) // MachO is the executable format used on MacOS. @@ -55,7 +55,7 @@ func (m *MachO) Write(writer io.Writer) { codeStart := uint64(32 + m.Header.SizeCommands) codeLength := uint64(len(m.Code)) codeEnd := codeStart + codeLength - dataPadding := elf.Padding(codeEnd, 4096) + dataPadding := common.Padding(codeEnd, config.Align) dataStart := codeEnd + dataPadding binary.Write(writer, binary.LittleEndian, &Segment64{ diff --git a/src/os/mac/macho/Prot.go b/src/os/mac/macho/Prot.go deleted file mode 100644 index 562b624..0000000 --- a/src/os/mac/macho/Prot.go +++ /dev/null @@ -1,9 +0,0 @@ -package macho - -type Prot uint32 - -const ( - ProtReadable Prot = 0x1 - ProtWritable Prot = 0x2 - ProtExecutable Prot = 0x4 -) diff --git a/src/os/windows/pe/Constants.go b/src/os/windows/pe/Constants.go new file mode 100644 index 0000000..c77bea7 --- /dev/null +++ b/src/os/windows/pe/Constants.go @@ -0,0 +1,72 @@ +package pe + +// CPU +const ( + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_MACHINE_ARM64 = 0xAA64 + IMAGE_FILE_MACHINE_RISCV64 = 0x5064 +) + +// Subsystems +const ( + IMAGE_SUBSYSTEM_UNKNOWN = 0 + IMAGE_SUBSYSTEM_NATIVE = 1 + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 + IMAGE_SUBSYSTEM_OS2_CUI = 5 + IMAGE_SUBSYSTEM_POSIX_CUI = 7 + IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8 + IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9 + IMAGE_SUBSYSTEM_EFI_APPLICATION = 10 + IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11 + IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12 + IMAGE_SUBSYSTEM_EFI_ROM = 13 + IMAGE_SUBSYSTEM_XBOX = 14 + IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16 +) + +// Characteristics +const ( + IMAGE_FILE_RELOCS_STRIPPED = 0x0001 + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 + IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 + IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 + IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 + IMAGE_FILE_32BIT_MACHINE = 0x0100 + IMAGE_FILE_DEBUG_STRIPPED = 0x0200 + IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 + IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 + IMAGE_FILE_SYSTEM = 0x1000 + IMAGE_FILE_DLL = 0x2000 + IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 + IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 +) + +// DLL characteristics +const ( + IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 + IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080 + IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 + IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200 + IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 + IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800 + IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000 + IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000 + IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000 + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 +) + +// Section characteristics +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_LNK_COMDAT = 0x00001000 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 +) diff --git a/src/os/windows/pe/DOSHeader.go b/src/os/windows/pe/DOSHeader.go new file mode 100644 index 0000000..809d8fc --- /dev/null +++ b/src/os/windows/pe/DOSHeader.go @@ -0,0 +1,9 @@ +package pe + +// DOSHeader is at the beginning of each EXE file and nowadays just points +// to the NTHeader using an absolute file offset. +type DOSHeader struct { + Magic [4]byte + _ [56]byte + NTHeaderOffset uint32 +} diff --git a/src/os/windows/pe/DataDirectory.go b/src/os/windows/pe/DataDirectory.go new file mode 100644 index 0000000..8a435b8 --- /dev/null +++ b/src/os/windows/pe/DataDirectory.go @@ -0,0 +1,6 @@ +package pe + +type DataDirectory struct { + VirtualAddress uint32 + Size uint32 +} diff --git a/src/os/windows/pe/EXE.go b/src/os/windows/pe/EXE.go new file mode 100644 index 0000000..13aa5e5 --- /dev/null +++ b/src/os/windows/pe/EXE.go @@ -0,0 +1,105 @@ +package pe + +import ( + "encoding/binary" + "io" + + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/os/common" +) + +// EXE is the portable executable format used on Windows. +type EXE struct { + DOSHeader + NTHeader + OptionalHeader64 + CodeHeader SectionHeader + Code []byte + Data []byte +} + +// New creates a new EXE file. +func New(code []byte, data []byte) *EXE { + const codeStart = 0x170 + const optHeaderSize = 0xF0 + + codeSize := uint32(len(code)) + headerSize := uint32(codeStart) + sectionAlign := uint32(0x10) + fileAlign := uint32(0x10) + imageSize := uint32(codeStart + len(code)) + imageSize += common.Padding(imageSize, sectionAlign) + + return &EXE{ + DOSHeader: DOSHeader{ + Magic: [4]byte{'M', 'Z', 0, 0}, + NTHeaderOffset: 0x40, + }, + NTHeader: NTHeader{ + Signature: [4]byte{'P', 'E', 0, 0}, + Machine: IMAGE_FILE_MACHINE_AMD64, + NumberOfSections: 1, + SizeOfOptionalHeader: optHeaderSize, + Characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE, + }, + OptionalHeader64: OptionalHeader64{ + Magic: 0x020B, // PE32+ executable + MajorLinkerVersion: 0x0E, + MinorLinkerVersion: 0x16, + SizeOfCode: codeSize, + AddressOfEntryPoint: codeStart, + ImageBase: config.BaseAddress, + SectionAlignment: sectionAlign, // power of 2, must be greater than or equal to FileAlignment + FileAlignment: fileAlign, // power of 2 + MajorOperatingSystemVersion: 0x06, + MajorSubsystemVersion: 0x06, + SizeOfImage: imageSize, + SizeOfHeaders: headerSize, + Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI, + 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, + NumberOfRvaAndSizes: 16, + DataDirectory: [16]DataDirectory{ + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + {VirtualAddress: 0, Size: 0}, + }, + }, + CodeHeader: SectionHeader{ + Name: [8]byte{'.', 't', 'e', 'x', 't'}, + VirtualSize: uint32(len(code)), + VirtualAddress: codeStart, + RawSize: uint32(len(code)), // must be a multiple of FileAlignment + RawAddress: codeStart, // must be a multiple of FileAlignment + Characteristics: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, + }, + Code: code, + // Data: data, + } +} + +// Write writes the EXE file to the given writer. +func (pe *EXE) Write(writer io.Writer) { + 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.CodeHeader) + binary.Write(writer, binary.LittleEndian, &pe.Code) + // binary.Write(writer, binary.LittleEndian, &pe.Data) +} diff --git a/src/os/windows/pe/NTHeader.go b/src/os/windows/pe/NTHeader.go new file mode 100644 index 0000000..f563c82 --- /dev/null +++ b/src/os/windows/pe/NTHeader.go @@ -0,0 +1,12 @@ +package pe + +type NTHeader struct { + Signature [4]byte + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} diff --git a/src/os/windows/pe/OptionalHeader64.go b/src/os/windows/pe/OptionalHeader64.go new file mode 100644 index 0000000..9a26a7e --- /dev/null +++ b/src/os/windows/pe/OptionalHeader64.go @@ -0,0 +1,34 @@ +package pe + +type OptionalHeader64 struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uint64 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint64 + SizeOfStackCommit uint64 + SizeOfHeapReserve uint64 + SizeOfHeapCommit uint64 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]DataDirectory +} diff --git a/src/os/windows/pe/SectionHeader.go b/src/os/windows/pe/SectionHeader.go new file mode 100644 index 0000000..8e95e85 --- /dev/null +++ b/src/os/windows/pe/SectionHeader.go @@ -0,0 +1,14 @@ +package pe + +type SectionHeader struct { + Name [8]byte + VirtualSize uint32 + VirtualAddress uint32 + RawSize uint32 + RawAddress uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +}