diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 1215b2ae..ff2d45d4 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -398,6 +398,11 @@ func NewCallbackCDecl(fn interface{}) uintptr { // 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 fd75c09c..bc11750c 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -701,3 +701,79 @@ func TestWinVerifyTrust(t *testing.T) { } } + +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 17f03312..88e0ce5d 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go @@ -242,6 +242,14 @@ const ( TH32CS_INHERIT = 0x80000000 ) +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 @@ -2773,3 +2781,9 @@ const ( // 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 2083ec37..e282e246 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -377,7 +377,12 @@ var ( 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 @@ func stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) { 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 @@ func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) { 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 {