I am new to programming and have decided to pickup Golang. One project I am working on is injecting a DLL on disk into a remote process via CreateRemoteThread. The intent of the program is to have it spawn a sacrificial process, notepad.exe
, and inject a DLL, test.dll
, into the sacrificial process. The DLL has a single export, in my case "Engage".
My approach, thus far, has been comparing Golang code that injects shellcode via CreateRemoteThread and C code that injects a DLL via CreateRemoteThread. The two resources I am using can be found hereand here.
My knowledge around DLL injection, how Windows APIs operate, and how memory works are all new areas of knowledge that I am trying to develop. With that said, my code might not be the proper sequence of events needed to create a remote thread. And using code that injects shellcode may not be the best framework to build my code upon. If so please let me know!
Below is the Golang code. It runs successfully, but the DLL is not injected. This was verified with Process Monitor. The only reference I see in Process Monitor is a "create file" operation pointing to the directory of where the DLL is located, but the actual DLL name is mangled(not ASCII, SHƒì0èf.DLL). There are a few areas that I was having issues with that may be the source of the problem.
- VirtualAllocEx requires a uintptr for the DLL. I am not sure if the proper information is being passed to allocate memory in the remote process. And looking at C code, people simply use the string length of the DLL path.
- Finding the location of
kernel32.dll
in the current process to find the address of "LoadLibraryA" to create a remote thread. Not sure if using "syscall.LoadLibrary" is the correct approach.
package main
import (
"fmt"
"log"
"os/exec"
"syscall"
"golang.org/x/sys/windows"
)
func main() {
dllPath := "C:\\Users\\Documents\\GoCode\\src\\test.dll"
/*
Spawn process to inject to
*/
cmd := exec.Command("notepad.exe")
cmd.Start()
cmdPid := cmd.Process.Pid
fmt.Println("PID of notepad.exe:", cmdPid)
/*
Process Injction
*/
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
VirtualAllocEx := kernel32.NewProc("VirtualAllocEx")
WriteProcessMemory := kernel32.NewProc("WriteProcessMemory")
CreateRemoteThreadEx := kernel32.NewProc("CreateRemoteThreadEx")
//OpenProcess(desired access, inherite handle, PID)
//Get a handle to the remote process with the proper secutiry attributes to create a remote thread
proc, errOpenProcess := windows.OpenProcess(windows.PROCESS_CREATE_THREAD|windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, false, uint32(cmdPid))
if errOpenProcess != nil {
panic(fmt.Sprintf("[!]Error calling OpenProcess:\r\n%s", errOpenProcess.Error()))
}
//VirtualAllocEx(handle to process/must have PROCESS_VM_OPERATION, optional starting address for alloc, size of mem region to alloc in bytes, type of mem alloc, mem protect)
//Allocate memory in the remote process for the DLL
addr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(proc), 0, uintptr(len(dllPath)), windows.MEM_RESERVE|windows.MEM_COMMIT, windows.PAGE_READWRITE)
if errVirtualAlloc != nil && errVirtualAlloc.Error() != "The operation completed successfully." {
panic(fmt.Sprintf("[!]Error calling VirtualAlloc:\r\n%s", errVirtualAlloc.Error()))
}
//Get uintptr to dll being injected, maybe?
dll, errLoadLib := syscall.LoadLibrary(dllPath)
if errLoadLib != nil {
log.Fatal(errLoadLib)
}
dllH, getProcAddErr := syscall.GetProcAddress(syscall.Handle(dll), "Engage")
if getProcAddErr != nil {
log.Fatal(errLoadLib)
}
loadLibA, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
log.Fatal(errLoadLib)
}
loadLibAHandle, err := syscall.GetProcAddress(loadLibA, "LoadLibraryA")
if err != nil {
log.Fatal(errLoadLib)
}
//WriteProcessMemory(handle to process, base addr to where data is written, pointer to buffer that contains data, num of bytes to be written, optional num of bytes transferred)
_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(proc), addr, (uintptr)(dllH), uintptr(len(dllPath)))
if errWriteProcessMemory != nil && errWriteProcessMemory.Error() != "The operation completed successfully." {
panic(fmt.Sprintf("[!]Error calling WriteProcessMemory:\r\n%s", errWriteProcessMemory.Error()))
}
//CreateRemoteThreadEx(handle to process, security attributes, ).
_, _, errCreateRemoteThreadEx := CreateRemoteThreadEx.Call(uintptr(proc), 0, 0, loadLibAHandle, addr, 0, 0)
if errCreateRemoteThreadEx != nil && errCreateRemoteThreadEx.Error() != "The operation completed successfully." {
panic(fmt.Sprintf("[!]Error calling CreateRemoteThreadEx:\r\n%s", errCreateRemoteThreadEx.Error()))
}
/*
Close handle to process
*/
closeHandle := windows.CloseHandle(proc)
if closeHandle != nil {
fmt.Println("Error closing process:", closeHandle)
} else {
fmt.Println("Closed handle successfully.")
}
}