Skip to content

Commit 345187f

Browse files
authored
feat: improve parsing in etw receiver (#2378)
* improve parsing in etw receiver * conditionally forward the event correlation information * fix lint * add go build tag
1 parent b7d122a commit 345187f

File tree

6 files changed

+114
-24
lines changed

6 files changed

+114
-24
lines changed

receiver/windowseventtracereceiver/internal/etw/advapi32/api.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,10 @@ const (
613613
EVENT_TRACE_REAL_TIME_MODE = 0x00000100
614614
)
615615

616+
const (
617+
EVENT_ENABLE_PROPERTY_SID = 0x00000001
618+
)
619+
616620
const (
617621
WNODE_FLAG_ALL_DATA uint32 = 0x00000001
618622
WNODE_FLAG_SINGLE_INSTANCE uint32 = 0x00000002
@@ -676,6 +680,7 @@ func (e *EventRecord) PointerSize() uint32 {
676680

677681
const (
678682
EventHeaderExtTypeRelatedActivityID = 0x0001
683+
EventHeaderExtTypeSID = 0x0002
679684
)
680685

681686
func (e *EventRecord) ExtendedDataItem(i uint16) *EventHeaderExtendedDataItem {
@@ -688,12 +693,23 @@ func (e *EventRecord) ExtendedDataItem(i uint16) *EventHeaderExtendedDataItem {
688693
func (e *EventRecord) RelatedActivityID() string {
689694
for i := uint16(0); i < e.ExtendedDataCount; i++ {
690695
item := e.ExtendedDataItem(i)
691-
if item.ExtType == EventHeaderExtTypeRelatedActivityID {
696+
if item != nil && item.ExtType == EventHeaderExtTypeRelatedActivityID {
692697
g := (*windows_.GUID)(unsafe.Pointer(item.DataPtr))
693698
return g.String()
694699
}
695700
}
696-
return "{00000000-0000-0000-0000-000000000000}"
701+
return ""
702+
}
703+
704+
func (e *EventRecord) SID() string {
705+
for i := uint16(0); i < e.ExtendedDataCount; i++ {
706+
item := e.ExtendedDataItem(i)
707+
if item != nil && item.ExtType == EventHeaderExtTypeSID {
708+
sid := (*windows.SID)(unsafe.Pointer(item.DataPtr))
709+
return sid.String()
710+
}
711+
}
712+
return ""
697713
}
698714

699715
/*

receiver/windowseventtracereceiver/internal/etw/consumer.go

+53-16
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ func (c *Consumer) rawEventCallback(eventRecord *advapi32.EventRecord) uintptr {
118118
return 1
119119
}
120120

121+
channelName, opcodeName, taskName, eventID := c.getEventInfoFromRecord(eventRecord)
122+
121123
// Create an XML-like representation
122124
var xmlBuilder strings.Builder
123125
xmlBuilder.WriteString("<Event>\n")
@@ -127,24 +129,38 @@ func (c *Consumer) rawEventCallback(eventRecord *advapi32.EventRecord) uintptr {
127129
xmlBuilder.WriteString(fmt.Sprintf(" <Provider Name=\"%s\" Guid=\"{%s}\"/>\n",
128130
providerName, providerGUID))
129131
xmlBuilder.WriteString(fmt.Sprintf(" <EventID>%d</EventID>\n",
130-
eventRecord.EventHeader.EventDescriptor.Id))
132+
eventID))
131133
xmlBuilder.WriteString(fmt.Sprintf(" <Version>%d</Version>\n",
132134
eventRecord.EventHeader.EventDescriptor.Version))
133135
xmlBuilder.WriteString(fmt.Sprintf(" <Level>%d</Level>\n",
134136
eventRecord.EventHeader.EventDescriptor.Level))
135-
xmlBuilder.WriteString(fmt.Sprintf(" <Task>%d</Task>\n",
136-
eventRecord.EventHeader.EventDescriptor.Task))
137-
xmlBuilder.WriteString(fmt.Sprintf(" <Opcode>%d</Opcode>\n",
138-
eventRecord.EventHeader.EventDescriptor.Opcode))
137+
xmlBuilder.WriteString(fmt.Sprintf(" <Task>%s</Task>\n",
138+
taskName))
139+
xmlBuilder.WriteString(fmt.Sprintf(" <Opcode>%s</Opcode>\n",
140+
opcodeName))
139141
xmlBuilder.WriteString(fmt.Sprintf(" <Keywords>0x%x</Keywords>\n",
140142
eventRecord.EventHeader.EventDescriptor.Keyword))
141143

142144
timeStr := eventRecord.EventHeader.UTC().Format(time.RFC3339Nano)
143145
xmlBuilder.WriteString(fmt.Sprintf(" <TimeCreated SystemTime=\"%s\"/>\n", timeStr))
144146

147+
if !eventRecord.EventHeader.ActivityId.Equals(&windows.GUID{}) {
148+
xmlBuilder.WriteString(fmt.Sprintf(" <Correlation ActivityID=\"%s\" RelatedActivityID=\"%s\"/>\n",
149+
eventRecord.EventHeader.ActivityId.String(), eventRecord.RelatedActivityID()))
150+
} else {
151+
xmlBuilder.WriteString(" <Correlation />\n")
152+
}
153+
145154
xmlBuilder.WriteString(fmt.Sprintf(" <Execution ProcessID=\"%d\" ThreadID=\"%d\"/>\n",
146155
eventRecord.EventHeader.ProcessId, eventRecord.EventHeader.ThreadId))
156+
157+
xmlBuilder.WriteString(fmt.Sprintf(" <Channel>%s</Channel>\n", channelName))
158+
147159
xmlBuilder.WriteString(fmt.Sprintf(" <Computer>%s</Computer>\n", hostname))
160+
161+
if sid := eventRecord.SID(); sid != "" {
162+
xmlBuilder.WriteString(fmt.Sprintf(" <Security UserID=\"%s\"/>\n", sid))
163+
}
148164
xmlBuilder.WriteString(" </System>\n")
149165

150166
// EventData section
@@ -188,38 +204,50 @@ func (c *Consumer) parsedEventCallback(eventRecord *advapi32.EventRecord) uintpt
188204
if provider, ok := c.providerMap[providerGUID]; ok {
189205
providerName = provider.Name
190206
}
207+
208+
// Get event information from TraceEventInfo
209+
channelName, opcodeName, taskName, eventID := c.getEventInfoFromRecord(eventRecord)
210+
191211
level := eventRecord.EventHeader.EventDescriptor.Level
192212
event := &Event{
193213
Flags: strconv.FormatUint(uint64(eventRecord.EventHeader.Flags), 10),
194214
Session: c.sessionName,
195215
Timestamp: parseTimestamp(uint64(eventRecord.EventHeader.TimeStamp)),
196216
System: EventSystem{
197217
ActivityID: eventRecord.EventHeader.ActivityId.String(),
198-
Channel: strconv.FormatInt(int64(eventRecord.EventHeader.EventDescriptor.Channel), 10),
199-
Keywords: strconv.FormatUint(eventRecord.EventHeader.EventDescriptor.Keyword, 10),
200-
EventID: fmt.Sprintf("%d", eventRecord.EventHeader.EventDescriptor.Id),
201-
Opcode: strconv.FormatUint(uint64(eventRecord.EventHeader.EventDescriptor.Opcode), 10),
202-
Task: strconv.FormatUint(uint64(eventRecord.EventHeader.EventDescriptor.Task), 10),
218+
Channel: channelName,
219+
Keywords: strconv.FormatUint(uint64(eventRecord.EventHeader.EventDescriptor.Keyword), 10),
220+
EventID: strconv.FormatUint(uint64(eventID), 10),
221+
Opcode: opcodeName,
222+
Task: taskName,
203223
Provider: EventProvider{
204224
GUID: providerGUID,
205225
Name: providerName,
206226
},
207-
Level: level,
208-
Computer: hostname,
209-
Correlation: EventCorrelation{
210-
ActivityID: eventRecord.EventHeader.ActivityId.String(),
211-
RelatedActivityID: eventRecord.RelatedActivityID(),
212-
},
227+
Level: level,
228+
Computer: hostname,
229+
Correlation: EventCorrelation{},
213230
Execution: EventExecution{
214231
ThreadID: eventRecord.EventHeader.ThreadId,
215232
ProcessID: eventRecord.EventHeader.ProcessId,
216233
},
217234
Version: eventRecord.EventHeader.EventDescriptor.Version,
218235
},
236+
Security: EventSecurity{
237+
SID: eventRecord.SID(),
238+
},
219239
EventData: data,
220240
ExtendedData: []string{},
221241
}
222242

243+
if activityID := eventRecord.EventHeader.ActivityId.String(); activityID != zeroGUID {
244+
event.System.Correlation.ActivityID = activityID
245+
}
246+
247+
if relatedActivityID := eventRecord.RelatedActivityID(); relatedActivityID != zeroGUID {
248+
event.System.Correlation.RelatedActivityID = relatedActivityID
249+
}
250+
223251
select {
224252
case c.Events <- event:
225253
return 0
@@ -228,6 +256,15 @@ func (c *Consumer) parsedEventCallback(eventRecord *advapi32.EventRecord) uintpt
228256
}
229257
}
230258

259+
func (c *Consumer) getEventInfoFromRecord(eventRecord *advapi32.EventRecord) (channelName string, opcodeName string, taskName string, eventID uint16) {
260+
ti, err := getEventInformation(eventRecord)
261+
if err != nil {
262+
c.logger.Error("Failed to get event information", zap.Error(err))
263+
return "", "", "", 0
264+
}
265+
return ti.ChannelName(), ti.OpcodeName(), ti.TaskName(), ti.EventID()
266+
}
267+
231268
func (c *Consumer) defaultBufferCallback(buffer *advapi32.EventTraceLogfile) uintptr {
232269
select {
233270
case <-c.doneChan:

receiver/windowseventtracereceiver/internal/etw/event.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,26 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
//go:build windows
16+
1517
// Package etw contains the functionality for interacting with the ETW API.
1618
package etw
1719

18-
import "time"
20+
import (
21+
"time"
22+
23+
"golang.org/x/sys/windows"
24+
)
1925

2026
// EventFlags contains flags for the event
2127
type EventFlags struct {
2228
// Use to flag event as being skippable for performance reason
2329
Skippable bool `json:"skippable"`
2430
}
2531

32+
// zeroGUID is a string representation of the zero GUID, which generally indicates that the value is not applicable
33+
var zeroGUID = windows.GUID{}.String()
34+
2635
// EventCorrelation contains correlation information for the event
2736
type EventCorrelation struct {
2837
ActivityID string `json:"activityID"`
@@ -91,4 +100,10 @@ type Event struct {
91100
UserData map[string]any `json:"userData,omitempty"`
92101
System EventSystem `json:"system"`
93102
ExtendedData []string `json:"extendedData,omitempty"`
103+
Security EventSecurity `json:"security,omitempty"`
104+
}
105+
106+
// EventSecurity contains security information for the event
107+
type EventSecurity struct {
108+
SID string `json:"sid"`
94109
}

receiver/windowseventtracereceiver/internal/etw/parser.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,29 @@ func (p *parser) getPropertyValue(r *advapi32.EventRecord, propInfo *tdh.TraceEv
8989
return nil, fmt.Errorf("failed to get array size: %w", err)
9090
}
9191

92+
if arraySize == 1 {
93+
if (propertyInfo.Flags & tdh.PropertyStruct) == tdh.PropertyStruct {
94+
return p.parseObject(propertyInfo)
95+
}
96+
return p.parseSimpleType(r, propertyInfo, 0)
97+
}
98+
9299
result := make([]any, arraySize)
93-
for i := range arraySize {
100+
for idx := range arraySize {
94101
var (
95102
value any
96103
err error
97104
)
98105
if (propertyInfo.Flags & tdh.PropertyStruct) == tdh.PropertyStruct {
99106
value, err = p.parseObject(propertyInfo)
100107
} else {
101-
value, err = p.parseSimpleType(r, propertyInfo, uint32(i))
108+
value, err = p.parseSimpleType(r, propertyInfo, uint32(idx))
102109
}
103110

104111
if err != nil {
105112
return nil, err
106113
}
107-
result[i] = value
114+
result[idx] = value
108115
}
109116

110117
return result, nil

receiver/windowseventtracereceiver/internal/etw/session_controller.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ func allocBuffer(logSessionName string) (propertyBuffer *advapi32.EventTraceProp
176176
// if we successfully enable the provider, we will return nil.
177177
func (s *SessionController) enableProvider(handle syscall.Handle, providerGUID *windows.GUID, provider *Provider, traceLevel advapi32.TraceLevel, matchAnyKeyword uint64, matchAllKeyword uint64) error {
178178
params := advapi32.EnableTraceParameters{
179-
Version: 2,
179+
Version: 2,
180+
EnableProperty: advapi32.EVENT_ENABLE_PROPERTY_SID,
180181
}
181182

182183
const maxAttempts = 5

receiver/windowseventtracereceiver/receiver.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ func (lr *logsReceiver) parseEvent(event *etw.Event) (plog.Logs, error) {
181181
resourceLog.Resource().Attributes().PutStr("provider", event.System.Provider.Name)
182182
resourceLog.Resource().Attributes().PutStr("provider_guid", event.System.Provider.GUID)
183183
resourceLog.Resource().Attributes().PutStr("computer", event.System.Computer)
184+
resourceLog.Resource().Attributes().PutStr("channel", event.System.Channel)
184185

185186
scopeLog := resourceLog.ScopeLogs().AppendEmpty()
186187
record := scopeLog.LogRecords().AppendEmpty()
@@ -224,10 +225,23 @@ func (lr *logsReceiver) parseEventData(event *etw.Event, record plog.LogRecord)
224225

225226
if event.System.EventID != "" {
226227
eventID := record.Body().Map().PutEmptyMap("event_id")
227-
// eventID.PutStr("guid", event.System.EventGUID)
228228
eventID.PutStr("id", event.System.EventID)
229229
}
230230

231+
correlation := record.Body().Map().PutEmptyMap("correlation")
232+
if event.System.Correlation.ActivityID != "" {
233+
correlation.PutStr("activity_id", event.System.Correlation.ActivityID)
234+
}
235+
236+
if event.System.Correlation.RelatedActivityID != "" {
237+
correlation.PutStr("related_activity_id", event.System.Correlation.RelatedActivityID)
238+
}
239+
240+
if event.Security.SID != "" {
241+
security := record.Body().Map().PutEmptyMap("security")
242+
security.PutStr("sid", event.Security.SID)
243+
}
244+
231245
if event.System.Execution.ProcessID != 0 {
232246
execution := record.Body().Map().PutEmptyMap("execution")
233247
execution.PutStr("process_id", strconv.FormatUint(uint64(event.System.Execution.ProcessID), 10))

0 commit comments

Comments
 (0)