From 3302398e2e85b4468e9375d0df42ee1e979efc66 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Sat, 20 Dec 2025 18:06:46 +0100 Subject: [PATCH] fix(ps): Make executable path assignation more robust Introduce a new metadata attribute in the process state to distinguish how the initial process state is created - either from internal event or system logger. This attribute is utilized in the state machine to set the correct executable path. --- pkg/ps/snapshotter_windows.go | 16 +++++++++------- pkg/ps/snapshotter_windows_test.go | 15 ++++++++++----- pkg/ps/types/types_windows.go | 13 +++++++++---- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/pkg/ps/snapshotter_windows.go b/pkg/ps/snapshotter_windows.go index 68801b5ac..ac86648eb 100644 --- a/pkg/ps/snapshotter_windows.go +++ b/pkg/ps/snapshotter_windows.go @@ -20,15 +20,16 @@ package ps import ( "expvar" - "github.com/rabbitstack/fibratus/pkg/sys" - "github.com/rabbitstack/fibratus/pkg/util/va" - "golang.org/x/sys/windows" "path/filepath" "strconv" "strings" "sync" "time" + "github.com/rabbitstack/fibratus/pkg/sys" + "github.com/rabbitstack/fibratus/pkg/util/va" + "golang.org/x/sys/windows" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -161,17 +162,17 @@ func (s *snapshotter) Write(e *event.Event) error { if ps := s.procs[pid]; ps == nil && (e.IsCreateProcessInternal() || e.IsProcessRundownInternal()) { // only modify the state if there is no process derived from the NT kernel logger process events s.procs[pid] = proc - } else if ps, ok := s.procs[pid]; ok && (e.IsCreateProcessInternal() || e.IsProcessRundownInternal()) { + } else if ps, ok := s.procs[pid]; ok && (e.IsCreateProcessInternal() || e.IsProcessRundownInternal()) && ps.IsCreatedFromSystemLogger { // process state derived from the core kernel events exists - enrich it ps.TokenIntegrityLevel = proc.TokenIntegrityLevel ps.TokenElevationType = proc.TokenElevationType ps.IsTokenElevated = proc.IsTokenElevated - if len(proc.Exe) > len(ps.Exe) { + if strings.EqualFold(ps.Name, filepath.Base(proc.Exe)) && len(proc.Exe) > len(ps.Exe) { // prefer full executable path ps.Exe = proc.Exe } s.procs[pid] = ps - } else if ps, ok := s.procs[pid]; ok && (e.IsCreateProcess() || e.IsProcessRundown()) && ps.TokenIntegrityLevel != "" { + } else if ps, ok := s.procs[pid]; ok && (e.IsCreateProcess() || e.IsProcessRundown()) && !ps.IsCreatedFromSystemLogger { // enrich the existing process state with the newly arrived NT kernel logger process events // but obtain the integrity level and executable path from the previous proc state processEnrichments.Add(1) @@ -179,7 +180,7 @@ func (s *snapshotter) Write(e *event.Event) error { proc.TokenElevationType = ps.TokenElevationType proc.IsTokenElevated = ps.IsTokenElevated - if len(ps.Exe) > len(proc.Exe) { + if strings.EqualFold(proc.Name, filepath.Base(ps.Exe)) && len(ps.Exe) > len(proc.Exe) { // prefer full executable path proc.Exe = ps.Exe e.AppendParam(params.Exe, params.Path, ps.Exe) @@ -397,6 +398,7 @@ func (s *snapshotter) newProcState(pid, ppid uint32, e *event.Event) (*pstypes.P proc.IsWOW64 = (e.Params.MustGetUint32(params.ProcessFlags) & event.PsWOW64) != 0 proc.IsPackaged = (e.Params.MustGetUint32(params.ProcessFlags) & event.PsPackaged) != 0 proc.IsProtected = (e.Params.MustGetUint32(params.ProcessFlags) & event.PsProtected) != 0 + proc.IsCreatedFromSystemLogger = true if !s.capture { if proc.Username != "" { diff --git a/pkg/ps/snapshotter_windows_test.go b/pkg/ps/snapshotter_windows_test.go index 254c76d2f..5340a96c8 100644 --- a/pkg/ps/snapshotter_windows_test.go +++ b/pkg/ps/snapshotter_windows_test.go @@ -19,6 +19,12 @@ package ps import ( + "os" + "path/filepath" + "strings" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -31,11 +37,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "os" - "path/filepath" - "strings" - "testing" - "time" ) func TestWrite(t *testing.T) { @@ -227,6 +228,7 @@ func TestWriteInternalEventsEnrichment(t *testing.T) { params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1024)}, params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(444)}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: `svchost.exe`}, + params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: `svchost.exe`}, params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: `svchost.exe -k LocalSystemNetworkRestricted -p -s NcbService`}, params.UserSID: {Name: params.UserSID, Type: params.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}}, params.SessionID: {Name: params.SessionID, Type: params.Uint32, Value: uint32(1)}, @@ -265,6 +267,7 @@ func TestWriteInternalEventsEnrichment(t *testing.T) { params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1024)}, params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(444)}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: `C:\Windows\System32\svchost.exe`}, + params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: `svchost.exe`}, params.ProcessTokenIntegrityLevel: {Name: params.ProcessTokenIntegrityLevel, Type: params.AnsiString, Value: "HIGH"}, params.ProcessTokenIsElevated: {Name: params.ProcessTokenIsElevated, Type: params.Bool, Value: true}, params.ProcessTokenElevationType: {Name: params.ProcessTokenElevationType, Type: params.AnsiString, Value: "FULL"}, @@ -275,6 +278,7 @@ func TestWriteInternalEventsEnrichment(t *testing.T) { Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1024)}, params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(444)}, + params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: `svchost.exe`}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: `svchost.exe`}, params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: `svchost.exe -k LocalSystemNetworkRestricted -p -s NcbService`}, params.UserSID: {Name: params.UserSID, Type: params.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}}, @@ -303,6 +307,7 @@ func TestWriteInternalEventsEnrichment(t *testing.T) { params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(os.Getpid())}, params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(444)}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: `svchost.exe`}, + params.ProcessName: {Name: params.ProcessName, Type: params.UnicodeString, Value: `svchost.exe`}, params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: `svchost.exe -k LocalSystemNetworkRestricted -p -s NcbService`}, params.UserSID: {Name: params.UserSID, Type: params.WbemSID, Value: []byte{224, 8, 226, 31, 15, 167, 255, 255, 0, 0, 0, 0, 15, 167, 255, 255, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0}}, params.SessionID: {Name: params.SessionID, Type: params.Uint32, Value: uint32(1)}, diff --git a/pkg/ps/types/types_windows.go b/pkg/ps/types/types_windows.go index 3d22d484f..f4976eaf6 100644 --- a/pkg/ps/types/types_windows.go +++ b/pkg/ps/types/types_windows.go @@ -21,20 +21,22 @@ package types import ( "encoding/binary" "fmt" + "path/filepath" + "strings" + "sync" + "github.com/rabbitstack/fibratus/pkg/sys" "github.com/rabbitstack/fibratus/pkg/util/cmdline" "github.com/rabbitstack/fibratus/pkg/util/va" "golang.org/x/sys/windows" - "path/filepath" - "strings" - "sync" "github.com/rabbitstack/fibratus/pkg/cap/section" htypes "github.com/rabbitstack/fibratus/pkg/handle/types" "github.com/rabbitstack/fibratus/pkg/pe" - "github.com/rabbitstack/fibratus/pkg/util/bootid" "time" + + "github.com/rabbitstack/fibratus/pkg/util/bootid" ) // PS encapsulates process' state such as allocated resources and other metadata. @@ -98,6 +100,9 @@ type PS struct { TokenElevationType string `json:"token_elevation_type"` // IsTokenElevated indicates if the process token is elevated. IsTokenElevated bool `json:"is_token_elevated"` + // IsCreatedFromSystemLogger is the metadata attribute that indicates if the + // process state is created from the event published by the NT kernel logger. + IsCreatedFromSystemLogger bool `json:"-"` } // UUID is meant to offer a more robust version of process ID that