Flattened package hierarchy

This commit is contained in:
2024-08-25 20:38:22 +02:00
parent 2d8fe15abb
commit b35b17bb32
89 changed files with 42 additions and 42 deletions

View File

@ -1,55 +0,0 @@
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
)

View File

@ -1,82 +0,0 @@
package elf
import (
"bytes"
"encoding/binary"
"io"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/exe"
)
// ELF represents an ELF file.
type ELF struct {
Header
CodeHeader ProgramHeader
DataHeader ProgramHeader
}
// Write writes the ELF64 format to the given writer.
func Write(writer io.Writer, code []byte, data []byte) {
const HeaderEnd = HeaderSize + ProgramHeaderSize*2
var (
codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
)
elf := &ELF{
Header: Header{
Magic: [4]byte{0x7F, 'E', 'L', 'F'},
Class: 2,
Endianness: LittleEndian,
Version: 1,
OSABI: 0,
ABIVersion: 0,
Type: TypeExecutable,
Architecture: ArchitectureAMD64,
FileVersion: 1,
EntryPointInMemory: config.BaseAddress + config.CodeOffset,
ProgramHeaderOffset: HeaderSize,
SectionHeaderOffset: 0,
Flags: 0,
Size: HeaderSize,
ProgramHeaderEntrySize: ProgramHeaderSize,
ProgramHeaderEntryCount: 2,
SectionHeaderEntrySize: 0,
SectionHeaderEntryCount: 0,
SectionNameStringTableIndex: 0,
},
CodeHeader: ProgramHeader{
Type: ProgramTypeLOAD,
Flags: ProgramFlagsExecutable | ProgramFlagsReadable,
Offset: config.CodeOffset,
VirtualAddress: config.BaseAddress + config.CodeOffset,
PhysicalAddress: config.BaseAddress + config.CodeOffset,
SizeInFile: int64(len(code)),
SizeInMemory: int64(len(code)),
Align: config.Align,
},
DataHeader: ProgramHeader{
Type: ProgramTypeLOAD,
Flags: ProgramFlagsReadable,
Offset: int64(dataStart),
VirtualAddress: int64(config.BaseAddress + dataStart),
PhysicalAddress: int64(config.BaseAddress + dataStart),
SizeInFile: int64(len(data)),
SizeInMemory: int64(len(data)),
Align: config.Align,
},
}
binary.Write(writer, binary.LittleEndian, &elf.Header)
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
writer.Write(bytes.Repeat([]byte{0x00}, codePadding))
writer.Write(code)
if len(data) > 0 {
writer.Write(bytes.Repeat([]byte{0x00}, dataPadding))
writer.Write(data)
}
}

View File

@ -1,12 +0,0 @@
package elf_test
import (
"io"
"testing"
"git.akyoto.dev/cli/q/src/exe/elf"
)
func TestWrite(t *testing.T) {
elf.Write(io.Discard, nil, nil)
}

View File

@ -1,28 +0,0 @@
package elf
// HeaderSize is equal to the size of a header in bytes.
const HeaderSize = 64
// Header contains general information.
type Header struct {
Magic [4]byte
Class byte
Endianness byte
Version byte
OSABI byte
ABIVersion byte
_ [7]byte
Type int16
Architecture int16
FileVersion int32
EntryPointInMemory int64
ProgramHeaderOffset int64
SectionHeaderOffset int64
Flags int32
Size int16
ProgramHeaderEntrySize int16
ProgramHeaderEntryCount int16
SectionHeaderEntrySize int16
SectionHeaderEntryCount int16
SectionNameStringTableIndex int16
}

View File

@ -1,16 +0,0 @@
package elf
// ProgramHeaderSize is equal to the size of a program header in bytes.
const ProgramHeaderSize = 56
// ProgramHeader points to the executable part of our program.
type ProgramHeader struct {
Type ProgramType
Flags ProgramFlags
Offset int64
VirtualAddress int64
PhysicalAddress int64
SizeInFile int64
SizeInMemory int64
Align int64
}

View File

@ -1,18 +0,0 @@
package elf
// SectionHeaderSize is equal to the size of a section header in bytes.
const SectionHeaderSize = 64
// SectionHeader points to the data sections of our program.
type SectionHeader struct {
NameIndex int32
Type SectionType
Flags SectionFlags
VirtualAddress int64
Offset int64
SizeInFile int64
Link int32
Info int32
Align int64
EntrySize int64
}

View File

@ -1,38 +0,0 @@
# ELF
## Basic structure
1. ELF header (0x00 - 0x40)
2. Program header (0x40 - 0x78)
3. Padding
4. Machine code
## Entry point
The entry point is defined in the first 64 bytes (ELF header).
## Base address
The minimum base address is controlled by the `mmap` settings:
```shell
sysctl vm.mmap_min_addr
```
Usually, this value is 65536 (0x1000).
## Initialization in Linux
ELF loader:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_elf.c
ELF register definitions:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/elf.h
## Links
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/elf.h
- https://lwn.net/Articles/631631/
- https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
- https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
- https://nathanotterness.com/2021/10/tiny_elf_modernized.html

View File

@ -1,60 +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
)
type Prot uint32
const (
ProtReadable Prot = 0x1
ProtWritable Prot = 0x2
ProtExecutable Prot = 0x4
)
type HeaderFlags uint32
const (
FlagNoUndefs HeaderFlags = 0x1
FlagIncrLink HeaderFlags = 0x2
FlagDyldLink HeaderFlags = 0x4
FlagBindAtLoad HeaderFlags = 0x8
FlagPrebound HeaderFlags = 0x10
FlagSplitSegs HeaderFlags = 0x20
FlagLazyInit HeaderFlags = 0x40
FlagTwoLevel HeaderFlags = 0x80
FlagForceFlat HeaderFlags = 0x100
FlagNoMultiDefs HeaderFlags = 0x200
FlagNoFixPrebinding HeaderFlags = 0x400
FlagPrebindable HeaderFlags = 0x800
FlagAllModsBound HeaderFlags = 0x1000
FlagSubsectionsViaSymbols HeaderFlags = 0x2000
FlagCanonical HeaderFlags = 0x4000
FlagWeakDefines HeaderFlags = 0x8000
FlagBindsToWeak HeaderFlags = 0x10000
FlagAllowStackExecution HeaderFlags = 0x20000
FlagRootSafe HeaderFlags = 0x40000
FlagSetuidSafe HeaderFlags = 0x80000
FlagNoReexportedDylibs HeaderFlags = 0x100000
FlagPIE HeaderFlags = 0x200000
FlagDeadStrippableDylib HeaderFlags = 0x400000
FlagHasTLVDescriptors HeaderFlags = 0x800000
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
)

View File

@ -1,15 +0,0 @@
package macho
const HeaderSize = 32
// Header contains general information.
type Header struct {
Magic uint32
Architecture CPU
MicroArchitecture uint32
Type HeaderType
NumCommands uint32
SizeCommands uint32
Flags HeaderFlags
Reserved uint32
}

View File

@ -1,34 +0,0 @@
package macho
type LoadCommand uint32
const (
LcSegment LoadCommand = 0x1
LcSymtab LoadCommand = 0x2
LcThread LoadCommand = 0x4
LcUnixthread LoadCommand = 0x5
LcDysymtab LoadCommand = 0xB
LcDylib LoadCommand = 0xC
LcIdDylib LoadCommand = 0xD
LcLoadDylinker LoadCommand = 0xE
LcIdDylinker LoadCommand = 0xF
LcSegment64 LoadCommand = 0x19
LcUuid LoadCommand = 0x1B
LcCodeSignature LoadCommand = 0x1D
LcSegmentSplitInfo LoadCommand = 0x1E
LcRpath LoadCommand = 0x8000001C
LcEncryptionInfo LoadCommand = 0x21
LcDyldInfo LoadCommand = 0x22
LcDyldInfoOnly LoadCommand = 0x80000022
LcVersionMinMacosx LoadCommand = 0x24
LcVersionMinIphoneos LoadCommand = 0x25
LcFunctionStarts LoadCommand = 0x26
LcDyldEnvironment LoadCommand = 0x27
LcMain LoadCommand = 0x80000028
LcDataInCode LoadCommand = 0x29
LcSourceVersion LoadCommand = 0x2A
LcDylibCodeSignDrs LoadCommand = 0x2B
LcEncryptionInfo64 LoadCommand = 0x2C
LcVersionMinTvos LoadCommand = 0x2F
LcVersionMinWatchos LoadCommand = 0x30
)

View File

@ -1,124 +0,0 @@
package macho
import (
"bytes"
"encoding/binary"
"io"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/exe"
)
// MachO is the executable format used on MacOS.
type MachO struct {
Header
PageZero Segment64
CodeHeader Segment64
DataHeader Segment64
UnixThread Thread
}
// Write writes the Mach-O format to the given writer.
func Write(writer io.Writer, code []byte, data []byte) {
const (
SizeCommands = Segment64Size*3 + ThreadSize
HeaderEnd = HeaderSize + SizeCommands
)
var (
codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
)
m := &MachO{
Header: Header{
Magic: 0xFEEDFACF,
Architecture: CPU_X86_64,
MicroArchitecture: 3 | 0x80000000,
Type: TypeExecute,
NumCommands: 4,
SizeCommands: SizeCommands,
Flags: FlagNoUndefs | FlagNoHeapExecution,
Reserved: 0,
},
PageZero: Segment64{
LoadCommand: LcSegment64,
Length: 72,
Name: [16]byte{'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E', 'R', 'O'},
Address: 0,
SizeInMemory: config.BaseAddress,
Offset: 0,
SizeInFile: 0,
NumSections: 0,
Flag: 0,
MaxProt: 0,
InitProt: 0,
},
CodeHeader: Segment64{
LoadCommand: LcSegment64,
Length: Segment64Size,
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
Address: config.BaseAddress,
SizeInMemory: uint64(codeStart + len(code)),
Offset: 0,
SizeInFile: uint64(codeStart + len(code)),
NumSections: 0,
Flag: 0,
MaxProt: ProtReadable | ProtExecutable,
InitProt: ProtReadable | ProtExecutable,
},
DataHeader: Segment64{
LoadCommand: LcSegment64,
Length: Segment64Size,
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
Address: uint64(config.BaseAddress + dataStart),
SizeInMemory: uint64(len(data)),
Offset: uint64(dataStart),
SizeInFile: uint64(len(data)),
NumSections: 0,
Flag: 0,
MaxProt: ProtReadable,
InitProt: ProtReadable,
},
UnixThread: Thread{
LoadCommand: LcUnixthread,
Len: ThreadSize,
Type: 0x4,
Data: [43]uint32{
42,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
config.BaseAddress + config.CodeOffset, 0,
0, 0,
0, 0,
0, 0,
0, 0,
},
},
}
binary.Write(writer, binary.LittleEndian, &m.Header)
binary.Write(writer, binary.LittleEndian, &m.PageZero)
binary.Write(writer, binary.LittleEndian, &m.CodeHeader)
binary.Write(writer, binary.LittleEndian, &m.DataHeader)
binary.Write(writer, binary.LittleEndian, &m.UnixThread)
writer.Write(bytes.Repeat([]byte{0x00}, int(codePadding)))
writer.Write(code)
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data)
}

View File

@ -1,12 +0,0 @@
package macho_test
import (
"io"
"testing"
"git.akyoto.dev/cli/q/src/exe/macho"
)
func TestWrite(t *testing.T) {
macho.Write(io.Discard, nil, nil)
}

View File

@ -1,18 +0,0 @@
package macho
const Segment64Size = 72
// Segment64 is a segment load command.
type Segment64 struct {
LoadCommand
Length uint32
Name [16]byte
Address uint64
SizeInMemory uint64
Offset uint64
SizeInFile uint64
MaxProt Prot
InitProt Prot
NumSections uint32
Flag uint32
}

View File

@ -1,11 +0,0 @@
package macho
const ThreadSize = 184
// Thread is a thread state load command.
type Thread struct {
LoadCommand
Len uint32
Type uint32
Data [43]uint32
}

View File

@ -1,16 +0,0 @@
# Mach-O
## Notes
MacOS is the only operating system that requires loading the headers
explicitly with both readable and executable permissions.
## Loader
https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
## Links
- https://en.wikipedia.org/wiki/Mach-O
- https://github.com/aidansteele/osx-abi-macho-file-format-reference
- https://stackoverflow.com/questions/39863112/what-is-required-for-a-mach-o-executable-to-load

View File

@ -1,72 +0,0 @@
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
)

View File

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

View File

@ -1,11 +0,0 @@
package pe
const DOSHeaderSize = 64
// DOSHeader is at the beginning of each EXE file and nowadays just points
// to the PEHeader using an absolute file offset.
type DOSHeader struct {
Magic [4]byte
_ [56]byte
NTHeaderOffset uint32
}

View File

@ -1,6 +0,0 @@
package pe
type DataDirectory struct {
VirtualAddress uint32
Size uint32
}

View File

@ -1,201 +0,0 @@
package pe
import (
"bytes"
"encoding/binary"
"io"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/dll"
"git.akyoto.dev/cli/q/src/exe"
)
// EXE is the portable executable format used on Windows.
type EXE struct {
DOSHeader
NTHeader
OptionalHeader64
Sections []SectionHeader
}
// Write writes the EXE file to the given writer.
func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
if len(data) == 0 {
data = []byte{0}
}
NumSections := 3
HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections
codeStart, codePadding := exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding := exe.Align(codeStart+len(code), config.Align)
importsStart, importsPadding := exe.Align(dataStart+len(data), config.Align)
subSystem := IMAGE_SUBSYSTEM_WINDOWS_CUI
if dlls.Contains("user32") {
subSystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
}
imports := make([]uint64, 0)
dllData := make([]byte, 0)
dllImports := []DLLImport{}
for _, library := range dlls {
functionsStart := len(imports) * 8
dllNamePos := len(dllData)
dllData = append(dllData, library.Name...)
dllData = append(dllData, ".dll"...)
dllData = append(dllData, 0x00)
dllImports = append(dllImports, DLLImport{
RvaFunctionNameList: uint32(importsStart + functionsStart),
TimeDateStamp: 0,
ForwarderChain: 0,
RvaModuleName: uint32(dllNamePos),
RvaFunctionAddressList: uint32(importsStart + functionsStart),
})
for _, fn := range library.Functions {
if len(dllData)&1 != 0 {
dllData = append(dllData, 0x00) // align the next entry on an even boundary
}
offset := len(dllData)
dllData = append(dllData, 0x00, 0x00)
dllData = append(dllData, fn...)
dllData = append(dllData, 0x00)
imports = append(imports, uint64(offset))
}
imports = append(imports, 0)
}
dllDataStart := importsStart + len(imports)*8
for i := range imports {
if imports[i] == 0 {
continue
}
imports[i] += uint64(dllDataStart)
}
for i := range dllImports {
dllImports[i].RvaModuleName += uint32(dllDataStart)
}
dllImports = append(dllImports, DLLImport{}) // a zeroed structure marks the end of the list
importDirectoryStart := dllDataStart + len(dllData)
importDirectorySize := DLLImportSize * len(dllImports)
importSectionSize := len(imports)*8 + len(dllData) + importDirectorySize
imageSize := importsStart + importSectionSize
imageSize, _ = exe.Align(imageSize, config.Align)
pe := &EXE{
DOSHeader: DOSHeader{
Magic: [4]byte{'M', 'Z', 0, 0},
NTHeaderOffset: DOSHeaderSize,
},
NTHeader: NTHeader{
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,
},
OptionalHeader64: OptionalHeader64{
Magic: 0x020B, // PE32+ / 64-bit executable
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: uint16(subSystem),
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, // IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
SizeOfStackReserve: 0x100000,
SizeOfStackCommit: 0x1000,
SizeOfHeapReserve: 0x100000,
SizeOfHeapCommit: 0x1000,
LoaderFlags: 0,
NumberOfRvaAndSizes: 16,
DataDirectory: [16]DataDirectory{
{VirtualAddress: 0, Size: 0},
{VirtualAddress: uint32(importDirectoryStart), Size: uint32(importDirectorySize)}, // RVA of the imported function table
{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: uint32(importsStart), Size: uint32(len(imports) * 8)}, // RVA of the import address table
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0},
},
},
Sections: []SectionHeader{
{
Name: [8]byte{'.', 't', 'e', 'x', 't'},
VirtualSize: uint32(len(code)),
VirtualAddress: config.CodeOffset,
RawSize: uint32(len(code)), // must be a multiple of FileAlignment
RawAddress: config.CodeOffset, // must be a multiple of FileAlignment
Characteristics: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
},
{
Name: [8]byte{'.', 'r', 'd', 'a', 't', 'a'},
VirtualSize: uint32(len(data)),
VirtualAddress: uint32(dataStart),
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(importSectionSize),
VirtualAddress: uint32(importsStart),
RawSize: uint32(importSectionSize),
RawAddress: uint32(importsStart),
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
},
},
}
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)
writer.Write(bytes.Repeat([]byte{0x00}, int(codePadding)))
writer.Write(code)
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data)
writer.Write(bytes.Repeat([]byte{0x00}, int(importsPadding)))
binary.Write(writer, binary.LittleEndian, &imports)
binary.Write(writer, binary.LittleEndian, &dllData)
binary.Write(writer, binary.LittleEndian, &dllImports)
}

View File

@ -1,12 +0,0 @@
package pe_test
import (
"io"
"testing"
"git.akyoto.dev/cli/q/src/exe/pe"
)
func TestWrite(t *testing.T) {
pe.Write(io.Discard, nil, nil, nil)
}

View File

@ -1,14 +0,0 @@
package pe
const NTHeaderSize = 24
type NTHeader struct {
Signature [4]byte
Machine uint16
NumberOfSections uint16
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}

View File

@ -1,36 +0,0 @@
package pe
const OptionalHeader64Size = 240
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
}

View File

@ -1,16 +0,0 @@
package pe
const SectionHeaderSize = 40
type SectionHeader struct {
Name [8]byte
VirtualSize uint32
VirtualAddress uint32
RawSize uint32
RawAddress uint32
PointerToRelocations uint32
PointerToLineNumbers uint32
NumberOfRelocations uint16
NumberOfLineNumbers uint16
Characteristics uint32
}

View File

@ -1,27 +0,0 @@
## Portable Executable
## Notes
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.
The solution used here is to guarantee that the data section is never empty by always
importing a few core functions from "kernel32.dll".
## DLL function pointers
The section where the DLL function pointers are stored does not need to be marked as writable.
The Windows executable loader resolves the pointers before they are loaded into memory.
The stack must be 16 byte aligned before a DLL function is called.
## 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
- https://blog.kowalczyk.info/articles/pefileformat.html
- https://keyj.emphy.de/win32-pe/
- https://corkamiwiki.github.io/PE
- https://github.com/ayaka14732/TinyPE-on-Win10