From 0db54ff6398758ed01b9870ff99fc646bf579404 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sun, 18 Aug 2024 13:29:44 +0200 Subject: [PATCH] Improved Windows support --- src/asm/Finalize.go | 20 +++++--- src/compiler/Result.go | 21 +------- src/{exe/pe => dll}/DLL.go | 2 +- src/dll/List.go | 24 +++++++++ src/exe/pe/EXE.go | 102 +++++++++++++++++++++++-------------- src/os/windows/DLLs.go | 21 ++++++++ 6 files changed, 127 insertions(+), 63 deletions(-) rename src/{exe/pe => dll}/DLL.go (83%) create mode 100644 src/dll/List.go create mode 100644 src/os/windows/DLLs.go diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index b4f7b3c..de3eae6 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -8,12 +8,13 @@ import ( "git.akyoto.dev/cli/q/src/arch/x64" "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/sizeof" ) // Finalize generates the final machine code. -func (a Assembler) Finalize() ([]byte, []byte) { +func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { var ( code = make([]byte, 0, len(a.Instructions)*8) data []byte @@ -21,6 +22,7 @@ func (a Assembler) Finalize() ([]byte, []byte) { dataLabels map[string]Address codePointers []*Pointer dataPointers []*Pointer + dllPointers []*Pointer ) for _, x := range a.Instructions { @@ -114,7 +116,7 @@ func (a Assembler) Finalize() ([]byte, []byte) { case CALL_AT: code = x64.CallAtAddress(code, 0x00_00_00_00) size := 4 - // label := x.Data.(*Label) + label := x.Data.(*Label) pointer := &Pointer{ Position: Address(len(code) - size), @@ -123,12 +125,11 @@ func (a Assembler) Finalize() ([]byte, []byte) { } pointer.Resolve = func() Address { - destination := Address(0x1038) - distance := destination - (pointer.Position + Address(pointer.Size)) - return Address(distance) + index := dlls.Index("kernel32.dll", label.Name) + return Address(index * 8) } - codePointers = append(codePointers, pointer) + dllPointers = append(dllPointers, pointer) case COMMENT: continue @@ -365,5 +366,12 @@ restart: 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 } diff --git a/src/compiler/Result.go b/src/compiler/Result.go index b4b0979..fda6953 100644 --- a/src/compiler/Result.go +++ b/src/compiler/Result.go @@ -77,7 +77,7 @@ func (r *Result) finalize() ([]byte, []byte) { final.Label(asm.CALL_AT, "ExitProcess") } - code, data := final.Finalize() + code, data := final.Finalize(windows.DLLs) return code, data } @@ -154,24 +154,7 @@ func write(writer io.Writer, code []byte, data []byte) error { case "mac": macho.Write(buffer, code, data) case "windows": - dlls := []pe.DLL{ - { - Name: "kernel32.dll", - Functions: []string{ - "ExitProcess", - "GetStdHandle", - "WriteFile", - }, - }, - { - Name: "user32.dll", - Functions: []string{ - "MessageBoxA", - }, - }, - } - - pe.Write(buffer, code, data, dlls) + pe.Write(buffer, code, data, windows.DLLs) default: return fmt.Errorf("unsupported platform '%s'", config.TargetOS) } diff --git a/src/exe/pe/DLL.go b/src/dll/DLL.go similarity index 83% rename from src/exe/pe/DLL.go rename to src/dll/DLL.go index 78a2b48..99c04b4 100644 --- a/src/exe/pe/DLL.go +++ b/src/dll/DLL.go @@ -1,4 +1,4 @@ -package pe +package dll type DLL struct { Name string diff --git a/src/dll/List.go b/src/dll/List.go new file mode 100644 index 0000000..b031981 --- /dev/null +++ b/src/dll/List.go @@ -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 +} diff --git a/src/exe/pe/EXE.go b/src/exe/pe/EXE.go index 3e20183..60a7a79 100644 --- a/src/exe/pe/EXE.go +++ b/src/exe/pe/EXE.go @@ -6,6 +6,7 @@ import ( "io" "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/dll" "git.akyoto.dev/cli/q/src/exe" ) @@ -18,56 +19,71 @@ type EXE struct { } // Write writes the EXE file to the given writer. -func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) { - NumSections := 2 +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) + imports := make([]uint64, 0) + dllData := make([]byte, 0) dllImports := []DLLImport{} - for _, dll := range dlls { - dllAddresses := []uint64{} - dllNamePos := len(data) - data = append(data, dll.Name...) - data = append(data, 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) + for _, library := range dlls { + functionsStart := len(imports) * 8 + dllNamePos := len(dllData) + dllData = append(dllData, library.Name...) + dllData = append(dllData, 0x00) dllImports = append(dllImports, DLLImport{ - RvaFunctionNameList: uint32(functionAddressesStart), + RvaFunctionNameList: uint32(importsStart + functionsStart), TimeDateStamp: 0, ForwarderChain: 0, - RvaModuleName: uint32(dataStart + dllNamePos), - RvaFunctionAddressList: uint32(functionAddressesStart), + 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) - // Add imports to the data section - importsStart := dataStart + len(data) - importsSize := DLLImportSize * len(dllImports) - data, _ = binary.Append(data, binary.LittleEndian, &dllImports) - - imageSize := dataStart + len(data) + importSectionSize := len(imports)*8 + len(dllData) + importDirectorySize + imageSize := importsStart + importSectionSize imageSize, _ = exe.Align(imageSize, config.Align) pe := &EXE{ @@ -117,8 +133,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) { NumberOfRvaAndSizes: 16, DataDirectory: [16]DataDirectory{ {VirtualAddress: 0, Size: 0}, - {VirtualAddress: uint32(importsStart), Size: uint32(importsSize)}, // RVA of the imported function table - {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}, @@ -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: 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}, @@ -151,6 +167,14 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) { 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, + }, }, } @@ -163,4 +187,8 @@ func Write(writer io.Writer, code []byte, data []byte, dlls []DLL) { 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) } diff --git a/src/os/windows/DLLs.go b/src/os/windows/DLLs.go new file mode 100644 index 0000000..3b148ac --- /dev/null +++ b/src/os/windows/DLLs.go @@ -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", + }, + }, +}