From 6e955aa454d3c2c5ec063bae5313ae915c9f1a08 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Mon, 23 Dec 2024 16:21:58 +0100 Subject: [PATCH] feat(event): Append callstack for OpenProcess/OpenThread events The callstack is included in the event parameters. For the sake of performance, we're deriving the callstack only for the interesting flag set. --- internal/etw/source_test.go | 34 ++++++++++++++++++++++++++++++++++ pkg/kevent/flags.go | 7 +++++-- pkg/kevent/kparam_windows.go | 16 ++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/internal/etw/source_test.go b/internal/etw/source_test.go index 9da72ff39..8af626abd 100644 --- a/internal/etw/source_test.go +++ b/internal/etw/source_test.go @@ -1162,6 +1162,40 @@ func testCallstackEnrichment(t *testing.T, hsnap handle.Snapshotter, psnap ps.Sn }, false, }, + { + "open process callstack", + func() error { + _, err := windows.OpenProcess(windows.PROCESS_VM_READ, false, uint32(os.Getpid())) + return err + }, + func(e *kevent.Kevent) bool { + if e.CurrentPid() && e.Type == ktypes.OpenProcess { + callstack := e.Callstack.String() + log.Infof("open process event %s: %s", e.String(), callstack) + return callstackContainsTestExe(callstack) && + strings.Contains(strings.ToLower(callstack), strings.ToLower("\\WINDOWS\\System32\\KERNELBASE.dll!OpenProcess")) + } + return false + }, + false, + }, + { + "open thread callstack", + func() error { + _, err := windows.OpenThread(windows.THREAD_IMPERSONATE, false, windows.GetCurrentThreadId()) + return err + }, + func(e *kevent.Kevent) bool { + if e.CurrentPid() && e.Type == ktypes.OpenThread { + callstack := e.Callstack.String() + log.Infof("open thread event %s: %s", e.String(), callstack) + return callstackContainsTestExe(callstack) && + strings.Contains(strings.ToLower(callstack), strings.ToLower("\\WINDOWS\\System32\\KERNELBASE.dll!OpenThread")) + } + return false + }, + false, + }, } kstreamConfig := config.KstreamConfig{ diff --git a/pkg/kevent/flags.go b/pkg/kevent/flags.go index 1298631aa..71b63c82a 100644 --- a/pkg/kevent/flags.go +++ b/pkg/kevent/flags.go @@ -76,9 +76,12 @@ var PsCreationFlags = []ParamFlag{ {"PACKAGED", PsPackaged}, } +// AllAccess represents the maximum process/thread access right +const AllAccess = windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xFFFF + // PsAccessRightFlags describes flags for the process access rights. var PsAccessRightFlags = []ParamFlag{ - {"ALL_ACCESS", windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xFFFF}, + {"ALL_ACCESS", AllAccess}, {"DELETE", windows.DELETE}, {"READ_CONTROL", windows.READ_CONTROL}, {"SYNCHRONIZE", windows.SYNCHRONIZE}, @@ -102,7 +105,7 @@ var PsAccessRightFlags = []ParamFlag{ // ThreadAccessRightFlags describes flags for the thread access rights. var ThreadAccessRightFlags = []ParamFlag{ - {"ALL_ACCESS", windows.STANDARD_RIGHTS_REQUIRED | windows.SYNCHRONIZE | 0xFFFF}, + {"ALL_ACCESS", AllAccess}, {"DELETE", windows.DELETE}, {"READ_CONTROL", windows.READ_CONTROL}, {"SYNCHRONIZE", windows.SYNCHRONIZE}, diff --git a/pkg/kevent/kparam_windows.go b/pkg/kevent/kparam_windows.go index a4ff13310..2e87055cd 100644 --- a/pkg/kevent/kparam_windows.go +++ b/pkg/kevent/kparam_windows.go @@ -269,6 +269,14 @@ func (e *Kevent) produceParams(evt *etw.EventRecord) { e.AppendParam(kparams.ProcessID, kparams.PID, processID) e.AppendParam(kparams.DesiredAccess, kparams.Flags, desiredAccess, WithFlags(PsAccessRightFlags)) e.AppendParam(kparams.NTStatus, kparams.Status, status) + + // append callstack for interested flags + if desiredAccess == AllAccess || ((desiredAccess & windows.PROCESS_VM_READ) != 0) || ((desiredAccess & windows.PROCESS_VM_WRITE) != 0) || + ((desiredAccess & windows.PROCESS_VM_OPERATION) != 0) || ((desiredAccess & windows.PROCESS_DUP_HANDLE) != 0) || + ((desiredAccess & windows.PROCESS_TERMINATE) != 0) || ((desiredAccess & windows.PROCESS_CREATE_PROCESS) != 0) || + ((desiredAccess & windows.PROCESS_CREATE_THREAD) != 0) || ((desiredAccess & windows.PROCESS_SET_INFORMATION) != 0) { + e.AppendParam(kparams.Callstack, kparams.Slice, evt.Callstack()) + } case ktypes.CreateThread, ktypes.TerminateThread, ktypes.ThreadRundown: @@ -323,6 +331,14 @@ func (e *Kevent) produceParams(evt *etw.EventRecord) { e.AppendParam(kparams.ThreadID, kparams.TID, threadID) e.AppendParam(kparams.DesiredAccess, kparams.Flags, desiredAccess, WithFlags(ThreadAccessRightFlags)) e.AppendParam(kparams.NTStatus, kparams.Status, status) + + // append callstack for interested flags + if desiredAccess == AllAccess || ((desiredAccess & windows.THREAD_SET_CONTEXT) != 0) || ((desiredAccess & windows.THREAD_SET_THREAD_TOKEN) != 0) || + ((desiredAccess & windows.THREAD_IMPERSONATE) != 0) || ((desiredAccess & windows.THREAD_DIRECT_IMPERSONATION) != 0) || + ((desiredAccess & windows.THREAD_SUSPEND_RESUME) != 0) || ((desiredAccess & windows.THREAD_TERMINATE) != 0) || + ((desiredAccess & windows.THREAD_SET_INFORMATION) != 0) { + e.AppendParam(kparams.Callstack, kparams.Slice, evt.Callstack()) + } case ktypes.SetThreadContext: status := evt.ReadUint32(0) e.AppendParam(kparams.NTStatus, kparams.Status, status)