From 5f20f19fad5e1481e32bf02fb4618ae183f7b8a8 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Thu, 2 Aug 2018 12:33:37 +0200 Subject: [PATCH] Add APIs to fetch processes information - ntdll.NtQueryInformationProcess (internal/unsupported API!) PROCESS_BASIC_INFORMATION struct UNICODE_STRING struct RTL_USER_PROCESS_PARAMETERS struct - kernel32.ReadProcessMemory - psapi.GetProcessImageFileNameA --- doc.go | 2 +- kernel32.go | 19 ++++ psapi.go | 22 +++++ vendor/github.com/elastic/go-windows/ntdll.go | 95 +++++++++++++++++++ zsyscall_windows.go | 47 +++++++-- 5 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 vendor/github.com/elastic/go-windows/ntdll.go diff --git a/doc.go b/doc.go index 2a13ced..03e106f 100644 --- a/doc.go +++ b/doc.go @@ -20,4 +20,4 @@ package windows // Use "GOOS=windows go generate -v -x" to generate the sources. // Add -trace to enable debug prints around syscalls. -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=false -output zsyscall_windows.go kernel32.go version.go psapi.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=false -output zsyscall_windows.go kernel32.go version.go psapi.go ntdll.go diff --git a/kernel32.go b/kernel32.go index 9dfff23..8d92b9e 100644 --- a/kernel32.go +++ b/kernel32.go @@ -33,6 +33,7 @@ import ( //sys _GetTickCount64() (millis uint64, err error) = kernel32.GetTickCount64 //sys _GetSystemTimes(idleTime *syscall.Filetime, kernelTime *syscall.Filetime, userTime *syscall.Filetime) (err error) = kernel32.GetSystemTimes //sys _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) = kernel32.GlobalMemoryStatusEx +//sys _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (s bool) = kernel32.ReadProcessMemory var ( sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MemoryStatusEx{})) @@ -67,6 +68,9 @@ const ( ProcessorArchitectureUnknown ProcessorArchitecture = 0xFFFF ) +// ErrReadFailed is returned by ReadProcessMemory on failure +var ErrReadFailed = errors.New("ReadProcessMemory failed") + func (a ProcessorArchitecture) String() string { names := map[ProcessorArchitecture]string{ ProcessorArchitectureAMD64: "x86_64", @@ -218,3 +222,18 @@ func GlobalMemoryStatusEx() (MemoryStatusEx, error) { return memoryStatusEx, nil } + +// ReadProcessMemory reads from another process memory. The Handle needs to have +// the PROCESS_VM_READ right. +// On failure, ErrReadFailed is returned. +// A zero-byte read is a no-op, no error is returned. +func ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, dest []byte) (numRead uintptr, err error) { + n := len(dest) + if n == 0 { + return 0, nil + } + if !_ReadProcessMemory(handle, baseAddress, uintptr(unsafe.Pointer(&dest[0])), uintptr(n), &numRead) { + return numRead, ErrReadFailed + } + return numRead, nil +} diff --git a/psapi.go b/psapi.go index 19e2383..2825401 100644 --- a/psapi.go +++ b/psapi.go @@ -28,6 +28,7 @@ import ( // Syscalls //sys _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCountersEx, cb uint32) (err error) = psapi.GetProcessMemoryInfo +//sys _GetProcessImageFileNameA(handle syscall.Handle, imageFileName *byte, nSize uint32) (len uint32, err error) = psapi.GetProcessImageFileNameA var ( sizeofProcessMemoryCountersEx = uint32(unsafe.Sizeof(ProcessMemoryCountersEx{})) @@ -60,3 +61,24 @@ func GetProcessMemoryInfo(process syscall.Handle) (ProcessMemoryCountersEx, erro } return info, nil } + +// GetProcessImageFileName retrieves the process main executable. +// The returned path is a device path, that is: +// "\Device\HardDisk0Volume1\Windows\notepad.exe" +// instead of +// "C:\Windows\notepad.exe" +// Use QueryDosDevice or equivalent to convert to a drive path. +func GetProcessImageFileName(handle syscall.Handle) (string, error) { + for bufLen, limit := syscall.MAX_PATH, syscall.MAX_PATH*4; bufLen <= limit; bufLen *= 2 { + buf := make([]byte, bufLen) + nameLen, err := _GetProcessImageFileNameA(handle, &buf[0], uint32(len(buf))) + if err == nil { + buf = buf[:nameLen] + return string(buf), nil + } + if err != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", err + } + } + return "", syscall.ERROR_INSUFFICIENT_BUFFER +} diff --git a/vendor/github.com/elastic/go-windows/ntdll.go b/vendor/github.com/elastic/go-windows/ntdll.go new file mode 100644 index 0000000..fc6e22a --- /dev/null +++ b/vendor/github.com/elastic/go-windows/ntdll.go @@ -0,0 +1,95 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build windows + +package windows + +import ( + "fmt" + "syscall" + "unsafe" +) + +const ( + // PROCESS_BASIC_INFORMATION ProcessInformationClass parameter for + // NtQueryInformationProcess. + ProcessBasicInformation = 0 + + SizeOfProcessBasicInformationStruct = unsafe.Sizeof(ProcessBasicInformationStruct{}) + SizeOfRtlUserProcessParameters = unsafe.Sizeof(RtlUserProcessParameters{}) +) + +// NTStatus is an error wrapper for NTSTATUS values, 32bit error-codes returned +// by the NT Kernel. +type NTStatus uint32 + +// ProcessBasicInformationStruct is Go's counterpart of the +// PROCESS_BASIC_INFORMATION struct, returned by NtQueryInformationProcess +// when ProcessBasicInformation is requested. +type ProcessBasicInformationStruct struct { + Reserved1 uintptr + PebBaseAddress uintptr + Reserved2 [2]uintptr + UniqueProcessId uintptr + Reserved3 uintptr +} + +// UnicodeString is Go's equivalent for the _UNICODE_STRING struct. +type UnicodeString struct { + Size uint16 + MaximumLength uint16 + Buffer uintptr +} + +// RtlUserProcessParameters is Go's equivalent for the +// _RTL_USER_PROCESS_PARAMETERS struct. +// A few undocumented fields are exposed. +type RtlUserProcessParameters struct { + Reserved1 [16]byte + Reserved2 [5]uintptr + + // + CurrentDirectoryPath UnicodeString + CurrentDirectoryHandle uintptr + DllPath UnicodeString + // + + ImagePathName UnicodeString + CommandLine UnicodeString +} + +// Syscalls +// Warning: NtQueryInformationProcess is an unsupported API that can change +// in future versions of Windows. Available from XP to Windows 10. +//sys _NtQueryInformationProcess(handle syscall.Handle, infoClass uint32, info uintptr, infoLen uint32, returnLen *uint32) (ntStatus uint32) = ntdll.NtQueryInformationProcess + +// NtQueryInformationProcess is a wrapper for ntdll.NtQueryInformationProcess. +// The handle must have the PROCESS_QUERY_INFORMATION access right. +// Returns an error that can be cast to a NTStatus type. +func NtQueryInformationProcess(handle syscall.Handle, infoClass uint32, info unsafe.Pointer, infoLen uint32, returnLen *uint32) error { + status := _NtQueryInformationProcess(handle, infoClass, uintptr(info), infoLen, returnLen) + if status != 0 { + return NTStatus(status) + } + return nil +} + +// Error prints the wrapped NTSTATUS in hex form. +func (status NTStatus) Error() string { + return fmt.Sprintf("ntstatus=%x", uint32(status)) +} diff --git a/zsyscall_windows.go b/zsyscall_windows.go index a0c5676..5f0c383 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -38,15 +38,19 @@ var ( modkernel32 = syscall.NewLazyDLL("kernel32.dll") modversion = syscall.NewLazyDLL("version.dll") modpsapi = syscall.NewLazyDLL("psapi.dll") - - procGetNativeSystemInfo = modkernel32.NewProc("GetNativeSystemInfo") - procGetTickCount64 = modkernel32.NewProc("GetTickCount64") - procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") - procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") - procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW") - procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW") - procVerQueryValueW = modversion.NewProc("VerQueryValueW") - procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + modntdll = syscall.NewLazyDLL("ntdll.dll") + + procGetNativeSystemInfo = modkernel32.NewProc("GetNativeSystemInfo") + procGetTickCount64 = modkernel32.NewProc("GetTickCount64") + procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") + procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") + procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") + procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW") + procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW") + procVerQueryValueW = modversion.NewProc("VerQueryValueW") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + procGetProcessImageFileNameA = modpsapi.NewProc("GetProcessImageFileNameA") + procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess") ) func _GetNativeSystemInfo(systemInfo *SystemInfo) { @@ -91,6 +95,12 @@ func _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) { return } +func _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (s bool) { + r0, _, _ := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(handle), uintptr(baseAddress), uintptr(buffer), uintptr(size), uintptr(unsafe.Pointer(numRead)), 0) + s = r0 != 0 + return +} + func _GetFileVersionInfo(filename string, reserved uint32, dataLen uint32, data *byte) (success bool, err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(filename) @@ -168,3 +178,22 @@ func _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCo } return } + +func _GetProcessImageFileNameA(handle syscall.Handle, imageFileName *byte, nSize uint32) (len uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameA.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(imageFileName)), uintptr(nSize)) + len = uint32(r0) + if len == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func _NtQueryInformationProcess(handle syscall.Handle, infoClass uint32, info uintptr, infoLen uint32, returnLen *uint32) (ntStatus uint32) { + r0, _, _ := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(handle), uintptr(infoClass), uintptr(info), uintptr(infoLen), uintptr(unsafe.Pointer(returnLen)), 0) + ntStatus = uint32(r0) + return +}