From 9351dac28110f410e14a9c9272dda471dbbb80b4 Mon Sep 17 00:00:00 2001 From: Chris Talib Date: Mon, 21 Oct 2024 13:40:27 +0200 Subject: [PATCH] first commit --- go-ps.go | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 + 2 files changed, 233 insertions(+) create mode 100644 go-ps.go create mode 100644 go.mod diff --git a/go-ps.go b/go-ps.go new file mode 100644 index 0000000..12d1af4 --- /dev/null +++ b/go-ps.go @@ -0,0 +1,230 @@ +//go:build windows +// +build windows + +// https://github.com/mitchellh/go-ps/blob/master/process_windows.go +// With addition from me on querying using NTDLL and not just kernel32 + +package ps + +import ( + "fmt" + "syscall" + "unsafe" +) + +// Windows API functions +var ( + modKernel32 = syscall.NewLazyDLL("kernel32.dll") + procCloseHandle = modKernel32.NewProc("CloseHandle") + procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot") + procProcess32First = modKernel32.NewProc("Process32FirstW") + procProcess32Next = modKernel32.NewProc("Process32NextW") + + // NTDLL + modNtDll = windows.NewLazySystemDLL("ntdll.dll") + procNtQuerySystemInformation = modNtDll.NewProc("NtQuerySystemInformation") +) + +// Some constants from the Windows API +const ( + ERROR_NO_MORE_FILES = 0x12 + MAX_PATH = 260 + + SystemProcessInformation = 5 +) + +// PROCESSENTRY32 is the Windows API structure that contains a process's +// information. +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 +} + +// WindowsProcess is an implementation of Process for Windows. +type WindowsProcess struct { + pid int + ppid int + exe string +} + +type SYSTEM_PROCESS_INFORMATION struct { + NextEntryOffset uint32 + NumberOfThreads uint32 + Reserved1 [3]uint64 + CreateTime int64 + UserTime int64 + KernelTime int64 + ImageName windows.NTUnicodeString + BasePriority int32 + UniqueProcessID uintptr + InheritedFromUniqueProcessID uintptr + HandleCount uint32 + SessionID uint32 + PageDirectoryBase uintptr + PeakVirtualSize uint64 + VirtualSize uint64 + PageFaultCount uint32 + PeakWorkingSetSize uint64 + WorkingSetSize uint64 + QuotaPeakPagedPoolUsage uint64 + QuotaPagedPoolUsage uint64 + QuotaPeakNonPagedPoolUsage uint64 + QuotaNonPagedPoolUsage uint64 + PagefileUsage uint64 + PeakPagefileUsage uint64 + PrivatePageCount uint64 + Reserved2 [6]uint64 +} + +// ps provides an API for finding and listing processes in a platform-agnostic +// way. +// +// NOTE: If you're reading these docs online via GoDocs or some other system, +// you might only see the Unix docs. This project makes heavy use of +// platform-specific implementations. We recommend reading the source if you +// are interested. + +// Process is the generic interface that is implemented on every platform +// and provides common operations for processes. +type Process interface { + // Pid is the process ID for this process. + Pid() int + + // PPid is the parent process ID for this process. + PPid() int + + // Executable name running this process. This is not a path to the + // executable. + Executable() string +} + +// Processes returns all processes. +// +// This of course will be a point-in-time snapshot of when this method was +// called. Some operating systems don't provide snapshot capability of the +// process table, in which case the process table returned might contain +// ephemeral entities that happened to be running when this was called. +func Processes(method string) ([]Process, error) { + if method == "k32" { + return processes32() + } else if method == "ntdll" { + return processesNTDLL() + } +} + +// FindProcess looks up a single process by pid. +// +// Process will be nil and error will be nil if a matching process is +// not found. +func FindProcess(pid int) (Process, error) { + return findProcess(pid) +} + +func (p *WindowsProcess) Pid() int { + return p.pid +} + +func (p *WindowsProcess) PPid() int { + return p.ppid +} + +func (p *WindowsProcess) Executable() string { + return p.exe +} + +func newWindowsProcess(e *PROCESSENTRY32) *WindowsProcess { + // Find when the string ends for decoding + end := 0 + for { + if e.ExeFile[end] == 0 { + break + } + end++ + } + + return &WindowsProcess{ + pid: int(e.ProcessID), + ppid: int(e.ParentProcessID), + exe: syscall.UTF16ToString(e.ExeFile[:end]), + } +} + +func findProcess(pid int) (Process, error) { + ps, err := processes() + if err != nil { + return nil, err + } + + for _, p := range ps { + if p.Pid() == pid { + return p, nil + } + } + + return nil, nil +} + +func processesk32() ([]Process, error) { + handle, _, _ := procCreateToolhelp32Snapshot.Call( + 0x00000002, + 0) + if handle < 0 { + return nil, syscall.GetLastError() + } + defer procCloseHandle.Call(handle) + + var entry PROCESSENTRY32 + entry.Size = uint32(unsafe.Sizeof(entry)) + ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry))) + if ret == 0 { + return nil, fmt.Errorf("Error retrieving process info.") + } + + results := make([]Process, 0, 50) + for { + results = append(results, newWindowsProcess(&entry)) + + ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry))) + if ret == 0 { + break + } + } + + return results, nil +} + +func processesNTDLL() ([]SYSTEM_PROCESS_INFORMATION, error) { + var buffer [1024 * 1024]byte + var returnLength uint32 + + r1, _, err := procNtQuerySystemInformation.Call( + uintptr(SystemProcessInformation), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(len(buffer)), + uintptr(unsafe.Pointer(&returnLength)), + ) + if r1 != 0 { + return nil, err + } + + var processes []SYSTEM_PROCESS_INFORMATION + offset := 0 + for { + spi := (*SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(&buffer[offset])) + processes = append(processes, *spi) + if spi.NextEntryOffset == 0 { + break + } + offset += int(spi.NextEntryOffset) + } + + return processes, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..108dba6 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.postblue.info/chris/go-ps + +go 1.21.6