diff --git a/process/process_test.go b/process/process_test.go index 40e3639..bdb2c99 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -1,6 +1,7 @@ package process import ( + "context" "errors" "fmt" "io/ioutil" @@ -168,6 +169,37 @@ func Test_Process_Ppid(t *testing.T) { t.Errorf("return value is %v, expected %v", v, expected) } } +func Test_Process_PpidViaPEB(t *testing.T) { + p := testGetProcess() + + v, err := p.PpidWithContextViaPEB(context.Background()) + skipIfNotImplementedErr(t, err) + if err != nil { + t.Errorf("getting ppid error %v", err) + } + if v == 0 { + t.Errorf("return value is 0 %v", v) + } + expected := os.Getppid() + if v != int32(expected) { + t.Errorf("return value is %v, expected %v", v, expected) + } +} + +func Test_Process_PpidViaPEB_Same_As_OG(t *testing.T) { + procs, err := Processes() + skipIfNotImplementedErr(t, err) + if err != nil { + t.Fatalf("getting processes error %v", err) + } + for _, proc := range procs { + expected, _ := proc.Ppid() + got, _ := proc.PpidWithContextViaPEB(context.Background()) + if expected != got { + t.Errorf("pid %v: return value is %v, expected %v", proc.Pid, got, expected) + } + } +} func Test_Process_Status(t *testing.T) { p := testGetProcess() @@ -858,3 +890,9 @@ func BenchmarkProcessPpid(b *testing.B) { p.Ppid() } } +func BenchmarkProcessPpidViaPEB(b *testing.B) { + p := testGetProcess() + for i := 0; i < b.N; i++ { + p.PpidWithContextViaPEB(context.Background()) + } +} diff --git a/process/process_windows.go b/process/process_windows.go index 14ed030..985c9ba 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -93,7 +93,7 @@ type processBasicInformation32 struct { Reserved2 uint32 Reserved3 uint32 UniqueProcessId uint32 - Reserved4 uint32 + InheritedFromUniqueProcessID uint32 } type processBasicInformation64 struct { @@ -102,7 +102,7 @@ type processBasicInformation64 struct { Reserved2 uint64 Reserved3 uint64 UniqueProcessId uint64 - Reserved4 uint64 + InheritedFromUniqueProcessID uint64 } type processEnvironmentBlock32 struct { @@ -302,20 +302,38 @@ func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) { } func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { - // if cached already, return from cache - cachedPpid := p.getPpid() - if cachedPpid != 0 { - return cachedPpid, nil + ppid, _, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err } - ppid, _, _, err := getFromSnapProcess(p.Pid) + return ppid, nil +} + +func (p *Process) PpidWithContextViaPEB(ctx context.Context) (int32, error) { + h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid)) + if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER { + ppid, _, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ppid, nil + } if err != nil { return 0, err } + defer syscall.CloseHandle(syscall.Handle(h)) - // no errors and not cached already, so cache it - p.setPpid(ppid) + procIs32Bits := is32BitProcess(h) + _, ppid, err := queryPebAddress(syscall.Handle(h), procIs32Bits) + if err != nil || ppid == -1 { + ppid, _, _, err := getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ppid, nil + } return ppid, nil } @@ -941,7 +959,7 @@ func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { } func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) { - pebAddress, err := queryPebAddress(syscall.Handle(handle), true) + pebAddress, _, err := queryPebAddress(syscall.Handle(handle), true) if err != nil { return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err) } @@ -960,7 +978,7 @@ func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, } func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) { - pebAddress, err := queryPebAddress(syscall.Handle(handle), false) + pebAddress, _, err := queryPebAddress(syscall.Handle(handle), false) if err != nil { return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err) } diff --git a/process/process_windows_32bit.go b/process/process_windows_32bit.go index 982287d..be993a1 100644 --- a/process/process_windows_32bit.go +++ b/process/process_windows_32bit.go @@ -26,7 +26,7 @@ type PROCESS_MEMORY_COUNTERS struct { PeakPagefileUsage uint32 } -func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) { +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, int32, error) { if is32BitProcess { // we are on a 32-bit process reading an external 32-bit process var info processBasicInformation32 @@ -39,9 +39,9 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er uintptr(0), ) if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { - return uint64(info.PebBaseAddress), nil + return uint64(info.PebBaseAddress), int32(info.InheritedFromUniqueProcessID), nil } else { - return 0, windows.NTStatus(ret) + return 0, -1, windows.NTStatus(ret) } } else { // we are on a 32-bit process reading an external 64-bit process @@ -56,12 +56,12 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er uintptr(0), ) if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { - return info.PebBaseAddress, nil + return info.PebBaseAddress, int32(info.InheritedFromUniqueProcessID), nil } else { - return 0, windows.NTStatus(ret) + return 0, -1, windows.NTStatus(ret) } } else { - return 0, errors.New("can't find API to query 64 bit process from 32 bit") + return 0, -1, errors.New("can't find API to query 64 bit process from 32 bit") } } } diff --git a/process/process_windows_64bit.go b/process/process_windows_64bit.go index 74c6212..2d11a37 100644 --- a/process/process_windows_64bit.go +++ b/process/process_windows_64bit.go @@ -24,7 +24,7 @@ type PROCESS_MEMORY_COUNTERS struct { PeakPagefileUsage uint64 } -func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, error) { +func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, int32, error) { if is32BitProcess { // we are on a 64-bit process reading an external 32-bit process var wow64 uint @@ -37,9 +37,9 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er uintptr(0), ) if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { - return uint64(wow64), nil + return uint64(wow64), -1, nil } else { - return 0, windows.NTStatus(ret) + return 0, -1, windows.NTStatus(ret) } } else { // we are on a 64-bit process reading an external 64-bit process @@ -53,9 +53,9 @@ func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) (uint64, er uintptr(0), ) if status := windows.NTStatus(ret); status == windows.STATUS_SUCCESS { - return info.PebBaseAddress, nil + return info.PebBaseAddress, int32(info.InheritedFromUniqueProcessID), nil } else { - return 0, windows.NTStatus(ret) + return 0, -1, windows.NTStatus(ret) } } }