windows: add process module related syscalls
Add EnumProcessModules, EnumProcessModulesEx, GetModuleInformation, GetModuleFileNameEx and GetModuleBaseName.
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodulesex
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmoduleinformation
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw
https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulebasenamew
Change-Id: I75b062daec7a2cc9685e803dfa851825acee32b6
GitHub-Last-Rev: 7c208d6d3eedfbcd536470c6dbde4f4aecdae61e
GitHub-Pull-Request: golang/sys#116
Reviewed-on: https://go-review.googlesource.com/c/sys/+/350870
Trust: Alex Brainman <alex.brainman@gmail.com>
Trust: Minux Ma <minux@golang.org>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go
index 1215b2a..ff2d45d 100644
--- a/windows/syscall_windows.go
+++ b/windows/syscall_windows.go
@@ -398,6 +398,11 @@
// Process Status API (PSAPI)
//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
+//sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules
+//sys EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) = psapi.EnumProcessModulesEx
+//sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation
+//sys GetModuleFileNameEx(process Handle, module Handle, filename *uint16, size uint32) (err error) = psapi.GetModuleFileNameExW
+//sys GetModuleBaseName(process Handle, module Handle, baseName *uint16, size uint32) (err error) = psapi.GetModuleBaseNameW
// NT Native APIs
//sys rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb
diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go
index fd75c09..bc11750 100644
--- a/windows/syscall_windows_test.go
+++ b/windows/syscall_windows_test.go
@@ -701,3 +701,79 @@
}
}
+
+func TestProcessModules(t *testing.T) {
+ process, err := windows.GetCurrentProcess()
+ if err != nil {
+ t.Fatalf("unable to get current process: %v", err)
+ }
+ // NB: Assume that we're always the first module. This technically isn't documented anywhere (that I could find), but seems to always hold.
+ var module windows.Handle
+ var cbNeeded uint32
+ err = windows.EnumProcessModules(process, &module, uint32(unsafe.Sizeof(module)), &cbNeeded)
+ if err != nil {
+ t.Fatalf("EnumProcessModules failed: %v", err)
+ }
+
+ var moduleEx windows.Handle
+ err = windows.EnumProcessModulesEx(process, &moduleEx, uint32(unsafe.Sizeof(moduleEx)), &cbNeeded, windows.LIST_MODULES_DEFAULT)
+ if err != nil {
+ t.Fatalf("EnumProcessModulesEx failed: %v", err)
+ }
+ if module != moduleEx {
+ t.Fatalf("module from EnumProcessModules does not match EnumProcessModulesEx: %v != %v", module, moduleEx)
+ }
+
+ exePath, err := os.Executable()
+ if err != nil {
+ t.Fatalf("unable to get current executable path: %v", err)
+ }
+
+ modulePathUTF16 := make([]uint16, len(exePath)+1)
+ err = windows.GetModuleFileNameEx(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
+ if err != nil {
+ t.Fatalf("GetModuleFileNameEx failed: %v", err)
+ }
+
+ modulePath := windows.UTF16ToString(modulePathUTF16)
+ if modulePath != exePath {
+ t.Fatalf("module does not match executable for GetModuleFileNameEx: %s != %s", modulePath, exePath)
+ }
+
+ err = windows.GetModuleBaseName(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
+ if err != nil {
+ t.Fatalf("GetModuleBaseName failed: %v", err)
+ }
+
+ modulePath = windows.UTF16ToString(modulePathUTF16)
+ baseExePath := filepath.Base(exePath)
+ if modulePath != baseExePath {
+ t.Fatalf("module does not match executable for GetModuleBaseName: %s != %s", modulePath, baseExePath)
+ }
+
+ var moduleInfo windows.ModuleInfo
+ err = windows.GetModuleInformation(process, module, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo)))
+ if err != nil {
+ t.Fatalf("GetModuleInformation failed: %v", err)
+ }
+
+ peFile, err := pe.Open(exePath)
+ if err != nil {
+ t.Fatalf("unable to open current executable: %v", err)
+ }
+ defer peFile.Close()
+
+ var peSizeOfImage uint32
+ switch runtime.GOARCH {
+ case "amd64", "arm64":
+ peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage
+ case "386", "arm":
+ peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage
+ default:
+ t.Fatalf("unable to test GetModuleInformation on arch %v", runtime.GOARCH)
+ }
+
+ if moduleInfo.SizeOfImage != peSizeOfImage {
+ t.Fatalf("module size does not match executable: %v != %v", moduleInfo.SizeOfImage, peSizeOfImage)
+ }
+}
diff --git a/windows/types_windows.go b/windows/types_windows.go
index 17f0331..88e0ce5 100644
--- a/windows/types_windows.go
+++ b/windows/types_windows.go
@@ -243,6 +243,14 @@
)
const (
+ // flags for EnumProcessModulesEx
+ LIST_MODULES_32BIT = 0x01
+ LIST_MODULES_64BIT = 0x02
+ LIST_MODULES_ALL = 0x03
+ LIST_MODULES_DEFAULT = 0x00
+)
+
+const (
// filters for ReadDirectoryChangesW and FindFirstChangeNotificationW
FILE_NOTIFY_CHANGE_FILE_NAME = 0x001
FILE_NOTIFY_CHANGE_DIR_NAME = 0x002
@@ -2773,3 +2781,9 @@
// Flag for QueryFullProcessImageName.
const PROCESS_NAME_NATIVE = 1
+
+type ModuleInfo struct {
+ BaseOfDll uintptr
+ SizeOfImage uint32
+ EntryPoint uintptr
+}
diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go
index 2083ec3..e282e24 100644
--- a/windows/zsyscall_windows.go
+++ b/windows/zsyscall_windows.go
@@ -377,7 +377,12 @@
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procStringFromGUID2 = modole32.NewProc("StringFromGUID2")
+ procEnumProcessModules = modpsapi.NewProc("EnumProcessModules")
+ procEnumProcessModulesEx = modpsapi.NewProc("EnumProcessModulesEx")
procEnumProcesses = modpsapi.NewProc("EnumProcesses")
+ procGetModuleBaseNameW = modpsapi.NewProc("GetModuleBaseNameW")
+ procGetModuleFileNameExW = modpsapi.NewProc("GetModuleFileNameExW")
+ procGetModuleInformation = modpsapi.NewProc("GetModuleInformation")
procSubscribeServiceChangeNotifications = modsechost.NewProc("SubscribeServiceChangeNotifications")
procUnsubscribeServiceChangeNotifications = modsechost.NewProc("UnsubscribeServiceChangeNotifications")
procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
@@ -3225,6 +3230,22 @@
return
}
+func EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procEnumProcessModules.Addr(), 4, uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded)), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procEnumProcessModulesEx.Addr(), 5, uintptr(process), uintptr(unsafe.Pointer(module)), uintptr(cb), uintptr(unsafe.Pointer(cbNeeded)), uintptr(filterFlag), 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) {
var _p0 *uint32
if len(processIds) > 0 {
@@ -3237,6 +3258,30 @@
return
}
+func GetModuleBaseName(process Handle, module Handle, baseName *uint16, size uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procGetModuleBaseNameW.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(baseName)), uintptr(size), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func GetModuleFileNameEx(process Handle, module Handle, filename *uint16, size uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procGetModuleFileNameExW.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procGetModuleInformation.Addr(), 4, uintptr(process), uintptr(module), uintptr(unsafe.Pointer(modinfo)), uintptr(cb), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) {
ret = procSubscribeServiceChangeNotifications.Find()
if ret != nil {