Flattened package hierarchy
This commit is contained in:
@ -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
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
@ -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
|
||||
)
|
@ -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
|
||||
}
|
@ -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
|
||||
)
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
@ -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
|
||||
)
|
@ -1,11 +0,0 @@
|
||||
package pe
|
||||
|
||||
const DLLImportSize = 20
|
||||
|
||||
type DLLImport struct {
|
||||
RvaFunctionNameList uint32
|
||||
TimeDateStamp uint32
|
||||
ForwarderChain uint32
|
||||
RvaModuleName uint32
|
||||
RvaFunctionAddressList uint32
|
||||
}
|
@ -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
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package pe
|
||||
|
||||
type DataDirectory struct {
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
Reference in New Issue
Block a user