December 2nd, 2021
Calling Windows APIs with Go
I took the decision to learn a new programming language, after looking for a while I did choose Go.
In order to practice this new languge I tried to write a simple malware for a ransomware simulation in my workplace and this exercise did lead me to wonder… how I can call Windows APIs with Go?
I did a little research and found how to do it, let’s see!
The package that will do the magic is the windows package and can be added to your project with the following command:
go get golang.org/x/sys/windows
Example #1. Calling IsDebuggerPresent.
package main
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func main() {
fmt.Println("Calling IsDebuggerPresent from kernel32.dll")
kernel32DLL := windows.NewLazyDLL("kernel32.dll")
isDebuggerPresent := kernel32DLL.NewProc("IsDebuggerPresent")
debuggerPresent, _, _ := isDebuggerPresent.Call()
fmt.Println("Result: ", debuggerPresent)
}
In this example, the API will return a 0 because it is not running in a debugger, otherwise the
output will be 1.
Example #2. Calling MessageBoxW.
package main
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func main() {
fmt.Println("Calling MessageBoxW from user32.dll")
user32dll := windows.NewLazyDLL("user32.dll")
messageBoxW := user32dll.NewProc("MessageBoxW")
showMessageBox, _, _ := messageBoxW.Call(
uintptr(0),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("I am a message box! :)"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Hi!"))),
uintptr(0x00000000),
)
}
This one is a little bit complicated because the MessageBoxW API will need at least 3 arguments.
As it can be observed, calling Windows APIs from Go is not difficult, maybe the most tedious task is to read the API documentation to check which arguments it will use.
Thanks for reading!