Improved Windows DLL calls
This commit is contained in:
parent
0db54ff639
commit
05789d9626
@ -1,14 +1,13 @@
|
|||||||
write(_ Int, _ Pointer, _ Int) -> Int {
|
write(fd Int, address Pointer, length Int) -> Int {
|
||||||
// WriteFile(fd, buffer, length, out numberOfBytesWritten, out overlapped)
|
// out numberOfBytesWritten
|
||||||
return 0
|
// out overlapped
|
||||||
|
return kernel32.WriteFile(fd, address, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
mmap(_ Int, _ Int, _ Int, _ Int) -> Pointer {
|
mmap(address Int, length Int, protection Int, flags Int) -> Pointer {
|
||||||
// VirtualAlloc(address, length, flags, protection)
|
return kernel32.VirtualAlloc(address, length, flags, protection)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(_ Int) {
|
exit(code Int) {
|
||||||
// ExitProcess(code)
|
kernel32.ExitProcess(code)
|
||||||
return
|
|
||||||
}
|
}
|
@ -113,24 +113,6 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) {
|
|||||||
|
|
||||||
codePointers = append(codePointers, pointer)
|
codePointers = append(codePointers, pointer)
|
||||||
|
|
||||||
case CALL_AT:
|
|
||||||
code = x64.CallAtAddress(code, 0x00_00_00_00)
|
|
||||||
size := 4
|
|
||||||
label := x.Data.(*Label)
|
|
||||||
|
|
||||||
pointer := &Pointer{
|
|
||||||
Position: Address(len(code) - size),
|
|
||||||
OpSize: 2,
|
|
||||||
Size: uint8(size),
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer.Resolve = func() Address {
|
|
||||||
index := dlls.Index("kernel32.dll", label.Name)
|
|
||||||
return Address(index * 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
dllPointers = append(dllPointers, pointer)
|
|
||||||
|
|
||||||
case COMMENT:
|
case COMMENT:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -142,6 +124,36 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) {
|
|||||||
code = x64.CompareRegisterRegister(code, operands.Destination, operands.Source)
|
code = x64.CompareRegisterRegister(code, operands.Destination, operands.Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DLLCALL:
|
||||||
|
size := 4
|
||||||
|
|
||||||
|
code = x64.SubRegisterNumber(code, x64.RSP, 32)
|
||||||
|
code = x64.CallAtAddress(code, 0x00_00_00_00)
|
||||||
|
position := len(code) - size
|
||||||
|
code = x64.AddRegisterNumber(code, x64.RSP, 32)
|
||||||
|
|
||||||
|
label := x.Data.(*Label)
|
||||||
|
pointer := &Pointer{
|
||||||
|
Position: Address(position),
|
||||||
|
OpSize: 2,
|
||||||
|
Size: uint8(size),
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer.Resolve = func() Address {
|
||||||
|
dot := strings.Index(label.Name, ".")
|
||||||
|
library := label.Name[:dot]
|
||||||
|
funcName := label.Name[dot+1:]
|
||||||
|
index := dlls.Index(library, funcName)
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
panic("unknown DLL function " + label.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Address(index * 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
dllPointers = append(dllPointers, pointer)
|
||||||
|
|
||||||
case JE, JNE, JG, JGE, JL, JLE, JUMP:
|
case JE, JNE, JG, JGE, JL, JLE, JUMP:
|
||||||
switch x.Mnemonic {
|
switch x.Mnemonic {
|
||||||
case JE:
|
case JE:
|
||||||
|
@ -20,6 +20,16 @@ func (a *Assembler) Call(name string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DLLCall calls a function in a DLL file.
|
||||||
|
func (a *Assembler) DLLCall(name string) {
|
||||||
|
a.Instructions = append(a.Instructions, Instruction{
|
||||||
|
Mnemonic: DLLCALL,
|
||||||
|
Data: &Label{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Return returns back to the caller.
|
// Return returns back to the caller.
|
||||||
func (a *Assembler) Return() {
|
func (a *Assembler) Return() {
|
||||||
if len(a.Instructions) > 0 {
|
if len(a.Instructions) > 0 {
|
||||||
|
@ -29,7 +29,7 @@ const (
|
|||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
CALL
|
CALL
|
||||||
CALL_AT
|
DLLCALL
|
||||||
JE
|
JE
|
||||||
JNE
|
JNE
|
||||||
JG
|
JG
|
||||||
@ -55,8 +55,8 @@ func (m Mnemonic) String() string {
|
|||||||
return "and"
|
return "and"
|
||||||
case CALL:
|
case CALL:
|
||||||
return "call"
|
return "call"
|
||||||
case CALL_AT:
|
case DLLCALL:
|
||||||
return "call at"
|
return "dllcall"
|
||||||
case COMMENT:
|
case COMMENT:
|
||||||
return "comment"
|
return "comment"
|
||||||
case COMPARE:
|
case COMPARE:
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"git.akyoto.dev/cli/q/src/asm"
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
"git.akyoto.dev/cli/q/src/config"
|
"git.akyoto.dev/cli/q/src/config"
|
||||||
"git.akyoto.dev/cli/q/src/core"
|
"git.akyoto.dev/cli/q/src/core"
|
||||||
|
"git.akyoto.dev/cli/q/src/dll"
|
||||||
"git.akyoto.dev/cli/q/src/exe/elf"
|
"git.akyoto.dev/cli/q/src/exe/elf"
|
||||||
"git.akyoto.dev/cli/q/src/exe/macho"
|
"git.akyoto.dev/cli/q/src/exe/macho"
|
||||||
"git.akyoto.dev/cli/q/src/exe/pe"
|
"git.akyoto.dev/cli/q/src/exe/pe"
|
||||||
@ -27,7 +28,7 @@ type Result struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// finalize generates the final machine code.
|
// finalize generates the final machine code.
|
||||||
func (r *Result) finalize() ([]byte, []byte) {
|
func (r *Result) finalize() ([]byte, []byte, dll.List) {
|
||||||
// This will be the entry point of the executable.
|
// This will be the entry point of the executable.
|
||||||
// The only job of the entry function is to call `main` and exit cleanly.
|
// The only job of the entry function is to call `main` and exit cleanly.
|
||||||
// The reason we call `main` instead of using `main` itself is to place
|
// The reason we call `main` instead of using `main` itself is to place
|
||||||
@ -49,15 +50,22 @@ func (r *Result) finalize() ([]byte, []byte) {
|
|||||||
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 0)
|
||||||
final.Syscall()
|
final.Syscall()
|
||||||
case "windows":
|
case "windows":
|
||||||
final.RegisterNumber(asm.SUB, x64.RSP, 32+8)
|
final.RegisterNumber(asm.MOVE, windows.X64InputRegisters[0], 0)
|
||||||
final.RegisterNumber(asm.MOVE, x64.RCX, 0)
|
final.DLLCall("kernel32.ExitProcess")
|
||||||
final.Label(asm.CALL_AT, "ExitProcess")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dlls := dll.List{}
|
||||||
|
|
||||||
// This will place the main function immediately after the entry point
|
// This will place the main function immediately after the entry point
|
||||||
// and also add everything the main function calls recursively.
|
// and also add everything the main function calls recursively.
|
||||||
r.eachFunction(r.Main, map[*core.Function]bool{}, func(f *core.Function) {
|
r.eachFunction(r.Main, map[*core.Function]bool{}, func(f *core.Function) {
|
||||||
final.Merge(f.Assembler)
|
final.Merge(f.Assembler)
|
||||||
|
|
||||||
|
for _, library := range f.DLLs {
|
||||||
|
for _, fn := range library.Functions {
|
||||||
|
dlls = dlls.Append(library.Name, fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
final.Label(asm.LABEL, "_crash")
|
final.Label(asm.LABEL, "_crash")
|
||||||
@ -72,13 +80,12 @@ func (r *Result) finalize() ([]byte, []byte) {
|
|||||||
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1)
|
final.RegisterNumber(asm.MOVE, x64.SyscallInputRegisters[1], 1)
|
||||||
final.Syscall()
|
final.Syscall()
|
||||||
case "windows":
|
case "windows":
|
||||||
final.RegisterNumber(asm.SUB, x64.RSP, 32+8)
|
|
||||||
final.RegisterNumber(asm.MOVE, windows.X64InputRegisters[0], 1)
|
final.RegisterNumber(asm.MOVE, windows.X64InputRegisters[0], 1)
|
||||||
final.Label(asm.CALL_AT, "ExitProcess")
|
final.Label(asm.DLLCALL, "kernel32.ExitProcess")
|
||||||
}
|
}
|
||||||
|
|
||||||
code, data := final.Finalize(windows.DLLs)
|
code, data := final.Finalize(dlls)
|
||||||
return code, data
|
return code, data, dlls
|
||||||
}
|
}
|
||||||
|
|
||||||
// eachFunction recursively finds all the calls to external functions.
|
// eachFunction recursively finds all the calls to external functions.
|
||||||
@ -116,8 +123,8 @@ func (r *Result) PrintInstructions() {
|
|||||||
|
|
||||||
// Write writes the executable to the given writer.
|
// Write writes the executable to the given writer.
|
||||||
func (r *Result) Write(writer io.Writer) error {
|
func (r *Result) Write(writer io.Writer) error {
|
||||||
code, data := r.finalize()
|
code, data, dlls := r.finalize()
|
||||||
return write(writer, code, data)
|
return write(writer, code, data, dlls)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes an executable file to disk.
|
// Write writes an executable file to disk.
|
||||||
@ -145,7 +152,7 @@ func (r *Result) WriteFile(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write writes an executable file to the given writer.
|
// write writes an executable file to the given writer.
|
||||||
func write(writer io.Writer, code []byte, data []byte) error {
|
func write(writer io.Writer, code []byte, data []byte, dlls dll.List) error {
|
||||||
buffer := bufio.NewWriter(writer)
|
buffer := bufio.NewWriter(writer)
|
||||||
|
|
||||||
switch config.TargetOS {
|
switch config.TargetOS {
|
||||||
@ -154,7 +161,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":
|
||||||
pe.Write(buffer, code, data, windows.DLLs)
|
pe.Write(buffer, code, data, dlls)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported platform '%s'", config.TargetOS)
|
return fmt.Errorf("unsupported platform '%s'", config.TargetOS)
|
||||||
}
|
}
|
||||||
|
@ -45,5 +45,3 @@ func Reset() {
|
|||||||
TargetOS = "mac"
|
TargetOS = "mac"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/asm"
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/os/windows"
|
||||||
"git.akyoto.dev/cli/q/src/types"
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +34,22 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
name = nameNode.Children[1].Token.Text(f.File.Bytes)
|
name = nameNode.Children[1].Token.Text(f.File.Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pkg != f.File.Package {
|
if pkg == "kernel32" || pkg == "user32" || pkg == "gdi32" || pkg == "comctl32" {
|
||||||
|
parameters := root.Children[1:]
|
||||||
|
registers := windows.X64InputRegisters[:len(parameters)]
|
||||||
|
|
||||||
|
for i := len(parameters) - 1; i >= 0; i-- {
|
||||||
|
_, err := f.ExpressionToRegister(parameters[i], registers[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.DLLs = f.DLLs.Append(pkg, name)
|
||||||
|
f.DLLCall(fmt.Sprintf("%s.%s", pkg, name))
|
||||||
|
return nil, nil
|
||||||
|
} else if pkg != f.File.Package {
|
||||||
if f.File.Imports == nil {
|
if f.File.Imports == nil {
|
||||||
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
|
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/dll"
|
||||||
"git.akyoto.dev/cli/q/src/fs"
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/register"
|
"git.akyoto.dev/cli/q/src/register"
|
||||||
"git.akyoto.dev/cli/q/src/scope"
|
"git.akyoto.dev/cli/q/src/scope"
|
||||||
@ -19,6 +20,7 @@ type Function struct {
|
|||||||
Parameters []*scope.Variable
|
Parameters []*scope.Variable
|
||||||
ReturnTypes []*types.Type
|
ReturnTypes []*types.Type
|
||||||
Functions map[string]*Function
|
Functions map[string]*Function
|
||||||
|
DLLs dll.List
|
||||||
Err error
|
Err error
|
||||||
deferred []func()
|
deferred []func()
|
||||||
count counter
|
count counter
|
||||||
|
@ -3,6 +3,26 @@ package dll
|
|||||||
// List is a slice of DLLs.
|
// List is a slice of DLLs.
|
||||||
type List []DLL
|
type List []DLL
|
||||||
|
|
||||||
|
// Append adds a function for the given DLL if it doesn't exist yet.
|
||||||
|
func (list List) Append(dllName string, funcName string) List {
|
||||||
|
for _, dll := range list {
|
||||||
|
if dll.Name != dllName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fn := range dll.Functions {
|
||||||
|
if fn == funcName {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dll.Functions = append(dll.Functions, funcName)
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(list, DLL{Name: dllName, Functions: []string{funcName}})
|
||||||
|
}
|
||||||
|
|
||||||
// Index returns the position of the given function name.
|
// Index returns the position of the given function name.
|
||||||
func (list List) Index(dllName string, funcName string) int {
|
func (list List) Index(dllName string, funcName string) int {
|
||||||
index := 0
|
index := 0
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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/dll"
|
||||||
@ -38,6 +39,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
functionsStart := len(imports) * 8
|
functionsStart := len(imports) * 8
|
||||||
dllNamePos := len(dllData)
|
dllNamePos := len(dllData)
|
||||||
dllData = append(dllData, library.Name...)
|
dllData = append(dllData, library.Name...)
|
||||||
|
dllData = append(dllData, ".dll"...)
|
||||||
dllData = append(dllData, 0x00)
|
dllData = append(dllData, 0x00)
|
||||||
|
|
||||||
dllImports = append(dllImports, DLLImport{
|
dllImports = append(dllImports, DLLImport{
|
||||||
@ -95,7 +97,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
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: uint16(NumSections),
|
NumberOfSections: uint16(NumSections),
|
||||||
TimeDateStamp: 0,
|
TimeDateStamp: uint32(time.Now().Unix()),
|
||||||
PointerToSymbolTable: 0,
|
PointerToSymbolTable: 0,
|
||||||
NumberOfSymbols: 0,
|
NumberOfSymbols: 0,
|
||||||
SizeOfOptionalHeader: OptionalHeader64Size,
|
SizeOfOptionalHeader: OptionalHeader64Size,
|
||||||
@ -124,7 +126,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) {
|
|||||||
SizeOfHeaders: config.CodeOffset, // section bodies begin here
|
SizeOfHeaders: config.CodeOffset, // section bodies begin here
|
||||||
CheckSum: 0,
|
CheckSum: 0,
|
||||||
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI,
|
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI,
|
||||||
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE,
|
DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, // IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
||||||
SizeOfStackReserve: 0x100000,
|
SizeOfStackReserve: 0x100000,
|
||||||
SizeOfStackCommit: 0x1000,
|
SizeOfStackCommit: 0x1000,
|
||||||
SizeOfHeapReserve: 0x100000,
|
SizeOfHeapReserve: 0x100000,
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
9
src/register/DLLCall.go
Normal file
9
src/register/DLLCall.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package register
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/asm"
|
||||||
|
|
||||||
|
func (f *Machine) DLLCall(label string) {
|
||||||
|
f.Assembler.Label(asm.DLLCALL, label)
|
||||||
|
f.UseRegister(f.CPU.Output[0])
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user