Improved security

This commit is contained in:
Eduard Urbach 2024-08-14 17:49:07 +02:00
parent 235188e457
commit b1b83201eb
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
11 changed files with 42 additions and 77 deletions

View File

@ -9,9 +9,6 @@ import (
"git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/arch/x64"
"git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/os/common" "git.akyoto.dev/cli/q/src/os/common"
"git.akyoto.dev/cli/q/src/os/linux/elf"
"git.akyoto.dev/cli/q/src/os/mac/macho"
"git.akyoto.dev/cli/q/src/os/windows/pe"
"git.akyoto.dev/cli/q/src/sizeof" "git.akyoto.dev/cli/q/src/sizeof"
) )
@ -341,25 +338,8 @@ restart:
data, dataLabels = a.Data.Finalize() data, dataLabels = a.Data.Finalize()
var ( dataStart := Address(config.BaseAddress) + config.CodeOffset + Address(len(code))
codeOffset Address dataStart += int32(common.Padding(dataStart, config.Align))
align Address
)
switch config.TargetOS {
case "linux":
codeOffset = elf.CodeOffset
align = elf.Align
case "mac":
codeOffset = macho.CodeOffset
align = macho.Align
case "windows":
codeOffset = pe.CodeOffset
align = pe.Align
}
dataStart := Address(config.BaseAddress) + codeOffset + Address(len(code))
dataStart += int32(common.Padding(dataStart, align))
for _, pointer := range dataPointers { for _, pointer := range dataPointers {
address := dataStart + pointer.Resolve() address := dataStart + pointer.Resolve()

View File

@ -8,6 +8,12 @@ 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 decides the alignment of the sections and it must be a multiple of the page size.
Align = 0x1000
// The code offset is the offset of the executable machine code within the file.
CodeOffset = Align
) )
var ( var (

View File

@ -1,6 +1,6 @@
package common package common
// Padding calculates the padding needed to align `n` bytes with the specified alignment. // Padding calculates the padding needed to align `n` bytes with the specified alignment.
func Padding[T int64 | uint64 | int32 | uint32](n T, align T) T { func Padding[T int | uint | int64 | uint64 | int32 | uint32](n T, align T) T {
return align - (n % align) return align - (n % align)
} }

View File

@ -1,13 +1,5 @@
package elf package elf
const (
// Align decides the alignment of the sections and it must be a multiple of the page size.
Align = 0x1000
// The code offset is the offset of the executable machine code within the file.
CodeOffset = HeaderSize + ProgramHeaderSize*2
)
const ( const (
LittleEndian = 1 LittleEndian = 1
TypeExecutable = 2 TypeExecutable = 2

View File

@ -22,8 +22,9 @@ type ELF struct {
// New creates a new ELF binary. // New creates a new ELF binary.
func New(code []byte, data []byte) *ELF { func New(code []byte, data []byte) *ELF {
dataOffset := CodeOffset + int64(len(code)) codePadding := common.Padding(HeaderSize+ProgramHeaderSize*2, config.Align)
dataPadding := common.Padding(dataOffset, Align) dataOffset := config.CodeOffset + int64(len(code))
dataPadding := common.Padding(dataOffset, config.Align)
dataOffset += dataPadding dataOffset += dataPadding
return &ELF{ return &ELF{
@ -37,7 +38,7 @@ func New(code []byte, data []byte) *ELF {
Type: TypeExecutable, Type: TypeExecutable,
Architecture: ArchitectureAMD64, Architecture: ArchitectureAMD64,
FileVersion: 1, FileVersion: 1,
EntryPointInMemory: config.BaseAddress + CodeOffset, EntryPointInMemory: config.BaseAddress + config.CodeOffset,
ProgramHeaderOffset: HeaderSize, ProgramHeaderOffset: HeaderSize,
SectionHeaderOffset: 0, SectionHeaderOffset: 0,
Flags: 0, Flags: 0,
@ -51,12 +52,12 @@ func New(code []byte, data []byte) *ELF {
CodeHeader: ProgramHeader{ CodeHeader: ProgramHeader{
Type: ProgramTypeLOAD, Type: ProgramTypeLOAD,
Flags: ProgramFlagsExecutable | ProgramFlagsReadable, Flags: ProgramFlagsExecutable | ProgramFlagsReadable,
Offset: CodeOffset, Offset: config.CodeOffset,
VirtualAddress: config.BaseAddress + CodeOffset, VirtualAddress: config.BaseAddress + config.CodeOffset,
PhysicalAddress: config.BaseAddress + CodeOffset, PhysicalAddress: config.BaseAddress + config.CodeOffset,
SizeInFile: int64(len(code)), SizeInFile: int64(len(code)),
SizeInMemory: int64(len(code)), SizeInMemory: int64(len(code)),
Align: Align, Align: config.Align,
}, },
DataHeader: ProgramHeader{ DataHeader: ProgramHeader{
Type: ProgramTypeLOAD, Type: ProgramTypeLOAD,
@ -66,9 +67,9 @@ func New(code []byte, data []byte) *ELF {
PhysicalAddress: config.BaseAddress + dataOffset, PhysicalAddress: config.BaseAddress + dataOffset,
SizeInFile: int64(len(data)), SizeInFile: int64(len(data)),
SizeInMemory: int64(len(data)), SizeInMemory: int64(len(data)),
Align: Align, Align: config.Align,
}, },
CodePadding: nil, CodePadding: bytes.Repeat([]byte{0}, int(codePadding)),
Code: code, Code: code,
DataPadding: bytes.Repeat([]byte{0}, int(dataPadding)), DataPadding: bytes.Repeat([]byte{0}, int(dataPadding)),
Data: data, Data: data,

View File

@ -1,13 +1,5 @@
package macho package macho
const (
// Align decides the alignment of the sections and it must be a multiple of the page size.
Align = 0x1000
// The code offset is the offset of the executable machine code within the file.
CodeOffset = 32 + 0x48*3 + 184
)
type CPU uint32 type CPU uint32
const ( const (

View File

@ -52,19 +52,19 @@ func (m *MachO) Write(writer io.Writer) {
InitProt: 0, InitProt: 0,
}) })
codeStart := uint64(32 + m.Header.SizeCommands) codePadding := common.Padding(32+m.Header.SizeCommands, config.Align)
codeLength := uint64(len(m.Code)) codeLength := uint64(len(m.Code))
codeEnd := codeStart + codeLength codeEnd := config.CodeOffset + codeLength
dataPadding := common.Padding(codeEnd, Align) dataPadding := common.Padding(codeEnd, config.Align)
dataStart := codeEnd + dataPadding dataStart := codeEnd + dataPadding
binary.Write(writer, binary.LittleEndian, &Segment64{ binary.Write(writer, binary.LittleEndian, &Segment64{
LoadCommand: LcSegment64, LoadCommand: LcSegment64,
Length: 72, Length: 72,
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'}, Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
Address: config.BaseAddress + codeStart, Address: config.BaseAddress + config.CodeOffset,
SizeInMemory: codeLength, SizeInMemory: codeLength,
Offset: 0, Offset: config.CodeOffset,
SizeInFile: codeLength, SizeInFile: codeLength,
NumSections: 0, NumSections: 0,
Flag: 0, Flag: 0,
@ -110,13 +110,14 @@ func (m *MachO) Write(writer io.Writer) {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
config.BaseAddress + uint32(codeStart), 0, config.BaseAddress + config.CodeOffset, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
}) })
writer.Write(bytes.Repeat([]byte{0}, int(codePadding)))
writer.Write(m.Code) writer.Write(m.Code)
writer.Write(bytes.Repeat([]byte{0}, int(dataPadding))) writer.Write(bytes.Repeat([]byte{0}, int(dataPadding)))
writer.Write(m.Data) writer.Write(m.Data)

View File

@ -1,13 +1,5 @@
package pe package pe
const (
// Align decides the alignment of the sections.
Align = 0x200
// The code offset is the offset of the executable machine code within the file.
CodeOffset = Align
)
// CPU // CPU
const ( const (
IMAGE_FILE_MACHINE_AMD64 = 0x8664 IMAGE_FILE_MACHINE_AMD64 = 0x8664

View File

@ -7,5 +7,5 @@ const DOSHeaderSize = 64
type DOSHeader struct { type DOSHeader struct {
Magic [4]byte Magic [4]byte
_ [56]byte _ [56]byte
PEHeaderOffset uint32 NTHeaderOffset uint32
} }

View File

@ -14,7 +14,7 @@ const NumSections = 2
// EXE is the portable executable format used on Windows. // EXE is the portable executable format used on Windows.
type EXE struct { type EXE struct {
DOSHeader DOSHeader
PEHeader NTHeader
OptionalHeader64 OptionalHeader64
Sections [NumSections]SectionHeader Sections [NumSections]SectionHeader
CodePadding []byte CodePadding []byte
@ -25,23 +25,23 @@ type EXE struct {
// New creates a new EXE file. // New creates a new EXE file.
func New(code []byte, data []byte) *EXE { func New(code []byte, data []byte) *EXE {
codeStart := uint32(DOSHeaderSize + PEHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections) codeStart := uint32(DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections)
codePadding := common.Padding(codeStart, Align) codePadding := common.Padding(codeStart, config.Align)
codeStart += codePadding codeStart += codePadding
dataStart := codeStart + uint32(len(code)) dataStart := codeStart + uint32(len(code))
dataPadding := common.Padding(dataStart, Align) dataPadding := common.Padding(dataStart, config.Align)
dataStart += dataPadding dataStart += dataPadding
imageSize := uint32(dataStart + uint32(len(data))) imageSize := uint32(dataStart + uint32(len(data)))
imageSize += common.Padding(imageSize, Align) imageSize += common.Padding(imageSize, config.Align)
return &EXE{ return &EXE{
DOSHeader: DOSHeader{ DOSHeader: DOSHeader{
Magic: [4]byte{'M', 'Z', 0, 0}, Magic: [4]byte{'M', 'Z', 0, 0},
PEHeaderOffset: 0x40, NTHeaderOffset: 0x40,
}, },
PEHeader: PEHeader{ NTHeader: NTHeader{
Signature: [4]byte{'P', 'E', 0, 0}, Signature: [4]byte{'P', 'E', 0, 0},
Machine: IMAGE_FILE_MACHINE_AMD64, Machine: IMAGE_FILE_MACHINE_AMD64,
NumberOfSections: NumSections, NumberOfSections: NumSections,
@ -54,9 +54,10 @@ func New(code []byte, data []byte) *EXE {
MinorLinkerVersion: 0x16, MinorLinkerVersion: 0x16,
SizeOfCode: uint32(len(code)), SizeOfCode: uint32(len(code)),
AddressOfEntryPoint: codeStart, AddressOfEntryPoint: codeStart,
BaseOfCode: codeStart,
ImageBase: config.BaseAddress, ImageBase: config.BaseAddress,
SectionAlignment: Align, // power of 2, must be greater than or equal to FileAlignment SectionAlignment: config.Align, // power of 2, must be greater than or equal to FileAlignment
FileAlignment: Align, // power of 2 FileAlignment: config.Align, // power of 2
MajorOperatingSystemVersion: 0x06, MajorOperatingSystemVersion: 0x06,
MajorSubsystemVersion: 0x06, MajorSubsystemVersion: 0x06,
SizeOfImage: imageSize, SizeOfImage: imageSize,
@ -89,7 +90,7 @@ func New(code []byte, data []byte) *EXE {
}, },
Sections: [NumSections]SectionHeader{ Sections: [NumSections]SectionHeader{
{ {
Name: [8]byte{'.', 'c', 'o', 'd', 'e'}, Name: [8]byte{'.', 't', 'e', 'x', 't'},
VirtualSize: uint32(len(code)), VirtualSize: uint32(len(code)),
VirtualAddress: codeStart, VirtualAddress: codeStart,
RawSize: uint32(len(code)), // must be a multiple of FileAlignment RawSize: uint32(len(code)), // must be a multiple of FileAlignment
@ -97,7 +98,7 @@ func New(code []byte, data []byte) *EXE {
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{'.', 'd', 'a', 't', 'a'}, Name: [8]byte{'.', 'r', 'd', 'a', 't', 'a'},
VirtualSize: uint32(len(data)), VirtualSize: uint32(len(data)),
VirtualAddress: dataStart, VirtualAddress: dataStart,
RawSize: uint32(len(data)), // must be a multiple of FileAlignment RawSize: uint32(len(data)), // must be a multiple of FileAlignment
@ -115,7 +116,7 @@ func New(code []byte, data []byte) *EXE {
// Write writes the EXE file to the given writer. // Write writes the EXE file to the given writer.
func (pe *EXE) Write(writer io.Writer) { func (pe *EXE) Write(writer io.Writer) {
binary.Write(writer, binary.LittleEndian, &pe.DOSHeader) binary.Write(writer, binary.LittleEndian, &pe.DOSHeader)
binary.Write(writer, binary.LittleEndian, &pe.PEHeader) 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)
binary.Write(writer, binary.LittleEndian, &pe.CodePadding) binary.Write(writer, binary.LittleEndian, &pe.CodePadding)

View File

@ -1,8 +1,8 @@
package pe package pe
const PEHeaderSize = 24 const NTHeaderSize = 24
type PEHeader struct { type NTHeader struct {
Signature [4]byte Signature [4]byte
Machine uint16 Machine uint16
NumberOfSections uint16 NumberOfSections uint16