Improved Windows support

This commit is contained in:
Eduard Urbach 2024-08-18 13:29:44 +02:00
parent 3fa3ff9227
commit 0db54ff639
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
6 changed files with 127 additions and 63 deletions

View File

@ -8,12 +8,13 @@ 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/dll"
"git.akyoto.dev/cli/q/src/exe" "git.akyoto.dev/cli/q/src/exe"
"git.akyoto.dev/cli/q/src/sizeof" "git.akyoto.dev/cli/q/src/sizeof"
) )
// Finalize generates the final machine code. // Finalize generates the final machine code.
func (a Assembler) Finalize() ([]byte, []byte) { func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) {
var ( var (
code = make([]byte, 0, len(a.Instructions)*8) code = make([]byte, 0, len(a.Instructions)*8)
data []byte data []byte
@ -21,6 +22,7 @@ func (a Assembler) Finalize() ([]byte, []byte) {
dataLabels map[string]Address dataLabels map[string]Address
codePointers []*Pointer codePointers []*Pointer
dataPointers []*Pointer dataPointers []*Pointer
dllPointers []*Pointer
) )
for _, x := range a.Instructions { for _, x := range a.Instructions {
@ -114,7 +116,7 @@ func (a Assembler) Finalize() ([]byte, []byte) {
case CALL_AT: case CALL_AT:
code = x64.CallAtAddress(code, 0x00_00_00_00) code = x64.CallAtAddress(code, 0x00_00_00_00)
size := 4 size := 4
// label := x.Data.(*Label) label := x.Data.(*Label)
pointer := &Pointer{ pointer := &Pointer{
Position: Address(len(code) - size), Position: Address(len(code) - size),
@ -123,12 +125,11 @@ func (a Assembler) Finalize() ([]byte, []byte) {
} }
pointer.Resolve = func() Address { pointer.Resolve = func() Address {
destination := Address(0x1038) index := dlls.Index("kernel32.dll", label.Name)
distance := destination - (pointer.Position + Address(pointer.Size)) return Address(index * 8)
return Address(distance)
} }
codePointers = append(codePointers, pointer) dllPointers = append(dllPointers, pointer)
case COMMENT: case COMMENT:
continue continue
@ -365,5 +366,12 @@ restart:
binary.LittleEndian.PutUint32(slice, uint32(address)) binary.LittleEndian.PutUint32(slice, uint32(address))
} }
for _, pointer := range dllPointers {
destination := Address(0x3000) + pointer.Resolve()
address := destination - Address(config.CodeOffset+pointer.Position+Address(pointer.Size))
slice := code[pointer.Position : pointer.Position+4]
binary.LittleEndian.PutUint32(slice, uint32(address))
}
return code, data return code, data
} }

View File

@ -77,7 +77,7 @@ func (r *Result) finalize() ([]byte, []byte) {
final.Label(asm.CALL_AT, "ExitProcess") final.Label(asm.CALL_AT, "ExitProcess")
} }
code, data := final.Finalize() code, data := final.Finalize(windows.DLLs)
return code, data return code, data
} }
@ -154,24 +154,7 @@ func write(writer io.Writer, code []byte, data []byte) error {
case "mac": case "mac":
macho.Write(buffer, code, data) macho.Write(buffer, code, data)
case "windows": case "windows":
dlls := []pe.DLL{ pe.Write(buffer, code, data, windows.DLLs)
{
Name: "kernel32.dll",
Functions: []string{
"ExitProcess",
"GetStdHandle",
"WriteFile",
},
},
{
Name: "user32.dll",
Functions: []string{
"MessageBoxA",
},
},
}
pe.Write(buffer, code, data, dlls)
default: default:
return fmt.Errorf("unsupported platform '%s'", config.TargetOS) return fmt.Errorf("unsupported platform '%s'", config.TargetOS)
} }

View File

@ -1,4 +1,4 @@
package pe package dll
type DLL struct { type DLL struct {
Name string Name string

24
src/dll/List.go Normal file
View File

@ -0,0 +1,24 @@
package dll
// List is a slice of DLLs.
type List []DLL
// Index returns the position of the given function name.
func (list List) Index(dllName string, funcName string) int {
index := 0
for _, dll := range list {
if dll.Name != dllName {
index += len(dll.Functions) + 1
continue
}
for i, fn := range dll.Functions {
if fn == funcName {
return index + i
}
}
}
return -1
}

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/dll"
"git.akyoto.dev/cli/q/src/exe" "git.akyoto.dev/cli/q/src/exe"
) )
@ -18,56 +19,71 @@ 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) { func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
NumSections := 2 if len(data) == 0 {
data = []byte{0}
}
NumSections := 3
HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections
codeStart, codePadding := exe.Align(HeaderEnd, config.Align) codeStart, codePadding := exe.Align(HeaderEnd, config.Align)
dataStart, dataPadding := exe.Align(codeStart+len(code), config.Align) dataStart, dataPadding := exe.Align(codeStart+len(code), config.Align)
importsStart, importsPadding := exe.Align(dataStart+len(data), config.Align)
imports := make([]uint64, 0)
dllData := make([]byte, 0)
dllImports := []DLLImport{} dllImports := []DLLImport{}
for _, dll := range dlls { for _, library := range dlls {
dllAddresses := []uint64{} functionsStart := len(imports) * 8
dllNamePos := len(data) dllNamePos := len(dllData)
data = append(data, dll.Name...) dllData = append(dllData, library.Name...)
data = append(data, 0x00) dllData = append(dllData, 0x00)
for _, f := range dll.Functions {
pos := len(data)
data = append(data, 0x00, 0x00)
data = append(data, f...)
data = append(data, 0x00)
if len(data)&1 != 0 {
data = append(data, 0x00) // align the next entry on an even boundary
}
dllAddresses = append(dllAddresses, uint64(dataStart+pos))
}
dllAddresses = append(dllAddresses, 0)
// Add the address table to the data section
functionAddressesStart := dataStart + len(data)
data, _ = binary.Append(data, binary.LittleEndian, &dllAddresses)
dllImports = append(dllImports, DLLImport{ dllImports = append(dllImports, DLLImport{
RvaFunctionNameList: uint32(functionAddressesStart), RvaFunctionNameList: uint32(importsStart + functionsStart),
TimeDateStamp: 0, TimeDateStamp: 0,
ForwarderChain: 0, ForwarderChain: 0,
RvaModuleName: uint32(dataStart + dllNamePos), RvaModuleName: uint32(dllNamePos),
RvaFunctionAddressList: uint32(functionAddressesStart), 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 dllImports = append(dllImports, DLLImport{}) // a zeroed structure marks the end of the list
importDirectoryStart := dllDataStart + len(dllData)
importDirectorySize := DLLImportSize * len(dllImports)
// Add imports to the data section importSectionSize := len(imports)*8 + len(dllData) + importDirectorySize
importsStart := dataStart + len(data) imageSize := importsStart + importSectionSize
importsSize := DLLImportSize * len(dllImports)
data, _ = binary.Append(data, binary.LittleEndian, &dllImports)
imageSize := dataStart + len(data)
imageSize, _ = exe.Align(imageSize, config.Align) imageSize, _ = exe.Align(imageSize, config.Align)
pe := &EXE{ pe := &EXE{
@ -117,8 +133,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) {
NumberOfRvaAndSizes: 16, NumberOfRvaAndSizes: 16,
DataDirectory: [16]DataDirectory{ DataDirectory: [16]DataDirectory{
{VirtualAddress: 0, Size: 0}, {VirtualAddress: 0, Size: 0},
{VirtualAddress: uint32(importsStart), Size: uint32(importsSize)}, // RVA of the imported function table {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},
@ -129,6 +144,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) {
{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}, {VirtualAddress: 0, Size: 0},
{VirtualAddress: 0, Size: 0}, {VirtualAddress: 0, Size: 0},
@ -151,6 +167,14 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) {
RawAddress: uint32(dataStart), RawAddress: uint32(dataStart),
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'},
VirtualSize: uint32(importSectionSize),
VirtualAddress: uint32(importsStart),
RawSize: uint32(importSectionSize),
RawAddress: uint32(importsStart),
Characteristics: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
},
}, },
} }
@ -163,4 +187,8 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) {
writer.Write(code) writer.Write(code)
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding))) writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data) 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)
} }

21
src/os/windows/DLLs.go Normal file
View File

@ -0,0 +1,21 @@
package windows
import "git.akyoto.dev/cli/q/src/dll"
// Temporary fix...
var DLLs = dll.List{
{
Name: "kernel32.dll",
Functions: []string{
"ExitProcess",
"GetStdHandle",
"WriteFile",
},
},
{
Name: "user32.dll",
Functions: []string{
"MessageBoxA",
},
},
}