Skip to content

Commit 88edf01

Browse files
committed
feat(stack): Enable callstack for VirtualAlloc events
The callstack for VirtualAlloc events is enabled with this change. Sadly, MapViewFile/UnmapViewFile events don't yield userspace stack address, so we can't consider them useful.
1 parent 94d5bcf commit 88edf01

File tree

5 files changed

+59
-19
lines changed

5 files changed

+59
-19
lines changed

internal/etw/source_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,27 @@ func testCallstackEnrichment(t *testing.T, hsnap handle.Snapshotter, psnap ps.Sn
10891089
},
10901090
false,
10911091
},
1092+
{
1093+
"virtual alloc callstack",
1094+
func() error {
1095+
_, err := windows.VirtualAlloc(0, 1024, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READ)
1096+
if err != nil {
1097+
return err
1098+
}
1099+
return nil
1100+
},
1101+
func(e *kevent.Kevent) bool {
1102+
if e.CurrentPid() && e.Type == ktypes.VirtualAlloc &&
1103+
e.GetParamAsString(kparams.MemAllocType) == "COMMIT|RESERVE" {
1104+
callstack := e.Callstack.String()
1105+
log.Infof("virtual alloc event %s: %s", e.String(), callstack)
1106+
return callstackContainsTestExe(callstack) &&
1107+
strings.Contains(strings.ToLower(callstack), strings.ToLower("\\Windows\\System32\\KernelBase.dll!VirtualAlloc"))
1108+
}
1109+
return false
1110+
},
1111+
false,
1112+
},
10921113
//{
10931114
// "copy file callstack",
10941115
// func() error {
@@ -1196,6 +1217,7 @@ func testCallstackEnrichment(t *testing.T, hsnap handle.Snapshotter, psnap ps.Sn
11961217
time.Sleep(time.Second * 5)
11971218

11981219
log.Infof("current process id is [%d]", os.Getpid())
1220+
11991221
for _, tt := range tests {
12001222
gen := tt.gen
12011223
log.Infof("executing [%s] test generator", tt.name)

internal/etw/stackext.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ func (s *StackExtensions) AddStackTracingWith(guid windows.GUID, hookID uint16)
5454
// EventIds returns all event types eligible for stack tracing.
5555
func (s *StackExtensions) EventIds() []etw.ClassicEventID { return s.ids }
5656

57-
// EnableProcessStackTracing populates the stack identifiers
57+
// EnableProcessCallstack populates the stack identifiers
5858
// with event types eligible for emitting stack walk events
5959
// related to process telemetry, such as creating a process,
6060
// creating/terminating a thread or loading an image into
6161
// process address space.
62-
func (s *StackExtensions) EnableProcessStackTracing() {
62+
func (s *StackExtensions) EnableProcessCallstack() {
6363
s.AddStackTracing(ktypes.CreateProcess)
6464
if s.config.EnableThreadKevents {
6565
s.AddStackTracing(ktypes.CreateThread)
@@ -70,25 +70,33 @@ func (s *StackExtensions) EnableProcessStackTracing() {
7070
}
7171
}
7272

73-
// EnableFileStackTracing populates the stack identifiers
73+
// EnableFileCallstack populates the stack identifiers
7474
// with event types eligible for publishing call stack
7575
// return addresses for file system activity.
76-
func (s *StackExtensions) EnableFileStackTracing() {
76+
func (s *StackExtensions) EnableFileCallstack() {
7777
if s.config.EnableFileIOKevents {
7878
s.AddStackTracing(ktypes.CreateFile)
7979
s.AddStackTracing(ktypes.DeleteFile)
8080
s.AddStackTracing(ktypes.RenameFile)
8181
}
8282
}
8383

84-
// EnableRegistryStackTracing populates the stack identifiers
84+
// EnableRegistryCallstack populates the stack identifiers
8585
// with event types eligible for publishing call stack
8686
// return addresses for registry operations.
87-
func (s *StackExtensions) EnableRegistryStackTracing() {
87+
func (s *StackExtensions) EnableRegistryCallstack() {
8888
if s.config.EnableRegistryKevents {
8989
s.AddStackTracing(ktypes.RegCreateKey)
9090
s.AddStackTracing(ktypes.RegDeleteKey)
9191
s.AddStackTracing(ktypes.RegSetValue)
9292
s.AddStackTracing(ktypes.RegDeleteValue)
9393
}
9494
}
95+
96+
// EnableMemoryCallstack enables stack tracing for the memory
97+
// events such as memory allocations.
98+
func (s *StackExtensions) EnableMemoryCallstack() {
99+
if s.config.EnableMemKevents {
100+
s.AddStackTracing(ktypes.VirtualAlloc)
101+
}
102+
}

internal/etw/stackext_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,25 @@ func TestStackExtensions(t *testing.T) {
3333
EnableThreadKevents: true,
3434
EnableNetKevents: true,
3535
EnableFileIOKevents: true,
36+
EnableMemKevents: true,
3637
BufferSize: 1024,
3738
FlushTimer: time.Millisecond * 2300,
3839
},
3940
}
4041
exts := NewStackExtensions(cfg.Kstream)
4142
assert.Len(t, exts.EventIds(), 0)
4243

43-
exts.EnableProcessStackTracing()
44-
exts.EnableRegistryStackTracing()
45-
exts.EnableFileStackTracing()
44+
exts.EnableProcessCallstack()
45+
exts.EnableRegistryCallstack()
46+
exts.EnableFileCallstack()
47+
exts.EnableMemoryCallstack()
4648

47-
assert.Len(t, exts.EventIds(), 6)
49+
assert.Len(t, exts.EventIds(), 7)
4850
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ProcessEventGUID, Type: uint8(ktypes.CreateProcess.HookID())})
4951
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ThreadEventGUID, Type: uint8(ktypes.CreateThread.HookID())})
5052
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.ThreadEventGUID, Type: uint8(ktypes.TerminateThread.HookID())})
5153
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.CreateFile.HookID())})
5254
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.RenameFile.HookID())})
5355
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.FileEventGUID, Type: uint8(ktypes.DeleteFile.HookID())})
56+
assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: ktypes.MemEventGUID, Type: uint8(ktypes.VirtualAlloc.HookID())})
5457
}

internal/etw/trace.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,30 @@ type Trace struct {
125125
// NewTrace creates a new trace with specified name, provider GUID, and keywords.
126126
func NewTrace(name string, guid windows.GUID, keywords uint64, config *config.Config) *Trace {
127127
t := &Trace{Name: name, GUID: guid, Keywords: keywords, stackExtensions: NewStackExtensions(config.Kstream), config: config}
128-
t.prepareStackTracing()
128+
t.enableCallstacks()
129129
return t
130130
}
131131

132-
func (t *Trace) prepareStackTracing() {
132+
func (t *Trace) enableCallstacks() {
133133
if t.IsKernelTrace() {
134+
t.stackExtensions.EnableProcessCallstack()
135+
134136
// Enabling stack tracing for kernel trace
135137
// with granular system providers support.
136-
// In this situation, only stack tracing for
137-
// file system events is enabled
138-
t.stackExtensions.EnableProcessStackTracing()
138+
// In this situation, registry event callstacks
139+
// are enabled if system provider support is not
140+
// detected
139141
if !SupportsSystemProviders() {
140-
t.stackExtensions.EnableRegistryStackTracing()
142+
t.stackExtensions.EnableRegistryCallstack()
141143
}
142-
t.stackExtensions.EnableFileStackTracing()
144+
145+
t.stackExtensions.EnableFileCallstack()
146+
147+
t.stackExtensions.EnableMemoryCallstack()
143148
}
149+
144150
if t.IsSystemRegistryTrace() {
145-
t.stackExtensions.EnableRegistryStackTracing()
151+
t.stackExtensions.EnableRegistryCallstack()
146152
}
147153
}
148154

pkg/kevent/ktypes/ktypes_windows.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ func (k Ktype) CanEnrichStack() bool {
504504
RegSetValue,
505505
RegDeleteValue,
506506
DeleteFile,
507-
RenameFile:
507+
RenameFile,
508+
VirtualAlloc:
508509
return true
509510
default:
510511
return false

0 commit comments

Comments
 (0)