631 lines
24 KiB
Go
631 lines
24 KiB
Go
package windows
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
sizeofUint32 = 4
|
|
sizeofProcessEntry32 = uint32(unsafe.Sizeof(ProcessEntry32{}))
|
|
sizeofProcessMemoryCountersEx = uint32(unsafe.Sizeof(ProcessMemoryCountersEx{}))
|
|
sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MemoryStatusEx{}))
|
|
)
|
|
|
|
// Process-specific access rights. Others are declared in the syscall package.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
|
|
const (
|
|
PROCESS_QUERY_LIMITED_INFORMATION uint32 = 0x1000
|
|
PROCESS_VM_READ uint32 = 0x0010
|
|
)
|
|
|
|
// error codes for GetVolumeInformation function
|
|
const (
|
|
ERROR_INVALID_FUNCTION syscall.Errno = 1
|
|
ERROR_NOT_READY syscall.Errno = 21
|
|
)
|
|
|
|
// SizeOfRtlUserProcessParameters gives the size
|
|
// of the RtlUserProcessParameters struct.
|
|
const SizeOfRtlUserProcessParameters = unsafe.Sizeof(RtlUserProcessParameters{})
|
|
|
|
// MAX_PATH is the maximum length for a path in Windows.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
|
const MAX_PATH = 260
|
|
|
|
// DriveType represents a type of drive (removable, fixed, CD-ROM, RAM disk, or
|
|
// network drive).
|
|
type DriveType uint32
|
|
|
|
// Drive types as returned by GetDriveType.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364939(v=vs.85).aspx
|
|
const (
|
|
DRIVE_UNKNOWN DriveType = iota
|
|
DRIVE_NO_ROOT_DIR
|
|
DRIVE_REMOVABLE
|
|
DRIVE_FIXED
|
|
DRIVE_REMOTE
|
|
DRIVE_CDROM
|
|
DRIVE_RAMDISK
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
func (dt DriveType) String() string {
|
|
names := map[DriveType]string{
|
|
DRIVE_UNKNOWN: "unknown",
|
|
DRIVE_NO_ROOT_DIR: "invalid",
|
|
DRIVE_REMOVABLE: "removable",
|
|
DRIVE_FIXED: "fixed",
|
|
DRIVE_REMOTE: "remote",
|
|
DRIVE_CDROM: "cdrom",
|
|
DRIVE_RAMDISK: "ramdisk",
|
|
}
|
|
|
|
name, found := names[dt]
|
|
if !found {
|
|
return "unknown DriveType value"
|
|
}
|
|
return name
|
|
}
|
|
|
|
// Flags that can be used with CreateToolhelp32Snapshot.
|
|
const (
|
|
TH32CS_INHERIT uint32 = 0x80000000 // Indicates that the snapshot handle is to be inheritable.
|
|
TH32CS_SNAPHEAPLIST uint32 = 0x00000001 // Includes all heaps of the process specified in th32ProcessID in the snapshot.
|
|
TH32CS_SNAPMODULE uint32 = 0x00000008 // Includes all modules of the process specified in th32ProcessID in the snapshot.
|
|
TH32CS_SNAPMODULE32 uint32 = 0x00000010 // Includes all 32-bit modules of the process specified in th32ProcessID in the snapshot when called from a 64-bit process.
|
|
TH32CS_SNAPPROCESS uint32 = 0x00000002 // Includes all processes in the system in the snapshot.
|
|
TH32CS_SNAPTHREAD uint32 = 0x00000004 // Includes all threads in the system in the snapshot.
|
|
)
|
|
|
|
// ProcessEntry32 is an equivalent representation of PROCESSENTRY32 in the
|
|
// Windows API. It contains a process's information. Do not modify or reorder.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684839(v=vs.85).aspx
|
|
type ProcessEntry32 struct {
|
|
size uint32
|
|
CntUsage uint32
|
|
ProcessID uint32
|
|
DefaultHeapID uintptr
|
|
ModuleID uint32
|
|
CntThreads uint32
|
|
ParentProcessID uint32
|
|
PriorityClassBase int32
|
|
Flags uint32
|
|
exeFile [MAX_PATH]uint16
|
|
}
|
|
|
|
// ExeFile returns the name of the executable file for the process. It does
|
|
// not contain the full path.
|
|
func (p ProcessEntry32) ExeFile() string {
|
|
return syscall.UTF16ToString(p.exeFile[:])
|
|
}
|
|
|
|
func (p ProcessEntry32) String() string {
|
|
return fmt.Sprintf("{CntUsage:%v ProcessID:%v DefaultHeapID:%v ModuleID:%v "+
|
|
"CntThreads:%v ParentProcessID:%v PriorityClassBase:%v Flags:%v ExeFile:%v",
|
|
p.CntUsage, p.ProcessID, p.DefaultHeapID, p.ModuleID, p.CntThreads,
|
|
p.ParentProcessID, p.PriorityClassBase, p.Flags, p.ExeFile())
|
|
}
|
|
|
|
// MemoryStatusEx is an equivalent representation of MEMORYSTATUSEX in the
|
|
// Windows API. It contains information about the current state of both physical
|
|
// and virtual memory, including extended memory.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770
|
|
type MemoryStatusEx struct {
|
|
length uint32
|
|
MemoryLoad uint32
|
|
TotalPhys uint64
|
|
AvailPhys uint64
|
|
TotalPageFile uint64
|
|
AvailPageFile uint64
|
|
TotalVirtual uint64
|
|
AvailVirtual uint64
|
|
AvailExtendedVirtual uint64
|
|
}
|
|
|
|
// ProcessMemoryCountersEx is an equivalent representation of
|
|
// PROCESS_MEMORY_COUNTERS_EX in the Windows API.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx
|
|
type ProcessMemoryCountersEx struct {
|
|
cb uint32
|
|
PageFaultCount uint32
|
|
PeakWorkingSetSize uintptr
|
|
WorkingSetSize uintptr
|
|
QuotaPeakPagedPoolUsage uintptr
|
|
QuotaPagedPoolUsage uintptr
|
|
QuotaPeakNonPagedPoolUsage uintptr
|
|
QuotaNonPagedPoolUsage uintptr
|
|
PagefileUsage uintptr
|
|
PeakPagefileUsage uintptr
|
|
PrivateUsage uintptr
|
|
}
|
|
|
|
// GetLogicalDriveStrings returns a list of drives in the system.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364975(v=vs.85).aspx
|
|
func GetLogicalDriveStrings() ([]string, error) {
|
|
// Determine the size of the buffer required to receive all drives.
|
|
bufferLength, err := _GetLogicalDriveStringsW(0, nil)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetLogicalDriveStringsW failed to get buffer length")
|
|
}
|
|
if bufferLength < 0 {
|
|
return nil, errors.New("GetLogicalDriveStringsW returned an invalid buffer length")
|
|
}
|
|
|
|
buffer := make([]uint16, bufferLength)
|
|
_, err = _GetLogicalDriveStringsW(uint32(len(buffer)), &buffer[0])
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetLogicalDriveStringsW failed")
|
|
}
|
|
|
|
return UTF16SliceToStringSlice(buffer), nil
|
|
}
|
|
|
|
// GetAccessPaths returns the list of access paths for volumes in the system.
|
|
func GetAccessPaths() ([]string, error) {
|
|
volumes, err := GetVolumes()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetVolumes failed")
|
|
}
|
|
|
|
var paths []string
|
|
for _, volumeName := range volumes {
|
|
volumePaths, err := GetVolumePathsForVolume(volumeName)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to get list of access paths for volume '%s'", volumeName)
|
|
}
|
|
if len(volumePaths) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Get only the first path
|
|
paths = append(paths, volumePaths[0])
|
|
}
|
|
|
|
return paths, nil
|
|
}
|
|
|
|
// GetVolumes returs the list of volumes in the system.
|
|
// https://docs.microsoft.com/es-es/windows/desktop/api/fileapi/nf-fileapi-findfirstvolumew
|
|
func GetVolumes() ([]string, error) {
|
|
buffer := make([]uint16, MAX_PATH+1)
|
|
|
|
var volumes []string
|
|
|
|
h, err := _FindFirstVolume(&buffer[0], uint32(len(buffer)))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "FindFirstVolumeW failed")
|
|
}
|
|
defer _FindVolumeClose(h)
|
|
|
|
for {
|
|
volumes = append(volumes, syscall.UTF16ToString(buffer))
|
|
|
|
err = _FindNextVolume(h, &buffer[0], uint32(len(buffer)))
|
|
if err != nil {
|
|
if errors.Cause(err) == syscall.ERROR_NO_MORE_FILES {
|
|
break
|
|
}
|
|
return nil, errors.Wrap(err, "FindNextVolumeW failed")
|
|
}
|
|
}
|
|
|
|
return volumes, nil
|
|
}
|
|
|
|
// GetVolumePathsForVolume returns the list of volume paths for a volume.
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-getvolumepathnamesforvolumenamew
|
|
func GetVolumePathsForVolume(volumeName string) ([]string, error) {
|
|
var length uint32
|
|
err := _GetVolumePathNamesForVolumeName(volumeName, nil, 0, &length)
|
|
if errors.Cause(err) != syscall.ERROR_MORE_DATA {
|
|
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed to get needed buffer length")
|
|
}
|
|
if length == 0 {
|
|
// Not mounted, no paths, that's ok
|
|
return nil, nil
|
|
}
|
|
|
|
buffer := make([]uint16, length*(MAX_PATH+1))
|
|
err = _GetVolumePathNamesForVolumeName(volumeName, &buffer[0], length, &length)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed")
|
|
}
|
|
|
|
return UTF16SliceToStringSlice(buffer), nil
|
|
}
|
|
|
|
// GlobalMemoryStatusEx retrieves information about the system's current usage
|
|
// of both physical and virtual memory.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx
|
|
func GlobalMemoryStatusEx() (MemoryStatusEx, error) {
|
|
memoryStatusEx := MemoryStatusEx{length: sizeofMemoryStatusEx}
|
|
err := _GlobalMemoryStatusEx(&memoryStatusEx)
|
|
if err != nil {
|
|
return MemoryStatusEx{}, errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
|
}
|
|
|
|
return memoryStatusEx, nil
|
|
}
|
|
|
|
// GetProcessMemoryInfo retrieves information about the memory usage of the
|
|
// specified process.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683219(v=vs.85).aspx
|
|
func GetProcessMemoryInfo(handle syscall.Handle) (ProcessMemoryCountersEx, error) {
|
|
processMemoryCountersEx := ProcessMemoryCountersEx{cb: sizeofProcessMemoryCountersEx}
|
|
err := _GetProcessMemoryInfo(handle, &processMemoryCountersEx, processMemoryCountersEx.cb)
|
|
if err != nil {
|
|
return ProcessMemoryCountersEx{}, errors.Wrap(err, "GetProcessMemoryInfo failed")
|
|
}
|
|
|
|
return processMemoryCountersEx, nil
|
|
}
|
|
|
|
// GetProcessImageFileName Retrieves the name of the executable file for the
|
|
// specified process.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx
|
|
func GetProcessImageFileName(handle syscall.Handle) (string, error) {
|
|
buffer := make([]uint16, MAX_PATH)
|
|
_, err := _GetProcessImageFileName(handle, &buffer[0], uint32(len(buffer)))
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "GetProcessImageFileName failed")
|
|
}
|
|
|
|
return syscall.UTF16ToString(buffer), nil
|
|
}
|
|
|
|
// GetSystemTimes retrieves system timing information. On a multiprocessor
|
|
// system, the values returned are the sum of the designated times across all
|
|
// processors. The returned kernel time does not include the system idle time.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724400(v=vs.85).aspx
|
|
func GetSystemTimes() (idle, kernel, user time.Duration, err error) {
|
|
var idleTime, kernelTime, userTime syscall.Filetime
|
|
err = _GetSystemTimes(&idleTime, &kernelTime, &userTime)
|
|
if err != nil {
|
|
return 0, 0, 0, errors.Wrap(err, "GetSystemTimes failed")
|
|
}
|
|
|
|
idle = FiletimeToDuration(&idleTime)
|
|
kernel = FiletimeToDuration(&kernelTime) // Kernel time includes idle time so we subtract it out.
|
|
user = FiletimeToDuration(&userTime)
|
|
|
|
return idle, kernel - idle, user, nil
|
|
}
|
|
|
|
// FiletimeToDuration converts a Filetime to a time.Duration. Do not use this
|
|
// method to convert a Filetime to an actual clock time, for that use
|
|
// Filetime.Nanosecond().
|
|
func FiletimeToDuration(ft *syscall.Filetime) time.Duration {
|
|
n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
|
|
return time.Duration(n * 100)
|
|
}
|
|
|
|
// GetDriveType Determines whether a disk drive is a removable, fixed, CD-ROM,
|
|
// RAM disk, or network drive. A trailing backslash is required on the
|
|
// rootPathName.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364939
|
|
func GetDriveType(rootPathName string) (DriveType, error) {
|
|
rootPathNamePtr, err := syscall.UTF16PtrFromString(rootPathName)
|
|
if err != nil {
|
|
return DRIVE_UNKNOWN, errors.Wrapf(err, "UTF16PtrFromString failed for rootPathName=%v", rootPathName)
|
|
}
|
|
|
|
dt, err := _GetDriveType(rootPathNamePtr)
|
|
if err != nil {
|
|
return DRIVE_UNKNOWN, errors.Wrapf(err, "GetDriveType failed for rootPathName=%v", rootPathName)
|
|
}
|
|
|
|
return dt, nil
|
|
}
|
|
|
|
// GetFilesystemType returns file system type information at the given root path.
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
|
|
func GetFilesystemType(rootPathName string) (string, error) {
|
|
rootPathNamePtr, err := syscall.UTF16PtrFromString(rootPathName)
|
|
var systemType = "unavailable"
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "UTF16PtrFromString failed for rootPathName=%v", rootPathName)
|
|
}
|
|
buffer := make([]uint16, MAX_PATH+1)
|
|
// _GetVolumeInformation will fail for external drives like CD-ROM or other type with error codes as ERROR_NOT_READY. ERROR_INVALID_FUNCTION, ERROR_INVALID_PARAMETER, etc., these types of errors will be ignored
|
|
success, err := _GetVolumeInformation(rootPathNamePtr, nil, 0, nil, nil, nil, &buffer[0], MAX_PATH)
|
|
if success {
|
|
systemType = strings.ToLower(syscall.UTF16ToString(buffer))
|
|
}
|
|
return systemType, nil
|
|
}
|
|
|
|
// EnumProcesses retrieves the process identifier for each process object in the
|
|
// system. This function can return a max of 65536 PIDs. If there are more
|
|
// processes than that then this will not return them all.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682629(v=vs.85).aspx
|
|
func EnumProcesses() ([]uint32, error) {
|
|
enumProcesses := func(size int) ([]uint32, error) {
|
|
var (
|
|
pids = make([]uint32, size)
|
|
sizeBytes = len(pids) * sizeofUint32
|
|
bytesWritten uint32
|
|
)
|
|
|
|
err := _EnumProcesses(&pids[0], uint32(sizeBytes), &bytesWritten)
|
|
|
|
pidsWritten := int(bytesWritten) / sizeofUint32
|
|
if int(bytesWritten)%sizeofUint32 != 0 || pidsWritten > len(pids) {
|
|
return nil, errors.Errorf("EnumProcesses returned an invalid bytesWritten value of %v", bytesWritten)
|
|
}
|
|
pids = pids[:pidsWritten]
|
|
|
|
return pids, err
|
|
}
|
|
|
|
// Retry the EnumProcesses call with larger arrays if needed.
|
|
size := 2048
|
|
var pids []uint32
|
|
for tries := 0; tries < 5; tries++ {
|
|
var err error
|
|
pids, err = enumProcesses(size)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "EnumProcesses failed")
|
|
}
|
|
|
|
if len(pids) < size {
|
|
break
|
|
}
|
|
|
|
// Increase the size the pids array and retry the enumProcesses call
|
|
// because the array wasn't large enough to hold all of the processes.
|
|
size *= 2
|
|
}
|
|
|
|
return pids, nil
|
|
}
|
|
|
|
// GetDiskFreeSpaceEx retrieves information about the amount of space that is
|
|
// available on a disk volume, which is the total amount of space, the total
|
|
// amount of free space, and the total amount of free space available to the
|
|
// user that is associated with the calling thread.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364937(v=vs.85).aspx
|
|
func GetDiskFreeSpaceEx(directoryName string) (freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes uint64, err error) {
|
|
directoryNamePtr, err := syscall.UTF16PtrFromString(directoryName)
|
|
if err != nil {
|
|
return 0, 0, 0, errors.Wrapf(err, "UTF16PtrFromString failed for directoryName=%v", directoryName)
|
|
}
|
|
|
|
err = _GetDiskFreeSpaceEx(directoryNamePtr, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
|
|
return freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes, nil
|
|
}
|
|
|
|
// CreateToolhelp32Snapshot takes a snapshot of the specified processes, as well
|
|
// as the heaps, modules, and threads used by these processes.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682489(v=vs.85).aspx
|
|
func CreateToolhelp32Snapshot(flags, pid uint32) (syscall.Handle, error) {
|
|
h, err := _CreateToolhelp32Snapshot(flags, pid)
|
|
if err != nil {
|
|
return syscall.InvalidHandle, err
|
|
}
|
|
if h == syscall.InvalidHandle {
|
|
return syscall.InvalidHandle, syscall.GetLastError()
|
|
}
|
|
|
|
return h, nil
|
|
}
|
|
|
|
// Process32First retrieves information about the first process encountered in a
|
|
// system snapshot.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684834
|
|
func Process32First(handle syscall.Handle) (ProcessEntry32, error) {
|
|
processEntry32 := ProcessEntry32{size: sizeofProcessEntry32}
|
|
err := _Process32First(handle, &processEntry32)
|
|
if err != nil {
|
|
return ProcessEntry32{}, errors.Wrap(err, "Process32First failed")
|
|
}
|
|
|
|
return processEntry32, nil
|
|
}
|
|
|
|
// Process32Next retrieves information about the next process recorded in a
|
|
// system snapshot. When there are no more processes to iterate then
|
|
// syscall.ERROR_NO_MORE_FILES is returned (use errors.Cause() to unwrap).
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684836
|
|
func Process32Next(handle syscall.Handle) (ProcessEntry32, error) {
|
|
processEntry32 := ProcessEntry32{size: sizeofProcessEntry32}
|
|
err := _Process32Next(handle, &processEntry32)
|
|
if err != nil {
|
|
return ProcessEntry32{}, errors.Wrap(err, "Process32Next failed")
|
|
}
|
|
|
|
return processEntry32, nil
|
|
}
|
|
|
|
// UTF16SliceToStringSlice converts slice of uint16 containing a list of UTF16
|
|
// strings to a slice of strings.
|
|
func UTF16SliceToStringSlice(buffer []uint16) []string {
|
|
// Split the uint16 slice at null-terminators.
|
|
var startIdx int
|
|
var stringsUTF16 [][]uint16
|
|
for i, value := range buffer {
|
|
if value == 0 {
|
|
stringsUTF16 = append(stringsUTF16, buffer[startIdx:i])
|
|
startIdx = i + 1
|
|
}
|
|
}
|
|
|
|
// Convert the utf16 slices to strings.
|
|
result := make([]string, 0, len(stringsUTF16))
|
|
for _, stringUTF16 := range stringsUTF16 {
|
|
if len(stringUTF16) > 0 {
|
|
result = append(result, syscall.UTF16ToString(stringUTF16))
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func GetUserProcessParams(handle syscall.Handle, pbi ProcessBasicInformation) (params RtlUserProcessParameters, err error) {
|
|
const is32bitProc = unsafe.Sizeof(uintptr(0)) == 4
|
|
|
|
// Offset of params field within PEB structure.
|
|
// This structure is different in 32 and 64 bit.
|
|
paramsOffset := 0x20
|
|
if is32bitProc {
|
|
paramsOffset = 0x10
|
|
}
|
|
|
|
// Read the PEB from the target process memory
|
|
pebSize := paramsOffset + 8
|
|
peb := make([]byte, pebSize)
|
|
nRead, err := ReadProcessMemory(handle, pbi.PebBaseAddress, peb)
|
|
if err != nil {
|
|
return params, err
|
|
}
|
|
if nRead != uintptr(pebSize) {
|
|
return params, errors.Errorf("PEB: short read (%d/%d)", nRead, pebSize)
|
|
}
|
|
|
|
// Get the RTL_USER_PROCESS_PARAMETERS struct pointer from the PEB
|
|
paramsAddr := *(*uintptr)(unsafe.Pointer(&peb[paramsOffset]))
|
|
|
|
// Read the RTL_USER_PROCESS_PARAMETERS from the target process memory
|
|
paramsBuf := make([]byte, SizeOfRtlUserProcessParameters)
|
|
nRead, err = ReadProcessMemory(handle, paramsAddr, paramsBuf)
|
|
if err != nil {
|
|
return params, err
|
|
}
|
|
if nRead != uintptr(SizeOfRtlUserProcessParameters) {
|
|
return params, errors.Errorf("RTL_USER_PROCESS_PARAMETERS: short read (%d/%d)", nRead, SizeOfRtlUserProcessParameters)
|
|
}
|
|
|
|
params = *(*RtlUserProcessParameters)(unsafe.Pointer(¶msBuf[0]))
|
|
return params, nil
|
|
}
|
|
|
|
// ReadProcessUnicodeString returns a zero-terminated UTF-16 string from another
|
|
// process's memory.
|
|
func ReadProcessUnicodeString(handle syscall.Handle, s *UnicodeString) ([]byte, error) {
|
|
// Allocate an extra UTF-16 null character at the end in case the read string
|
|
// is not terminated.
|
|
extra := 2
|
|
if s.Size&1 != 0 {
|
|
extra = 3 // If size is odd, need 3 nulls to terminate.
|
|
}
|
|
buf := make([]byte, int(s.Size)+extra)
|
|
nRead, err := ReadProcessMemory(handle, s.Buffer, buf[:s.Size])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if nRead != uintptr(s.Size) {
|
|
return nil, errors.Errorf("unicode string: short read: (%d/%d)", nRead, s.Size)
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
// ByteSliceToStringSlice uses CommandLineToArgv API to split an UTF-16 command
|
|
// line string into a list of parameters.
|
|
func ByteSliceToStringSlice(utf16 []byte) ([]string, error) {
|
|
n := len(utf16)
|
|
// Discard odd byte
|
|
if n&1 != 0 {
|
|
n--
|
|
utf16 = utf16[:n]
|
|
}
|
|
if n == 0 {
|
|
return nil, nil
|
|
}
|
|
terminated := false
|
|
for i := 0; i < n && !terminated; i += 2 {
|
|
terminated = utf16[i] == 0 && utf16[i+1] == 0
|
|
}
|
|
if !terminated {
|
|
// Append a null uint16 at the end if terminator is missing
|
|
utf16 = append(utf16, 0, 0)
|
|
}
|
|
var numArgs int32
|
|
argsWide, err := syscall.CommandLineToArgv((*uint16)(unsafe.Pointer(&utf16[0])), &numArgs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Free memory allocated for CommandLineToArgvW arguments.
|
|
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(argsWide)))
|
|
|
|
args := make([]string, numArgs)
|
|
for idx := range args {
|
|
args[idx] = syscall.UTF16ToString(argsWide[idx][:])
|
|
}
|
|
return args, 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
|
|
}
|
|
|
|
func GetTickCount64() (uptime uint64, err error) {
|
|
if uptime, err = _GetTickCount64(); err != nil {
|
|
return 0, err
|
|
}
|
|
return uptime, nil
|
|
}
|
|
|
|
// Windows API calls
|
|
//sys _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) = kernel32.GlobalMemoryStatusEx
|
|
//sys _GetLogicalDriveStringsW(bufferLength uint32, buffer *uint16) (length uint32, err error) = kernel32.GetLogicalDriveStringsW
|
|
//sys _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCountersEx, cb uint32) (err error) = psapi.GetProcessMemoryInfo
|
|
//sys _GetProcessImageFileName(handle syscall.Handle, outImageFileName *uint16, size uint32) (length uint32, err error) = psapi.GetProcessImageFileNameW
|
|
//sys _GetSystemTimes(idleTime *syscall.Filetime, kernelTime *syscall.Filetime, userTime *syscall.Filetime) (err error) = kernel32.GetSystemTimes
|
|
//sys _GetDriveType(rootPathName *uint16) (dt DriveType, err error) = kernel32.GetDriveTypeW
|
|
//sys _GetVolumeInformation(rootPathName *uint16, volumeName *uint16, volumeNameSize uint32, volumeSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemName *uint16, fileSystemNameSize uint32) (success bool, err error) [true] = kernel32.GetVolumeInformationW
|
|
//sys _EnumProcesses(processIds *uint32, sizeBytes uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
|
|
//sys _GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailable *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) = kernel32.GetDiskFreeSpaceExW
|
|
//sys _Process32First(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) = kernel32.Process32FirstW
|
|
//sys _Process32Next(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) = kernel32.Process32NextW
|
|
//sys _CreateToolhelp32Snapshot(flags uint32, processID uint32) (handle syscall.Handle, err error) = kernel32.CreateToolhelp32Snapshot
|
|
//sys _NtQuerySystemInformation(systemInformationClass uint32, systemInformation *byte, systemInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) = ntdll.NtQuerySystemInformation
|
|
//sys _NtQueryInformationProcess(processHandle syscall.Handle, processInformationClass uint32, processInformation *byte, processInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) = ntdll.NtQueryInformationProcess
|
|
//sys _LookupPrivilegeName(systemName string, luid *int64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
|
//sys _LookupPrivilegeValue(systemName string, name string, luid *int64) (err error) = advapi32.LookupPrivilegeValueW
|
|
//sys _AdjustTokenPrivileges(token syscall.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
|
//sys _FindFirstVolume(volumeName *uint16, size uint32) (handle syscall.Handle, err error) = kernel32.FindFirstVolumeW
|
|
//sys _FindNextVolume(handle syscall.Handle, volumeName *uint16, size uint32) (err error) = kernel32.FindNextVolumeW
|
|
//sys _FindVolumeClose(handle syscall.Handle) (err error) = kernel32.FindVolumeClose
|
|
//sys _GetVolumePathNamesForVolumeName(volumeName string, buffer *uint16, bufferSize uint32, length *uint32) (err error) = kernel32.GetVolumePathNamesForVolumeNameW
|
|
//sys _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (err error) = kernel32.ReadProcessMemory
|
|
//sys _GetTickCount64() (uptime uint64, err error) = kernel32.GetTickCount64
|