diff --git a/internal/etw/source_test.go b/internal/etw/source_test.go index 9da72ff39..67d589169 100644 --- a/internal/etw/source_test.go +++ b/internal/etw/source_test.go @@ -1089,6 +1089,27 @@ func testCallstackEnrichment(t *testing.T, hsnap handle.Snapshotter, psnap ps.Sn }, false, }, + { + "virtual alloc callstack", + func() error { + _, err := windows.VirtualAlloc(0, 1024, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READ) + if err != nil { + return err + } + return nil + }, + func(e *kevent.Kevent) bool { + if e.CurrentPid() && e.Type == ktypes.VirtualAlloc && + e.GetParamAsString(kparams.MemAllocType) == "COMMIT|RESERVE" { + callstack := e.Callstack.String() + log.Infof("virtual alloc event %s: %s", e.String(), callstack) + return callstackContainsTestExe(callstack) && + strings.Contains(strings.ToLower(callstack), strings.ToLower("\\Windows\\System32\\KernelBase.dll!VirtualAlloc")) + } + return false + }, + false, + }, //{ // "copy file callstack", // func() error { @@ -1196,6 +1217,7 @@ func testCallstackEnrichment(t *testing.T, hsnap handle.Snapshotter, psnap ps.Sn time.Sleep(time.Second * 5) log.Infof("current process id is [%d]", os.Getpid()) + for _, tt := range tests { gen := tt.gen log.Infof("executing [%s] test generator", tt.name) diff --git a/internal/etw/stackext.go b/internal/etw/stackext.go index 77d97904c..396df8617 100644 --- a/internal/etw/stackext.go +++ b/internal/etw/stackext.go @@ -54,12 +54,12 @@ func (s *StackExtensions) AddStackTracingWith(guid windows.GUID, hookID uint16) // EventIds returns all event types eligible for stack tracing. func (s *StackExtensions) EventIds() []etw.ClassicEventID { return s.ids } -// EnableProcessStackTracing populates the stack identifiers +// EnableProcessCallstack populates the stack identifiers // with event types eligible for emitting stack walk events // related to process telemetry, such as creating a process, // creating/terminating a thread or loading an image into // process address space. -func (s *StackExtensions) EnableProcessStackTracing() { +func (s *StackExtensions) EnableProcessCallstack() { s.AddStackTracing(ktypes.CreateProcess) if s.config.EnableThreadKevents { s.AddStackTracing(ktypes.CreateThread) @@ -70,10 +70,10 @@ func (s *StackExtensions) EnableProcessStackTracing() { } } -// EnableFileStackTracing populates the stack identifiers +// EnableFileCallstack populates the stack identifiers // with event types eligible for publishing call stack // return addresses for file system activity. -func (s *StackExtensions) EnableFileStackTracing() { +func (s *StackExtensions) EnableFileCallstack() { if s.config.EnableFileIOKevents { s.AddStackTracing(ktypes.CreateFile) s.AddStackTracing(ktypes.DeleteFile) @@ -81,10 +81,10 @@ func (s *StackExtensions) EnableFileStackTracing() { } } -// EnableRegistryStackTracing populates the stack identifiers +// EnableRegistryCallstack populates the stack identifiers // with event types eligible for publishing call stack // return addresses for registry operations. -func (s *StackExtensions) EnableRegistryStackTracing() { +func (s *StackExtensions) EnableRegistryCallstack() { if s.config.EnableRegistryKevents { s.AddStackTracing(ktypes.RegCreateKey) s.AddStackTracing(ktypes.RegDeleteKey) @@ -92,3 +92,11 @@ func (s *StackExtensions) EnableRegistryStackTracing() { s.AddStackTracing(ktypes.RegDeleteValue) } } + +// EnableMemoryCallstack enables stack tracing for the memory +// events such as memory allocations. +func (s *StackExtensions) EnableMemoryCallstack() { + if s.config.EnableMemKevents { + s.AddStackTracing(ktypes.VirtualAlloc) + } +} diff --git a/internal/etw/stackext_test.go b/internal/etw/stackext_test.go index 8f813e60e..0f8457b86 100644 --- a/internal/etw/stackext_test.go +++ b/internal/etw/stackext_test.go @@ -33,6 +33,7 @@ func TestStackExtensions(t *testing.T) { EnableThreadKevents: true, EnableNetKevents: true, EnableFileIOKevents: true, + EnableMemKevents: true, BufferSize: 1024, FlushTimer: time.Millisecond * 2300, }, @@ -40,15 +41,17 @@ func TestStackExtensions(t *testing.T) { exts := NewStackExtensions(cfg.Kstream) assert.Len(t, exts.EventIds(), 0) - exts.EnableProcessStackTracing() - exts.EnableRegistryStackTracing() - exts.EnableFileStackTracing() + exts.EnableProcessCallstack() + exts.EnableRegistryCallstack() + exts.EnableFileCallstack() + exts.EnableMemoryCallstack() - assert.Len(t, exts.EventIds(), 6) + assert.Len(t, exts.EventIds(), 7) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ProcessEventGUID, Type: uint8(ktypes.CreateProcess.HookID())}) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ThreadEventGUID, Type: uint8(ktypes.CreateThread.HookID())}) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ThreadEventGUID, Type: uint8(ktypes.TerminateThread.HookID())}) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.CreateFile.HookID())}) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.RenameFile.HookID())}) assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.DeleteFile.HookID())}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.MemEventGUID, Type: uint8(ktypes.VirtualAlloc.HookID())}) } diff --git a/internal/etw/trace.go b/internal/etw/trace.go index 64e3b0f0d..3f268e8d4 100644 --- a/internal/etw/trace.go +++ b/internal/etw/trace.go @@ -125,24 +125,30 @@ type Trace struct { // NewTrace creates a new trace with specified name, provider GUID, and keywords. func NewTrace(name string, guid windows.GUID, keywords uint64, config *config.Config) *Trace { t := &Trace{Name: name, GUID: guid, Keywords: keywords, stackExtensions: NewStackExtensions(config.Kstream), config: config} - t.prepareStackTracing() + t.enableCallstacks() return t } -func (t *Trace) prepareStackTracing() { +func (t *Trace) enableCallstacks() { if t.IsKernelTrace() { + t.stackExtensions.EnableProcessCallstack() + // Enabling stack tracing for kernel trace // with granular system providers support. - // In this situation, only stack tracing for - // file system events is enabled - t.stackExtensions.EnableProcessStackTracing() + // In this situation, registry event callstacks + // are enabled if system provider support is not + // detected if !SupportsSystemProviders() { - t.stackExtensions.EnableRegistryStackTracing() + t.stackExtensions.EnableRegistryCallstack() } - t.stackExtensions.EnableFileStackTracing() + + t.stackExtensions.EnableFileCallstack() + + t.stackExtensions.EnableMemoryCallstack() } + if t.IsSystemRegistryTrace() { - t.stackExtensions.EnableRegistryStackTracing() + t.stackExtensions.EnableRegistryCallstack() } } diff --git a/pkg/kevent/kevent_windows.go b/pkg/kevent/kevent_windows.go index ec35d5c61..e4f8f83e8 100644 --- a/pkg/kevent/kevent_windows.go +++ b/pkg/kevent/kevent_windows.go @@ -83,7 +83,10 @@ func (e *Kevent) adjustPID() { e.PID, _ = e.Kparams.GetPid() } case ktypes.File: - e.Tid, _ = e.Kparams.GetTid() + if !e.IsMapViewFile() && !e.IsUnmapViewFile() { + // take thread id from the event parameters + e.Tid, _ = e.Kparams.GetTid() + } switch { case e.InvalidPid() && e.Type == ktypes.MapFileRundown: // a valid pid for map rundown events diff --git a/pkg/kevent/ktypes/ktypes_windows.go b/pkg/kevent/ktypes/ktypes_windows.go index fe10be09e..43ea219ee 100644 --- a/pkg/kevent/ktypes/ktypes_windows.go +++ b/pkg/kevent/ktypes/ktypes_windows.go @@ -504,7 +504,8 @@ func (k Ktype) CanEnrichStack() bool { RegSetValue, RegDeleteValue, DeleteFile, - RenameFile: + RenameFile, + VirtualAlloc: return true default: return false diff --git a/pkg/symbolize/symbolizer.go b/pkg/symbolize/symbolizer.go index 8521f0ab0..e4be4ff6e 100644 --- a/pkg/symbolize/symbolizer.go +++ b/pkg/symbolize/symbolizer.go @@ -285,7 +285,15 @@ func (s *Symbolizer) syncModules(e *kevent.Kevent) error { func (s *Symbolizer) processCallstack(e *kevent.Kevent) error { addrs := e.Kparams.MustGetSliceAddrs(kparams.Callstack) e.Callstack.Init(len(addrs)) - if (e.IsCreateFile() && e.IsOpenDisposition()) || (e.IsSystemPid() && !e.IsLoadImage()) { + + // skip stack enrichment for the events generated by the System process + // except the LoadImage event which may prove to be useful when the driver + // is loaded and the kernel address symbolization is enabled + if e.IsSystemPid() && !e.IsLoadImage() { + return nil + } + + if e.IsCreateFile() && e.IsOpenDisposition() { // for high-volume events decorating // the frames with symbol information // is not viable. For this reason, the