 5978a0b8f5
			
		
	
	5978a0b8f5
	
	
	
		
			
			- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			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
 |