Added exe package to manage sections
This commit is contained in:
@ -3,7 +3,7 @@ package asmc
|
|||||||
import (
|
import (
|
||||||
"git.urbach.dev/cli/q/src/config"
|
"git.urbach.dev/cli/q/src/config"
|
||||||
"git.urbach.dev/cli/q/src/elf"
|
"git.urbach.dev/cli/q/src/elf"
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
"git.urbach.dev/cli/q/src/macho"
|
"git.urbach.dev/cli/q/src/macho"
|
||||||
"git.urbach.dev/cli/q/src/pe"
|
"git.urbach.dev/cli/q/src/pe"
|
||||||
)
|
)
|
||||||
@ -21,6 +21,5 @@ func codeOffset() Address {
|
|||||||
headerEnd = pe.HeaderEnd
|
headerEnd = pe.HeaderEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
offset, _ := fs.Align(headerEnd, config.Align)
|
return exe.Align(headerEnd, config.FileAlign)
|
||||||
return offset
|
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,5 @@ type compiler struct {
|
|||||||
dlls dll.List
|
dlls dll.List
|
||||||
codeStart Address
|
codeStart Address
|
||||||
dataStart Address
|
dataStart Address
|
||||||
|
importsStart Address
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *compiler) dllCall(x asm.Instruction) {
|
func (c *compiler) dllCall(x asm.Instruction) {
|
||||||
size := 4
|
|
||||||
c.code = x86.CallAt(c.code, 0x00_00_00_00)
|
c.code = x86.CallAt(c.code, 0x00_00_00_00)
|
||||||
position := len(c.code) - size
|
next := Address(len(c.code))
|
||||||
|
position := next - 4
|
||||||
label := x.Data.(*asm.Label)
|
label := x.Data.(*asm.Label)
|
||||||
|
|
||||||
pointer := &pointer{
|
pointer := &pointer{
|
||||||
Position: Address(position),
|
Position: Address(position),
|
||||||
OpSize: 2,
|
OpSize: 2,
|
||||||
Size: uint8(size),
|
Size: 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer.Resolve = func() Address {
|
pointer.Resolve = func() Address {
|
||||||
@ -29,7 +29,9 @@ func (c *compiler) dllCall(x asm.Instruction) {
|
|||||||
panic("unknown DLL function " + label.Name)
|
panic("unknown DLL function " + label.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Address(index * 8)
|
destination := c.importsStart + Address(index*8)
|
||||||
|
from := c.codeStart + next
|
||||||
|
return destination - from
|
||||||
}
|
}
|
||||||
|
|
||||||
c.dllPointers = append(c.dllPointers, pointer)
|
c.dllPointers = append(c.dllPointers, pointer)
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/config"
|
"git.urbach.dev/cli/q/src/config"
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
"git.urbach.dev/cli/q/src/sizeof"
|
"git.urbach.dev/cli/q/src/sizeof"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +77,9 @@ restart:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.dataStart, _ = fs.Align(c.codeStart+Address(len(c.code)), config.Align)
|
sections := exe.MakeSections(int(codeOffset()), c.code, c.data, nil)
|
||||||
|
data := sections[1]
|
||||||
|
c.dataStart = Address(data.MemoryOffset)
|
||||||
|
|
||||||
for _, pointer := range c.dataPointers {
|
for _, pointer := range c.dataPointers {
|
||||||
address := pointer.Resolve()
|
address := pointer.Resolve()
|
||||||
@ -86,14 +88,13 @@ restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.TargetOS == config.Windows {
|
if config.TargetOS == config.Windows {
|
||||||
importsStart, _ := fs.Align(c.dataStart+Address(len(c.data)), config.Align)
|
imports := sections[2]
|
||||||
|
c.importsStart = Address(imports.MemoryOffset)
|
||||||
|
|
||||||
for _, pointer := range c.dllPointers {
|
for _, pointer := range c.dllPointers {
|
||||||
destination := importsStart + pointer.Resolve()
|
address := pointer.Resolve()
|
||||||
from := c.codeStart + pointer.Position + Address(pointer.Size)
|
slice := c.code[pointer.Position : pointer.Position+Address(pointer.Size)]
|
||||||
offset := destination - from
|
binary.LittleEndian.PutUint32(slice, uint32(address))
|
||||||
slice := c.code[pointer.Position : pointer.Position+4]
|
|
||||||
binary.LittleEndian.PutUint32(slice, uint32(offset))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ const (
|
|||||||
// The base address is the virtual address for our ELF file.
|
// The base address is the virtual address for our ELF file.
|
||||||
BaseAddress = 0x40 * MinAddress
|
BaseAddress = 0x40 * MinAddress
|
||||||
|
|
||||||
// Align is the alignment of the sections and it must be a multiple of the page size.
|
// FileAlign is the alignment of the sections in the file.
|
||||||
Align = 0x1000
|
FileAlign = 0x4000
|
||||||
|
|
||||||
|
// MemoryAlign is the alignment of the sections in memory and it must be a multiple of the page size.
|
||||||
|
MemoryAlign = 0x4000
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/config"
|
"git.urbach.dev/cli/q/src/config"
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const HeaderEnd = HeaderSize + ProgramHeaderSize*2
|
const HeaderEnd = HeaderSize + ProgramHeaderSize*2
|
||||||
@ -21,19 +21,11 @@ type ELF struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write writes the ELF64 format to the given writer.
|
// Write writes the ELF64 format to the given writer.
|
||||||
func Write(writer io.Writer, code []byte, data []byte) {
|
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) {
|
||||||
var (
|
sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes)
|
||||||
codeStart, codePadding = fs.Align(HeaderEnd, config.Align)
|
code := sections[0]
|
||||||
dataStart, dataPadding = fs.Align(codeStart+len(code), config.Align)
|
data := sections[1]
|
||||||
arch int16
|
arch := cpu()
|
||||||
)
|
|
||||||
|
|
||||||
switch config.TargetArch {
|
|
||||||
case config.ARM:
|
|
||||||
arch = ArchitectureARM64
|
|
||||||
case config.X86:
|
|
||||||
arch = ArchitectureAMD64
|
|
||||||
}
|
|
||||||
|
|
||||||
elf := &ELF{
|
elf := &ELF{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
@ -46,7 +38,7 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
Type: TypeExecutable,
|
Type: TypeExecutable,
|
||||||
Architecture: arch,
|
Architecture: arch,
|
||||||
FileVersion: 1,
|
FileVersion: 1,
|
||||||
EntryPointInMemory: int64(config.BaseAddress + codeStart),
|
EntryPointInMemory: int64(config.BaseAddress + code.MemoryOffset),
|
||||||
ProgramHeaderOffset: HeaderSize,
|
ProgramHeaderOffset: HeaderSize,
|
||||||
SectionHeaderOffset: 0,
|
SectionHeaderOffset: 0,
|
||||||
Flags: 0,
|
Flags: 0,
|
||||||
@ -58,24 +50,22 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
SectionNameStringTableIndex: 0,
|
SectionNameStringTableIndex: 0,
|
||||||
},
|
},
|
||||||
CodeHeader: ProgramHeader{
|
CodeHeader: ProgramHeader{
|
||||||
Type: ProgramTypeLOAD,
|
Type: ProgramTypeLOAD,
|
||||||
Flags: ProgramFlagsExecutable | ProgramFlagsReadable,
|
Flags: ProgramFlagsExecutable | ProgramFlagsReadable,
|
||||||
Offset: int64(codeStart),
|
Offset: int64(code.FileOffset),
|
||||||
VirtualAddress: int64(config.BaseAddress + codeStart),
|
VirtualAddress: int64(config.BaseAddress + code.MemoryOffset),
|
||||||
PhysicalAddress: int64(config.BaseAddress + codeStart),
|
SizeInFile: int64(len(code.Bytes)),
|
||||||
SizeInFile: int64(len(code)),
|
SizeInMemory: int64(len(code.Bytes)),
|
||||||
SizeInMemory: int64(len(code)),
|
Align: config.MemoryAlign,
|
||||||
Align: config.Align,
|
|
||||||
},
|
},
|
||||||
DataHeader: ProgramHeader{
|
DataHeader: ProgramHeader{
|
||||||
Type: ProgramTypeLOAD,
|
Type: ProgramTypeLOAD,
|
||||||
Flags: ProgramFlagsReadable,
|
Flags: ProgramFlagsReadable,
|
||||||
Offset: int64(dataStart),
|
Offset: int64(data.FileOffset),
|
||||||
VirtualAddress: int64(config.BaseAddress + dataStart),
|
VirtualAddress: int64(config.BaseAddress + data.MemoryOffset),
|
||||||
PhysicalAddress: int64(config.BaseAddress + dataStart),
|
SizeInFile: int64(len(data.Bytes)),
|
||||||
SizeInFile: int64(len(data)),
|
SizeInMemory: int64(len(data.Bytes)),
|
||||||
SizeInMemory: int64(len(data)),
|
Align: config.MemoryAlign,
|
||||||
Align: config.Align,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +76,10 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
binary.Write(writer, binary.LittleEndian, &elf.Header)
|
binary.Write(writer, binary.LittleEndian, &elf.Header)
|
||||||
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
|
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
|
||||||
binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
|
binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, codePadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding))
|
||||||
writer.Write(code)
|
writer.Write(code.Bytes)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, dataPadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding))
|
||||||
writer.Write(data)
|
writer.Write(data.Bytes)
|
||||||
|
|
||||||
if config.Sections {
|
if config.Sections {
|
||||||
writer.Write(elf.StringTable)
|
writer.Write(elf.StringTable)
|
||||||
|
15
src/elf/cpu.go
Normal file
15
src/elf/cpu.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package elf
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/config"
|
||||||
|
|
||||||
|
// cpu returns the CPU architecture used in the ELF header.
|
||||||
|
func cpu() int16 {
|
||||||
|
switch config.TargetArch {
|
||||||
|
case config.ARM:
|
||||||
|
return ArchitectureARM64
|
||||||
|
case config.X86:
|
||||||
|
return ArchitectureAMD64
|
||||||
|
default:
|
||||||
|
panic("unknown architecture")
|
||||||
|
}
|
||||||
|
}
|
12
src/exe/Align.go
Normal file
12
src/exe/Align.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package exe
|
||||||
|
|
||||||
|
// Align calculates the next aligned address.
|
||||||
|
func Align[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) T {
|
||||||
|
return (n + (alignment - 1)) & ^(alignment - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlignPad calculates the next aligned address and the padding needed.
|
||||||
|
func AlignPad[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) (T, T) {
|
||||||
|
aligned := Align(n, alignment)
|
||||||
|
return aligned, aligned - n
|
||||||
|
}
|
31
src/exe/Executable.go
Normal file
31
src/exe/Executable.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package exe
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/config"
|
||||||
|
|
||||||
|
// Executable is a generic definition of the binary that later gets translated to OS-specific formats.
|
||||||
|
type Executable []*Section
|
||||||
|
|
||||||
|
// MakeSections creates new sections.
|
||||||
|
func MakeSections(headerEnd int, raw ...[]byte) Executable {
|
||||||
|
sections := make(Executable, len(raw))
|
||||||
|
|
||||||
|
for i, data := range raw {
|
||||||
|
sections[i] = &Section{Bytes: data}
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.Update(headerEnd)
|
||||||
|
return sections
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update recalculates all section offsets.
|
||||||
|
func (sections Executable) Update(headerEnd int) {
|
||||||
|
first := sections[0]
|
||||||
|
first.FileOffset, first.Padding = AlignPad(headerEnd, config.FileAlign)
|
||||||
|
first.MemoryOffset = Align(headerEnd, config.MemoryAlign)
|
||||||
|
|
||||||
|
for i, section := range sections[1:] {
|
||||||
|
previous := sections[i]
|
||||||
|
section.FileOffset, section.Padding = AlignPad(previous.FileOffset+len(previous.Bytes), config.FileAlign)
|
||||||
|
section.MemoryOffset = Align(previous.MemoryOffset+len(previous.Bytes), config.MemoryAlign)
|
||||||
|
}
|
||||||
|
}
|
9
src/exe/Section.go
Normal file
9
src/exe/Section.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package exe
|
||||||
|
|
||||||
|
// Section represents some data within the executable that will also be loaded into memory.
|
||||||
|
type Section struct {
|
||||||
|
Bytes []byte
|
||||||
|
FileOffset int
|
||||||
|
Padding int
|
||||||
|
MemoryOffset int
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
// Align calculates the next aligned address and the padding needed.
|
|
||||||
func Align[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) (T, T) {
|
|
||||||
aligned := (n + (alignment - 1)) & ^(alignment - 1)
|
|
||||||
return aligned, aligned - n
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/config"
|
"git.urbach.dev/cli/q/src/config"
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -24,22 +24,11 @@ type MachO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write writes the Mach-O format to the given writer.
|
// Write writes the Mach-O format to the given writer.
|
||||||
func Write(writer io.Writer, code []byte, data []byte) {
|
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte) {
|
||||||
var (
|
sections := exe.MakeSections(HeaderEnd, codeBytes, dataBytes)
|
||||||
codeStart, codePadding = fs.Align(HeaderEnd, config.Align)
|
code := sections[0]
|
||||||
dataStart, dataPadding = fs.Align(codeStart+len(code), config.Align)
|
data := sections[1]
|
||||||
arch CPU
|
arch, microArch := cpu()
|
||||||
microArch uint32
|
|
||||||
)
|
|
||||||
|
|
||||||
switch config.TargetArch {
|
|
||||||
case config.ARM:
|
|
||||||
arch = CPU_ARM_64
|
|
||||||
microArch = CPU_SUBTYPE_ARM64_ALL | 0x80000000
|
|
||||||
case config.X86:
|
|
||||||
arch = CPU_X86_64
|
|
||||||
microArch = CPU_SUBTYPE_X86_64_ALL | 0x80000000
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &MachO{
|
m := &MachO{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
@ -70,9 +59,9 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
Length: Segment64Size,
|
Length: Segment64Size,
|
||||||
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
|
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
|
||||||
Address: config.BaseAddress,
|
Address: config.BaseAddress,
|
||||||
SizeInMemory: uint64(codeStart + len(code)),
|
SizeInMemory: uint64(code.MemoryOffset + len(code.Bytes)),
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
SizeInFile: uint64(codeStart + len(code)),
|
SizeInFile: uint64(code.FileOffset + len(code.Bytes)),
|
||||||
NumSections: 0,
|
NumSections: 0,
|
||||||
Flag: 0,
|
Flag: 0,
|
||||||
MaxProt: ProtReadable | ProtExecutable,
|
MaxProt: ProtReadable | ProtExecutable,
|
||||||
@ -82,10 +71,10 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
LoadCommand: LcSegment64,
|
LoadCommand: LcSegment64,
|
||||||
Length: Segment64Size,
|
Length: Segment64Size,
|
||||||
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
|
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
|
||||||
Address: uint64(config.BaseAddress + dataStart),
|
Address: uint64(config.BaseAddress + data.MemoryOffset),
|
||||||
SizeInMemory: uint64(len(data)),
|
SizeInMemory: uint64(len(data.Bytes)),
|
||||||
Offset: uint64(dataStart),
|
Offset: uint64(data.FileOffset),
|
||||||
SizeInFile: uint64(len(data)),
|
SizeInFile: uint64(len(data.Bytes)),
|
||||||
NumSections: 0,
|
NumSections: 0,
|
||||||
Flag: 0,
|
Flag: 0,
|
||||||
MaxProt: ProtReadable,
|
MaxProt: ProtReadable,
|
||||||
@ -113,7 +102,7 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
0, 0,
|
0, 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
uint32(config.BaseAddress + codeStart), 0,
|
uint32(config.BaseAddress + code.MemoryOffset), 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
0, 0,
|
0, 0,
|
||||||
@ -127,8 +116,8 @@ func Write(writer io.Writer, code []byte, data []byte) {
|
|||||||
binary.Write(writer, binary.LittleEndian, &m.CodeHeader)
|
binary.Write(writer, binary.LittleEndian, &m.CodeHeader)
|
||||||
binary.Write(writer, binary.LittleEndian, &m.DataHeader)
|
binary.Write(writer, binary.LittleEndian, &m.DataHeader)
|
||||||
binary.Write(writer, binary.LittleEndian, &m.UnixThread)
|
binary.Write(writer, binary.LittleEndian, &m.UnixThread)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, codePadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding))
|
||||||
writer.Write(code)
|
writer.Write(code.Bytes)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, dataPadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding))
|
||||||
writer.Write(data)
|
writer.Write(data.Bytes)
|
||||||
}
|
}
|
||||||
|
15
src/macho/cpu.go
Normal file
15
src/macho/cpu.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package macho
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/config"
|
||||||
|
|
||||||
|
// cpu returns the CPU architecture used in the Mach-O header.
|
||||||
|
func cpu() (CPU, uint32) {
|
||||||
|
switch config.TargetArch {
|
||||||
|
case config.ARM:
|
||||||
|
return CPU_ARM_64, CPU_SUBTYPE_ARM64_ALL | 0x80000000
|
||||||
|
case config.X86:
|
||||||
|
return CPU_X86_64, CPU_SUBTYPE_X86_64_ALL | 0x80000000
|
||||||
|
default:
|
||||||
|
panic("unknown architecture")
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"git.urbach.dev/cli/q/src/config"
|
"git.urbach.dev/cli/q/src/config"
|
||||||
"git.urbach.dev/cli/q/src/dll"
|
"git.urbach.dev/cli/q/src/dll"
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/exe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -24,31 +24,30 @@ type EXE struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write writes the EXE file to the given writer.
|
// Write writes the EXE file to the given writer.
|
||||||
func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
func Write(writer io.Writer, codeBytes []byte, dataBytes []byte, dlls dll.List) {
|
||||||
var (
|
var (
|
||||||
codeStart, codePadding = fs.Align(HeaderEnd, config.Align)
|
sections = exe.MakeSections(HeaderEnd, codeBytes, dataBytes, nil)
|
||||||
dataStart, dataPadding = fs.Align(codeStart+len(code), config.Align)
|
code = sections[0]
|
||||||
importsStart, importsPadding = fs.Align(dataStart+len(data), config.Align)
|
data = sections[1]
|
||||||
subSystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
|
imports = sections[2]
|
||||||
imports, dllData, dllImports, dllDataStart = importLibraries(dlls, importsStart)
|
subSystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||||
importDirectoryStart = dllDataStart + len(dllData)
|
arch = cpu()
|
||||||
importDirectorySize = DLLImportSize * len(dllImports)
|
|
||||||
importSectionSize = len(imports)*8 + len(dllData) + importDirectorySize
|
|
||||||
imageSize, _ = fs.Align(importsStart+importSectionSize, config.Align)
|
|
||||||
arch uint16
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
importsData, dllData, dllImports, dllDataStart := importLibraries(dlls, imports.FileOffset)
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
binary.Write(&buffer, binary.LittleEndian, &importsData)
|
||||||
|
binary.Write(&buffer, binary.LittleEndian, &dllData)
|
||||||
|
binary.Write(&buffer, binary.LittleEndian, &dllImports)
|
||||||
|
imports.Bytes = buffer.Bytes()
|
||||||
|
importDirectoryStart := dllDataStart + len(dllData)
|
||||||
|
importDirectorySize := DLLImportSize * len(dllImports)
|
||||||
|
imageSize := exe.Align(imports.MemoryOffset+len(imports.Bytes), config.MemoryAlign)
|
||||||
|
|
||||||
if dlls.Contains("user32") {
|
if dlls.Contains("user32") {
|
||||||
subSystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
|
subSystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.TargetArch {
|
|
||||||
case config.ARM:
|
|
||||||
arch = IMAGE_FILE_MACHINE_ARM64
|
|
||||||
case config.X86:
|
|
||||||
arch = IMAGE_FILE_MACHINE_AMD64
|
|
||||||
}
|
|
||||||
|
|
||||||
pe := &EXE{
|
pe := &EXE{
|
||||||
DOSHeader: DOSHeader{
|
DOSHeader: DOSHeader{
|
||||||
Magic: [4]byte{'M', 'Z', 0, 0},
|
Magic: [4]byte{'M', 'Z', 0, 0},
|
||||||
@ -68,14 +67,14 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
Magic: 0x020B, // PE32+ / 64-bit executable
|
Magic: 0x020B, // PE32+ / 64-bit executable
|
||||||
MajorLinkerVersion: 0x0E,
|
MajorLinkerVersion: 0x0E,
|
||||||
MinorLinkerVersion: 0x16,
|
MinorLinkerVersion: 0x16,
|
||||||
SizeOfCode: uint32(len(code)),
|
SizeOfCode: uint32(len(code.Bytes)),
|
||||||
SizeOfInitializedData: 0,
|
SizeOfInitializedData: 0,
|
||||||
SizeOfUninitializedData: 0,
|
SizeOfUninitializedData: 0,
|
||||||
AddressOfEntryPoint: uint32(codeStart),
|
AddressOfEntryPoint: uint32(code.MemoryOffset),
|
||||||
BaseOfCode: uint32(codeStart),
|
BaseOfCode: uint32(code.MemoryOffset),
|
||||||
ImageBase: config.BaseAddress,
|
ImageBase: config.BaseAddress,
|
||||||
SectionAlignment: config.Align, // power of 2, must be greater than or equal to FileAlignment
|
SectionAlignment: config.MemoryAlign, // power of 2, must be greater than or equal to FileAlignment
|
||||||
FileAlignment: config.Align, // power of 2
|
FileAlignment: config.FileAlign, // power of 2
|
||||||
MajorOperatingSystemVersion: 0x06,
|
MajorOperatingSystemVersion: 0x06,
|
||||||
MinorOperatingSystemVersion: 0,
|
MinorOperatingSystemVersion: 0,
|
||||||
MajorImageVersion: 0,
|
MajorImageVersion: 0,
|
||||||
@ -83,8 +82,8 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
MajorSubsystemVersion: 0x06,
|
MajorSubsystemVersion: 0x06,
|
||||||
MinorSubsystemVersion: 0,
|
MinorSubsystemVersion: 0,
|
||||||
Win32VersionValue: 0,
|
Win32VersionValue: 0,
|
||||||
SizeOfImage: uint32(imageSize),
|
SizeOfImage: uint32(imageSize), // a multiple of SectionAlignment
|
||||||
SizeOfHeaders: uint32(codeStart), // section bodies begin here
|
SizeOfHeaders: uint32(code.FileOffset), // section bodies begin here
|
||||||
CheckSum: 0,
|
CheckSum: 0,
|
||||||
Subsystem: uint16(subSystem),
|
Subsystem: uint16(subSystem),
|
||||||
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, // IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, // IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
||||||
@ -107,7 +106,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
{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: uint32(imports.MemoryOffset), Size: uint32(len(importsData) * 8)}, // RVA of the import address 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},
|
||||||
@ -116,26 +115,26 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
Sections: []SectionHeader{
|
Sections: []SectionHeader{
|
||||||
{
|
{
|
||||||
Name: [8]byte{'.', 't', 'e', 'x', 't'},
|
Name: [8]byte{'.', 't', 'e', 'x', 't'},
|
||||||
VirtualSize: uint32(len(code)),
|
VirtualSize: uint32(len(code.Bytes)),
|
||||||
VirtualAddress: uint32(codeStart),
|
VirtualAddress: uint32(code.MemoryOffset),
|
||||||
RawSize: uint32(len(code)), // must be a multiple of FileAlignment
|
RawSize: uint32(len(code.Bytes)), // must be a multiple of FileAlignment
|
||||||
RawAddress: uint32(codeStart), // must be a multiple of FileAlignment
|
RawAddress: uint32(code.FileOffset), // must be a multiple of FileAlignment
|
||||||
Characteristics: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
|
Characteristics: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: [8]byte{'.', 'r', 'd', 'a', 't', 'a'},
|
Name: [8]byte{'.', 'r', 'd', 'a', 't', 'a'},
|
||||||
VirtualSize: uint32(len(data)),
|
VirtualSize: uint32(len(data.Bytes)),
|
||||||
VirtualAddress: uint32(dataStart),
|
VirtualAddress: uint32(data.MemoryOffset),
|
||||||
RawSize: uint32(len(data)),
|
RawSize: uint32(len(data.Bytes)),
|
||||||
RawAddress: uint32(dataStart),
|
RawAddress: uint32(data.FileOffset),
|
||||||
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
|
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: [8]byte{'.', 'i', 'd', 'a', 't', 'a'},
|
Name: [8]byte{'.', 'i', 'd', 'a', 't', 'a'},
|
||||||
VirtualSize: uint32(importSectionSize),
|
VirtualSize: uint32(len(imports.Bytes)),
|
||||||
VirtualAddress: uint32(importsStart),
|
VirtualAddress: uint32(imports.MemoryOffset),
|
||||||
RawSize: uint32(importSectionSize),
|
RawSize: uint32(len(imports.Bytes)),
|
||||||
RawAddress: uint32(importsStart),
|
RawAddress: uint32(imports.FileOffset),
|
||||||
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
|
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -145,12 +144,10 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
binary.Write(writer, binary.LittleEndian, &pe.NTHeader)
|
binary.Write(writer, binary.LittleEndian, &pe.NTHeader)
|
||||||
binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64)
|
binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64)
|
||||||
binary.Write(writer, binary.LittleEndian, &pe.Sections)
|
binary.Write(writer, binary.LittleEndian, &pe.Sections)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, codePadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, code.Padding))
|
||||||
writer.Write(code)
|
writer.Write(code.Bytes)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, dataPadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, data.Padding))
|
||||||
writer.Write(data)
|
writer.Write(data.Bytes)
|
||||||
writer.Write(bytes.Repeat([]byte{0x00}, importsPadding))
|
writer.Write(bytes.Repeat([]byte{0x00}, imports.Padding))
|
||||||
binary.Write(writer, binary.LittleEndian, &imports)
|
writer.Write(imports.Bytes)
|
||||||
binary.Write(writer, binary.LittleEndian, &dllData)
|
|
||||||
binary.Write(writer, binary.LittleEndian, &dllImports)
|
|
||||||
}
|
}
|
||||||
|
15
src/pe/cpu.go
Normal file
15
src/pe/cpu.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package pe
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/config"
|
||||||
|
|
||||||
|
// cpu returns the CPU architecture used in the PE header.
|
||||||
|
func cpu() uint16 {
|
||||||
|
switch config.TargetArch {
|
||||||
|
case config.ARM:
|
||||||
|
return IMAGE_FILE_MACHINE_ARM64
|
||||||
|
case config.X86:
|
||||||
|
return IMAGE_FILE_MACHINE_AMD64
|
||||||
|
default:
|
||||||
|
panic("unknown architecture")
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
- [data](data) - Data container that can re-use existing data (e.g. the `Hello` in `Hello World`)
|
- [data](data) - Data container that can re-use existing data (e.g. the `Hello` in `Hello World`)
|
||||||
- [dll](dll) - DLL support for Windows systems (w.i.p.)
|
- [dll](dll) - DLL support for Windows systems (w.i.p.)
|
||||||
- [elf](elf) - ELF format for Linux executables
|
- [elf](elf) - ELF format for Linux executables
|
||||||
|
- [exe](exe) - Generic executable format to calculate section offsets
|
||||||
- [errors](errors) - Error types
|
- [errors](errors) - Error types
|
||||||
- [eval](eval) - Evaluates expressions
|
- [eval](eval) - Evaluates expressions
|
||||||
- [expression](expression) - Expression parser generating trees with the `Parse` function
|
- [expression](expression) - Expression parser generating trees with the `Parse` function
|
||||||
|
Reference in New Issue
Block a user