1// +build windows 2 3package process 4 5import ( 6 "bufio" 7 "context" 8 "errors" 9 "fmt" 10 "io" 11 "os" 12 "strings" 13 "syscall" 14 "unsafe" 15 16 "github.com/shirou/gopsutil/v3/cpu" 17 "github.com/shirou/gopsutil/v3/internal/common" 18 "github.com/shirou/gopsutil/v3/net" 19 "golang.org/x/sys/windows" 20) 21 22var ( 23 modntdll = windows.NewLazySystemDLL("ntdll.dll") 24 procNtResumeProcess = modntdll.NewProc("NtResumeProcess") 25 procNtSuspendProcess = modntdll.NewProc("NtSuspendProcess") 26 27 modpsapi = windows.NewLazySystemDLL("psapi.dll") 28 procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") 29 procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW") 30 31 advapi32 = windows.NewLazySystemDLL("advapi32.dll") 32 procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") 33 procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") 34 35 procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW") 36 procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass") 37 procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters") 38 procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") 39 40 processorArchitecture uint 41) 42 43const processQueryInformation = windows.PROCESS_QUERY_LIMITED_INFORMATION 44 45type systemProcessorInformation struct { 46 ProcessorArchitecture uint16 47 ProcessorLevel uint16 48 ProcessorRevision uint16 49 Reserved uint16 50 ProcessorFeatureBits uint16 51} 52 53type systemInfo struct { 54 wProcessorArchitecture uint16 55 wReserved uint16 56 dwpageSize uint32 57 lpMinimumApplicationAddress uintptr 58 lpMaximumApplicationAddress uintptr 59 dwActiveProcessorMask uintptr 60 dwNumberOfProcessors uint32 61 dwProcessorType uint32 62 dwAllocationGranularity uint32 63 wProcessorLevel uint16 64 wProcessorRevision uint16 65} 66 67// Memory_info_ex is different between OSes 68type MemoryInfoExStat struct { 69} 70 71type MemoryMapsStat struct { 72} 73 74// ioCounters is an equivalent representation of IO_COUNTERS in the Windows API. 75// https://docs.microsoft.com/windows/win32/api/winnt/ns-winnt-io_counters 76type ioCounters struct { 77 ReadOperationCount uint64 78 WriteOperationCount uint64 79 OtherOperationCount uint64 80 ReadTransferCount uint64 81 WriteTransferCount uint64 82 OtherTransferCount uint64 83} 84 85type processBasicInformation32 struct { 86 Reserved1 uint32 87 PebBaseAddress uint32 88 Reserved2 uint32 89 Reserved3 uint32 90 UniqueProcessId uint32 91 Reserved4 uint32 92} 93 94type processBasicInformation64 struct { 95 Reserved1 uint64 96 PebBaseAddress uint64 97 Reserved2 uint64 98 Reserved3 uint64 99 UniqueProcessId uint64 100 Reserved4 uint64 101} 102 103type processEnvironmentBlock32 struct { 104 Reserved1 [2]uint8 105 BeingDebugged uint8 106 Reserved2 uint8 107 Reserved3 [2]uint32 108 Ldr uint32 109 ProcessParameters uint32 110 // More fields which we don't use so far 111} 112 113type processEnvironmentBlock64 struct { 114 Reserved1 [2]uint8 115 BeingDebugged uint8 116 Reserved2 uint8 117 _ [4]uint8 // padding, since we are 64 bit, the next pointer is 64 bit aligned (when compiling for 32 bit, this is not the case without manual padding) 118 Reserved3 [2]uint64 119 Ldr uint64 120 ProcessParameters uint64 121 // More fields which we don't use so far 122} 123 124type rtlUserProcessParameters32 struct { 125 Reserved1 [16]uint8 126 Reserved2 [10]uint32 127 ImagePathNameLength uint16 128 _ uint16 129 ImagePathAddress uint32 130 CommandLineLength uint16 131 _ uint16 132 CommandLineAddress uint32 133 EnvironmentAddress uint32 134 // More fields which we don't use so far 135} 136 137type rtlUserProcessParameters64 struct { 138 Reserved1 [16]uint8 139 Reserved2 [10]uint64 140 ImagePathNameLength uint16 141 _ uint16 // Max Length 142 _ uint32 // Padding 143 ImagePathAddress uint64 144 CommandLineLength uint16 145 _ uint16 // Max Length 146 _ uint32 // Padding 147 CommandLineAddress uint64 148 EnvironmentAddress uint64 149 // More fields which we don't use so far 150} 151 152type winLUID struct { 153 LowPart winDWord 154 HighPart winLong 155} 156 157// LUID_AND_ATTRIBUTES 158type winLUIDAndAttributes struct { 159 Luid winLUID 160 Attributes winDWord 161} 162 163// TOKEN_PRIVILEGES 164type winTokenPriviledges struct { 165 PrivilegeCount winDWord 166 Privileges [1]winLUIDAndAttributes 167} 168 169type winLong int32 170type winDWord uint32 171 172func init() { 173 var systemInfo systemInfo 174 175 procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo))) 176 processorArchitecture = uint(systemInfo.wProcessorArchitecture) 177 178 // enable SeDebugPrivilege https://github.com/midstar/proci/blob/6ec79f57b90ba3d9efa2a7b16ef9c9369d4be875/proci_windows.go#L80-L119 179 handle, err := syscall.GetCurrentProcess() 180 if err != nil { 181 return 182 } 183 184 var token syscall.Token 185 err = syscall.OpenProcessToken(handle, 0x0028, &token) 186 if err != nil { 187 return 188 } 189 defer token.Close() 190 191 tokenPriviledges := winTokenPriviledges{PrivilegeCount: 1} 192 lpName := syscall.StringToUTF16("SeDebugPrivilege") 193 ret, _, _ := procLookupPrivilegeValue.Call( 194 0, 195 uintptr(unsafe.Pointer(&lpName[0])), 196 uintptr(unsafe.Pointer(&tokenPriviledges.Privileges[0].Luid))) 197 if ret == 0 { 198 return 199 } 200 201 tokenPriviledges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED 202 203 procAdjustTokenPrivileges.Call( 204 uintptr(token), 205 0, 206 uintptr(unsafe.Pointer(&tokenPriviledges)), 207 uintptr(unsafe.Sizeof(tokenPriviledges)), 208 0, 209 0) 210} 211 212func pidsWithContext(ctx context.Context) ([]int32, error) { 213 // inspired by https://gist.github.com/henkman/3083408 214 // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 215 var ret []int32 216 var read uint32 = 0 217 var psSize uint32 = 1024 218 const dwordSize uint32 = 4 219 220 for { 221 ps := make([]uint32, psSize) 222 if err := windows.EnumProcesses(ps, &read); err != nil { 223 return nil, err 224 } 225 if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one 226 psSize += 1024 227 continue 228 } 229 for _, pid := range ps[:read/dwordSize] { 230 ret = append(ret, int32(pid)) 231 } 232 return ret, nil 233 234 } 235 236} 237 238func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { 239 if pid == 0 { // special case for pid 0 System Idle Process 240 return true, nil 241 } 242 if pid < 0 { 243 return false, fmt.Errorf("invalid pid %v", pid) 244 } 245 if pid%4 != 0 { 246 // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 247 // so we list every pid just to be sure and be future-proof 248 pids, err := PidsWithContext(ctx) 249 if err != nil { 250 return false, err 251 } 252 for _, i := range pids { 253 if i == pid { 254 return true, err 255 } 256 } 257 return false, err 258 } 259 const STILL_ACTIVE = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess 260 h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) 261 if err == windows.ERROR_ACCESS_DENIED { 262 return true, nil 263 } 264 if err == windows.ERROR_INVALID_PARAMETER { 265 return false, nil 266 } 267 if err != nil { 268 return false, err 269 } 270 defer syscall.CloseHandle(syscall.Handle(h)) 271 var exitCode uint32 272 err = windows.GetExitCodeProcess(h, &exitCode) 273 return exitCode == STILL_ACTIVE, err 274} 275 276func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { 277 // if cached already, return from cache 278 cachedPpid := p.getPpid() 279 if cachedPpid != 0 { 280 return cachedPpid, nil 281 } 282 283 ppid, _, _, err := getFromSnapProcess(p.Pid) 284 if err != nil { 285 return 0, err 286 } 287 288 // no errors and not cached already, so cache it 289 p.setPpid(ppid) 290 291 return ppid, nil 292} 293 294func (p *Process) NameWithContext(ctx context.Context) (string, error) { 295 ppid, _, name, err := getFromSnapProcess(p.Pid) 296 if err != nil { 297 return "", fmt.Errorf("could not get Name: %s", err) 298 } 299 300 // if no errors and not cached already, cache ppid 301 p.parent = ppid 302 if 0 == p.getPpid() { 303 p.setPpid(ppid) 304 } 305 306 return name, nil 307} 308 309func (p *Process) TgidWithContext(ctx context.Context) (int32, error) { 310 return 0, common.ErrNotImplementedError 311} 312 313func (p *Process) ExeWithContext(ctx context.Context) (string, error) { 314 c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) 315 if err != nil { 316 return "", err 317 } 318 defer windows.CloseHandle(c) 319 buf := make([]uint16, syscall.MAX_LONG_PATH) 320 size := uint32(syscall.MAX_LONG_PATH) 321 if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+ 322 ret, _, err := procQueryFullProcessImageNameW.Call( 323 uintptr(c), 324 uintptr(0), 325 uintptr(unsafe.Pointer(&buf[0])), 326 uintptr(unsafe.Pointer(&size))) 327 if ret == 0 { 328 return "", err 329 } 330 return windows.UTF16ToString(buf[:]), nil 331 } 332 // XP fallback 333 ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size)) 334 if ret == 0 { 335 return "", err 336 } 337 return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil 338} 339 340func (p *Process) CmdlineWithContext(_ context.Context) (string, error) { 341 cmdline, err := getProcessCommandLine(p.Pid) 342 if err != nil { 343 return "", fmt.Errorf("could not get CommandLine: %s", err) 344 } 345 return cmdline, nil 346} 347 348func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { 349 cmdline, err := p.CmdlineWithContext(ctx) 350 if err != nil { 351 return nil, err 352 } 353 return strings.Split(cmdline, " "), nil 354} 355 356func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) { 357 ru, err := getRusage(p.Pid) 358 if err != nil { 359 return 0, fmt.Errorf("could not get CreationDate: %s", err) 360 } 361 362 return ru.CreationTime.Nanoseconds() / 1000000, nil 363} 364 365func (p *Process) CwdWithContext(ctx context.Context) (string, error) { 366 return "", common.ErrNotImplementedError 367} 368 369func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { 370 ppid, err := p.PpidWithContext(ctx) 371 if err != nil { 372 return nil, fmt.Errorf("could not get ParentProcessID: %s", err) 373 } 374 375 return NewProcessWithContext(ctx, ppid) 376} 377 378func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) { 379 return []string{""}, common.ErrNotImplementedError 380} 381 382func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { 383 return false, common.ErrNotImplementedError 384} 385 386func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { 387 pid := p.Pid 388 c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) 389 if err != nil { 390 return "", err 391 } 392 defer windows.CloseHandle(c) 393 394 var token syscall.Token 395 err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token) 396 if err != nil { 397 return "", err 398 } 399 defer token.Close() 400 tokenUser, err := token.GetTokenUser() 401 if err != nil { 402 return "", err 403 } 404 405 user, domain, _, err := tokenUser.User.Sid.LookupAccount("") 406 return domain + "\\" + user, err 407} 408 409func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) { 410 return nil, common.ErrNotImplementedError 411} 412 413func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) { 414 return nil, common.ErrNotImplementedError 415} 416 417func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) { 418 return nil, common.ErrNotImplementedError 419} 420 421func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { 422 return "", common.ErrNotImplementedError 423} 424 425// priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority 426// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass 427// https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process 428var priorityClasses = map[int]int32{ 429 0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS 430 0x00004000: 6, // BELOW_NORMAL_PRIORITY_CLASS 431 0x00000080: 13, // HIGH_PRIORITY_CLASS 432 0x00000040: 4, // IDLE_PRIORITY_CLASS 433 0x00000020: 8, // NORMAL_PRIORITY_CLASS 434 0x00000100: 24, // REALTIME_PRIORITY_CLASS 435} 436 437func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { 438 c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) 439 if err != nil { 440 return 0, err 441 } 442 defer windows.CloseHandle(c) 443 ret, _, err := procGetPriorityClass.Call(uintptr(c)) 444 if ret == 0 { 445 return 0, err 446 } 447 priority, ok := priorityClasses[int(ret)] 448 if !ok { 449 return 0, fmt.Errorf("unknown priority class %v", ret) 450 } 451 return priority, nil 452} 453 454func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) { 455 return 0, common.ErrNotImplementedError 456} 457 458func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) { 459 return nil, common.ErrNotImplementedError 460} 461 462func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) { 463 return nil, common.ErrNotImplementedError 464} 465 466func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) { 467 c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) 468 if err != nil { 469 return nil, err 470 } 471 defer windows.CloseHandle(c) 472 var ioCounters ioCounters 473 ret, _, err := procGetProcessIoCounters.Call(uintptr(c), uintptr(unsafe.Pointer(&ioCounters))) 474 if ret == 0 { 475 return nil, err 476 } 477 stats := &IOCountersStat{ 478 ReadCount: ioCounters.ReadOperationCount, 479 ReadBytes: ioCounters.ReadTransferCount, 480 WriteCount: ioCounters.WriteOperationCount, 481 WriteBytes: ioCounters.WriteTransferCount, 482 } 483 484 return stats, nil 485} 486 487func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) { 488 return nil, common.ErrNotImplementedError 489} 490 491func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { 492 return 0, common.ErrNotImplementedError 493} 494 495func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { 496 ppid, ret, _, err := getFromSnapProcess(p.Pid) 497 if err != nil { 498 return 0, err 499 } 500 501 // if no errors and not cached already, cache ppid 502 p.parent = ppid 503 if 0 == p.getPpid() { 504 p.setPpid(ppid) 505 } 506 507 return ret, nil 508} 509 510func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) { 511 return nil, common.ErrNotImplementedError 512} 513 514func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { 515 sysTimes, err := getProcessCPUTimes(p.Pid) 516 if err != nil { 517 return nil, err 518 } 519 520 // User and kernel times are represented as a FILETIME structure 521 // which contains a 64-bit value representing the number of 522 // 100-nanosecond intervals since January 1, 1601 (UTC): 523 // http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx 524 // To convert it into a float representing the seconds that the 525 // process has executed in user/kernel mode I borrowed the code 526 // below from psutil's _psutil_windows.c, and in turn from Python's 527 // Modules/posixmodule.c 528 529 user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7 530 kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7 531 532 return &cpu.TimesStat{ 533 User: user, 534 System: kernel, 535 }, nil 536} 537 538func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) { 539 return nil, common.ErrNotImplementedError 540} 541 542func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { 543 mem, err := getMemoryInfo(p.Pid) 544 if err != nil { 545 return nil, err 546 } 547 548 ret := &MemoryInfoStat{ 549 RSS: uint64(mem.WorkingSetSize), 550 VMS: uint64(mem.PagefileUsage), 551 } 552 553 return ret, nil 554} 555 556func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) { 557 return nil, common.ErrNotImplementedError 558} 559 560func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) { 561 return nil, common.ErrNotImplementedError 562} 563 564func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { 565 out := []*Process{} 566 snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(0)) 567 if err != nil { 568 return out, err 569 } 570 defer windows.CloseHandle(snap) 571 var pe32 windows.ProcessEntry32 572 pe32.Size = uint32(unsafe.Sizeof(pe32)) 573 if err := windows.Process32First(snap, &pe32); err != nil { 574 return out, err 575 } 576 for { 577 if pe32.ParentProcessID == uint32(p.Pid) { 578 p, err := NewProcessWithContext(ctx, int32(pe32.ProcessID)) 579 if err == nil { 580 out = append(out, p) 581 } 582 } 583 if err = windows.Process32Next(snap, &pe32); err != nil { 584 break 585 } 586 } 587 return out, nil 588} 589 590func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) { 591 return nil, common.ErrNotImplementedError 592} 593 594func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) { 595 return net.ConnectionsPidWithContext(ctx, "all", p.Pid) 596} 597 598func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) { 599 return nil, common.ErrNotImplementedError 600} 601 602func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) { 603 return nil, common.ErrNotImplementedError 604} 605 606func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error { 607 return common.ErrNotImplementedError 608} 609 610func (p *Process) SuspendWithContext(ctx context.Context) error { 611 c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) 612 if err != nil { 613 return err 614 } 615 defer windows.CloseHandle(c) 616 617 r1, _, _ := procNtSuspendProcess.Call(uintptr(c)) 618 if r1 != 0 { 619 // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 620 return fmt.Errorf("NtStatus='0x%.8X'", r1) 621 } 622 623 return nil 624} 625 626func (p *Process) ResumeWithContext(ctx context.Context) error { 627 c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid)) 628 if err != nil { 629 return err 630 } 631 defer windows.CloseHandle(c) 632 633 r1, _, _ := procNtResumeProcess.Call(uintptr(c)) 634 if r1 != 0 { 635 // See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 636 return fmt.Errorf("NtStatus='0x%.8X'", r1) 637 } 638 639 return nil 640} 641 642func (p *Process) TerminateWithContext(ctx context.Context) error { 643 proc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, uint32(p.Pid)) 644 if err != nil { 645 return err 646 } 647 err = windows.TerminateProcess(proc, 0) 648 windows.CloseHandle(proc) 649 return err 650} 651 652func (p *Process) KillWithContext(ctx context.Context) error { 653 process := os.Process{Pid: int(p.Pid)} 654 return process.Kill() 655} 656 657func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) { 658 envVars, err := getProcessEnvironmentVariables(p.Pid, ctx) 659 if err != nil { 660 return nil, fmt.Errorf("could not get environment variables: %s", err) 661 } 662 return envVars, nil 663} 664 665// retrieve Ppid in a thread-safe manner 666func (p *Process) getPpid() int32 { 667 p.parentMutex.RLock() 668 defer p.parentMutex.RUnlock() 669 return p.parent 670} 671 672// cache Ppid in a thread-safe manner (WINDOWS ONLY) 673// see https://psutil.readthedocs.io/en/latest/#psutil.Process.ppid 674func (p *Process) setPpid(ppid int32) { 675 p.parentMutex.Lock() 676 defer p.parentMutex.Unlock() 677 p.parent = ppid 678} 679 680func getFromSnapProcess(pid int32) (int32, int32, string, error) { 681 snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid)) 682 if err != nil { 683 return 0, 0, "", err 684 } 685 defer windows.CloseHandle(snap) 686 var pe32 windows.ProcessEntry32 687 pe32.Size = uint32(unsafe.Sizeof(pe32)) 688 if err = windows.Process32First(snap, &pe32); err != nil { 689 return 0, 0, "", err 690 } 691 for { 692 if pe32.ProcessID == uint32(pid) { 693 szexe := windows.UTF16ToString(pe32.ExeFile[:]) 694 return int32(pe32.ParentProcessID), int32(pe32.Threads), szexe, nil 695 } 696 if err = windows.Process32Next(snap, &pe32); err != nil { 697 break 698 } 699 } 700 return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid) 701} 702 703func ProcessesWithContext(ctx context.Context) ([]*Process, error) { 704 out := []*Process{} 705 706 pids, err := PidsWithContext(ctx) 707 if err != nil { 708 return out, fmt.Errorf("could not get Processes %s", err) 709 } 710 711 for _, pid := range pids { 712 p, err := NewProcessWithContext(ctx, pid) 713 if err != nil { 714 continue 715 } 716 out = append(out, p) 717 } 718 719 return out, nil 720} 721 722func getRusage(pid int32) (*windows.Rusage, error) { 723 var CPU windows.Rusage 724 725 c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) 726 if err != nil { 727 return nil, err 728 } 729 defer windows.CloseHandle(c) 730 731 if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil { 732 return nil, err 733 } 734 735 return &CPU, nil 736} 737 738func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { 739 var mem PROCESS_MEMORY_COUNTERS 740 c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) 741 if err != nil { 742 return mem, err 743 } 744 defer windows.CloseHandle(c) 745 if err := getProcessMemoryInfo(c, &mem); err != nil { 746 return mem, err 747 } 748 749 return mem, err 750} 751 752func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) { 753 r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem))) 754 if r1 == 0 { 755 if e1 != 0 { 756 err = error(e1) 757 } else { 758 err = syscall.EINVAL 759 } 760 } 761 return 762} 763 764type SYSTEM_TIMES struct { 765 CreateTime syscall.Filetime 766 ExitTime syscall.Filetime 767 KernelTime syscall.Filetime 768 UserTime syscall.Filetime 769} 770 771func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { 772 var times SYSTEM_TIMES 773 774 h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid)) 775 if err != nil { 776 return times, err 777 } 778 defer windows.CloseHandle(h) 779 780 err = syscall.GetProcessTimes( 781 syscall.Handle(h), 782 ×.CreateTime, 783 ×.ExitTime, 784 ×.KernelTime, 785 ×.UserTime, 786 ) 787 788 return times, err 789} 790 791 792func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) { 793 pebAddress, err := queryPebAddress(syscall.Handle(handle), true) 794 if err != nil { 795 return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err) 796 } 797 798 buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{}))) 799 if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) { 800 return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB") 801 } 802 peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0])) 803 userProcessAddress := uint64(peb.ProcessParameters) 804 buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{}))) 805 if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) { 806 return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters") 807 } 808 return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil 809} 810 811func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) { 812 pebAddress, err := queryPebAddress(syscall.Handle(handle), false) 813 if err != nil { 814 return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err) 815 } 816 817 buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{}))) 818 if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock64{})) { 819 return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB") 820 } 821 peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0])) 822 userProcessAddress := peb.ProcessParameters 823 buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{}))) 824 if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters64{})) { 825 return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters") 826 } 827 return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil 828} 829 830func is32BitProcess(h windows.Handle) bool { 831 const ( 832 PROCESSOR_ARCHITECTURE_INTEL = 0 833 PROCESSOR_ARCHITECTURE_ARM = 5 834 PROCESSOR_ARCHITECTURE_ARM64 = 12 835 PROCESSOR_ARCHITECTURE_IA64 = 6 836 PROCESSOR_ARCHITECTURE_AMD64 = 9 837 ) 838 839 var procIs32Bits bool 840 switch processorArchitecture { 841 case PROCESSOR_ARCHITECTURE_INTEL: 842 fallthrough 843 case PROCESSOR_ARCHITECTURE_ARM: 844 procIs32Bits = true 845 case PROCESSOR_ARCHITECTURE_ARM64: 846 fallthrough 847 case PROCESSOR_ARCHITECTURE_IA64: 848 fallthrough 849 case PROCESSOR_ARCHITECTURE_AMD64: 850 var wow64 uint 851 852 ret, _, _ := common.ProcNtQueryInformationProcess.Call( 853 uintptr(h), 854 uintptr(common.ProcessWow64Information), 855 uintptr(unsafe.Pointer(&wow64)), 856 uintptr(unsafe.Sizeof(wow64)), 857 uintptr(0), 858 ) 859 if int(ret) >= 0 { 860 if wow64 != 0 { 861 procIs32Bits = true 862 } 863 } else { 864 //if the OS does not support the call, we fallback into the bitness of the app 865 if unsafe.Sizeof(wow64) == 4 { 866 procIs32Bits = true 867 } 868 } 869 870 default: 871 //for other unknown platforms, we rely on process platform 872 if unsafe.Sizeof(processorArchitecture) == 8 { 873 procIs32Bits = false 874 } else { 875 procIs32Bits = true 876 } 877 } 878 return procIs32Bits 879} 880 881func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) { 882 h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) 883 if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { 884 return nil, nil 885 } 886 if err != nil { 887 return nil, err 888 } 889 defer syscall.CloseHandle(syscall.Handle(h)) 890 891 procIs32Bits := is32BitProcess(h) 892 893 var processParameterBlockAddress uint64 894 895 if procIs32Bits { 896 peb, err := getUserProcessParams32(h) 897 if err != nil { 898 return nil, err 899 } 900 processParameterBlockAddress = uint64(peb.EnvironmentAddress) 901 } else { 902 peb, err := getUserProcessParams64(h) 903 if err != nil { 904 return nil, err 905 } 906 processParameterBlockAddress = peb.EnvironmentAddress 907 } 908 envvarScanner := bufio.NewScanner(&processReader{ 909 processHandle: h, 910 is32BitProcess: procIs32Bits, 911 offset: processParameterBlockAddress, 912 }) 913 envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error){ 914 if atEOF && len(data) == 0 { 915 return 0, nil, nil 916 } 917 // Check for UTF-16 zero character 918 for i := 0; i < len(data) - 1; i+=2 { 919 if data[i] == 0 && data[i+1] == 0 { 920 return i+2, data[0:i], nil 921 } 922 } 923 if atEOF { 924 return len(data), data, nil 925 } 926 // Request more data 927 return 0, nil, nil 928 }) 929 var envVars []string 930 for envvarScanner.Scan() { 931 entry := envvarScanner.Bytes() 932 if len(entry) == 0 { 933 break // Block is finished 934 } 935 envVars = append(envVars, convertUTF16ToString(entry)) 936 select { 937 case <-ctx.Done(): 938 break 939 default: 940 continue 941 } 942 } 943 if err := envvarScanner.Err(); err != nil { 944 return nil, err 945 } 946 return envVars, nil 947} 948 949type processReader struct { 950 processHandle windows.Handle 951 is32BitProcess bool 952 offset uint64 953} 954 955func (p *processReader) Read(buf []byte) (int, error) { 956 processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf))) 957 if len(processMemory) == 0 { 958 return 0, io.EOF 959 } 960 copy(buf, processMemory) 961 p.offset += uint64(len(processMemory)) 962 return len(processMemory), nil 963} 964 965func getProcessCommandLine(pid int32) (string, error) { 966 h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid)) 967 if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { 968 return "", nil 969 } 970 if err != nil { 971 return "", err 972 } 973 defer syscall.CloseHandle(syscall.Handle(h)) 974 975 procIs32Bits := is32BitProcess(h) 976 977 if procIs32Bits { 978 userProcParams, err := getUserProcessParams32(h) 979 if err != nil { 980 return "", err 981 } 982 if userProcParams.CommandLineLength > 0 { 983 cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CommandLineAddress), uint(userProcParams.CommandLineLength)) 984 if len(cmdLine) != int(userProcParams.CommandLineLength) { 985 return "", errors.New("cannot read cmdline") 986 } 987 988 return convertUTF16ToString(cmdLine), nil 989 } 990 } else { 991 userProcParams, err := getUserProcessParams64(h) 992 if err != nil { 993 return "", err 994 } 995 if userProcParams.CommandLineLength > 0 { 996 cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength)) 997 if len(cmdLine) != int(userProcParams.CommandLineLength) { 998 return "", errors.New("cannot read cmdline") 999 } 1000 1001 return convertUTF16ToString(cmdLine), nil 1002 } 1003 } 1004 1005 //if we reach here, we have no command line 1006 return "", nil 1007} 1008 1009func convertUTF16ToString(src []byte) string { 1010 srcLen := len(src) / 2 1011 1012 codePoints := make([]uint16, srcLen) 1013 1014 srcIdx := 0 1015 for i := 0; i < srcLen; i++ { 1016 codePoints[i] = uint16(src[srcIdx]) | uint16(src[srcIdx+1])<<8 1017 srcIdx += 2 1018 } 1019 return syscall.UTF16ToString(codePoints) 1020} 1021