Skip to content

Commit

Permalink
Add APIs to fetch processes information
Browse files Browse the repository at this point in the history
- ntdll.NtQueryInformationProcess (internal/unsupported API!)
  PROCESS_BASIC_INFORMATION struct
  UNICODE_STRING struct
  RTL_USER_PROCESS_PARAMETERS struct
- kernel32.ReadProcessMemory
- psapi.GetProcessImageFileNameA
- psapi.EnumProcesses
  • Loading branch information
adriansr committed Aug 13, 2018
1 parent f11b8c9 commit 8d35ed7
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 10 deletions.
1 change: 1 addition & 0 deletions .ci/scripts/check_lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

var ignoreWarnings = []string{
`don't use underscores in Go names`,
`don't use ALL_CAPS in Go names`,
}

var ignoreWarningsRe = regexp.MustCompile(strings.Join(ignoreWarnings, "|"))
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Changelog

* Add GetProcessMemoryInfo: [#2](https://github.com/elastic/go-windows/pull/2)
* Add APIs to fetch process information:
- NtQueryInformationProcess
- ReadProcessMemory
- GetProcessImageFileName
- EnumProcesses
[#6](https://github.com/elastic/go-windows/pull/6)
32 changes: 32 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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

const (
// This process access rights are missing from Go's syscall package as of 1.10.3

// PROCESS_VM_READ right allows to read memory from the target process.
PROCESS_VM_READ = 0x10

// PROCESS_QUERY_LIMITED_INFORMATION right allows to access a subset of the
// information granted by PROCESS_QUERY_INFORMATION. Not available in XP
// and Server 2003.
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
)
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 18 additions & 0 deletions kernel32.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) (err error) = kernel32.ReadProcessMemory

var (
sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MemoryStatusEx{}))
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -218,3 +222,17 @@ func GlobalMemoryStatusEx() (MemoryStatusEx, error) {

return memoryStatusEx, nil
}

// ReadProcessMemory reads from another process memory. The Handle needs to have
// the PROCESS_VM_READ right.
// 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 err = _ReadProcessMemory(handle, baseAddress, uintptr(unsafe.Pointer(&dest[0])), uintptr(n), &numRead); err != nil {
return 0, err
}
return numRead, nil
}
129 changes: 129 additions & 0 deletions ntdll.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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 (
// SizeOfProcessBasicInformationStruct gives the size
// of the ProcessBasicInformationStruct struct.
SizeOfProcessBasicInformationStruct = unsafe.Sizeof(ProcessBasicInformationStruct{})

// SizeOfRtlUserProcessParameters gives the size
// of the RtlUserProcessParameters struct.
SizeOfRtlUserProcessParameters = unsafe.Sizeof(RtlUserProcessParameters{})
)

// NTStatus is an error wrapper for NTSTATUS values, 32bit error-codes returned
// by the NT Kernel.
type NTStatus uint32

// ProcessInfoClass is Go's counterpart for the PROCESSINFOCLASS enumeration
// defined in ntdll.h.
type ProcessInfoClass uint32

const (
// ProcessInfoClass enumeration values that can be used as arguments to
// NtQueryInformationProcess

// ProcessBasicInformation returns a pointer to
// the Process Environment Block (PEB) structure.
ProcessBasicInformation ProcessInfoClass = 0

// ProcessDebugPort returns a uint32 that is the port number for the
// debugger of the process.
ProcessDebugPort = 7

// ProcessWow64Information returns whether a process is running under
// WOW64.
ProcessWow64Information = 26

// ProcessImageFileName returns the image file name for the process, as a
// UnicodeString struct.
ProcessImageFileName = 27

// ProcessBreakOnTermination returns a uintptr that tells if the process
// is critical.
ProcessBreakOnTermination = 29

// ProcessSubsystemInformation returns the subsystem type of the process.
ProcessSubsystemInformation = 75
)

// 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
// Undocumented:
InheritedFromUniqueProcessID 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

// <undocumented>
CurrentDirectoryPath UnicodeString
CurrentDirectoryHandle uintptr
DllPath UnicodeString
// </undocumented>

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 of type NTStatus.
func NtQueryInformationProcess(handle syscall.Handle, infoClass ProcessInfoClass, info unsafe.Pointer, infoLen uint32) (returnedLen uint32, err error) {
status := _NtQueryInformationProcess(handle, uint32(infoClass), uintptr(info), infoLen, &returnedLen)
if status != 0 {
return returnedLen, NTStatus(status)
}
return returnedLen, nil
}

// Error prints the wrapped NTSTATUS in hex form.
func (status NTStatus) Error() string {
return fmt.Sprintf("ntstatus=%x", uint32(status))
}
38 changes: 38 additions & 0 deletions psapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ 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
//sys _EnumProcesses(lpidProcess *uint32, cb uint32, lpcbNeeded *uint32) (err error) = psapi.EnumProcesses

var (
sizeofProcessMemoryCountersEx = uint32(unsafe.Sizeof(ProcessMemoryCountersEx{}))
Expand Down Expand Up @@ -60,3 +62,39 @@ 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.
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/nf-psapi-getprocessimagefilenamea
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
}

// EnumProcesses returns a list of running processes.
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/nf-psapi-enumprocesses
func EnumProcesses() (pids []uint32, err error) {
for nAlloc, nGot := uint32(128), uint32(0); ; nAlloc *= 2 {
pids = make([]uint32, nAlloc)
if err = _EnumProcesses(&pids[0], nAlloc*4, &nGot); err != nil {
return nil, err
}
if nGot/4 < nAlloc {
return pids, nil
}
}
}
95 changes: 95 additions & 0 deletions vendor/github.com/elastic/go-windows/ntdll.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8d35ed7

Please sign in to comment.