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