Skip to content

Commit

Permalink
Fixes for wineventlog experimental api
Browse files Browse the repository at this point in the history
Some valid message strings have a "message ID" of 0. So set the EvtFormatMessageId flag when we don't have a valid event handle when calling EvtFormatMessage.

Change the delimiters used in message templates. There were a few cases were the template failed to parse because the raw message used the same delims in some way.

EventMetadata is an exported type, but its EventData field was using an unexported type called `eventData`. To make external usage easier make that type exported as well.

Add winevetlog.Publishers() to list all registered event publishers on the system.
  • Loading branch information
andrewkroh committed Jul 9, 2021
1 parent 4933aef commit b0123c9
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Protect against accessing undefined variables in Sysmon module. {issue}22219[22219] {pull}22236[22236]
- Protect against accessing an undefined variable in Security module. {pull}22937[22937]
- Fix related.ip field in renameCommonAuthFields {pull}24892[24892]
- Fix an issue with message template caching in the `wineventlog-experimental` API implementation. {pull}26826[26826]

*Functionbeat*

Expand Down
2 changes: 1 addition & 1 deletion winlogbeat/sys/wineventlog/format_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func getMessageString(metadata *PublisherMetadata, eventHandle EvtHandle, messag
var flags EvtFormatMessageFlag
if eventHandle > 0 {
flags = EvtFormatMessageEvent
} else if messageID > 0 {
} else {
flags = EvtFormatMessageId
}

Expand Down
22 changes: 15 additions & 7 deletions winlogbeat/sys/wineventlog/metadata_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ type EventMetadata struct {
Version uint8 // Event format version.
MsgStatic string // Used when the message has no parameters.
MsgTemplate *template.Template `json:"-"` // Template that expects an array of values as its data.
EventData []eventData // Names of parameters from XML template.
EventData []EventData // Names of parameters from XML template.
}

// newEventMetadataFromEventHandle collects metadata about an event type using
Expand All @@ -307,11 +307,11 @@ func newEventMetadataFromEventHandle(publisher *PublisherMetadata, eventHandle E
}
if len(event.EventData.Pairs) > 0 {
for _, pair := range event.EventData.Pairs {
em.EventData = append(em.EventData, eventData{Name: pair.Key})
em.EventData = append(em.EventData, EventData{Name: pair.Key})
}
} else {
for _, pair := range event.UserData.Pairs {
em.EventData = append(em.EventData, eventData{Name: pair.Key})
em.EventData = append(em.EventData, EventData{Name: pair.Key})
}
}

Expand Down Expand Up @@ -396,10 +396,15 @@ func (em *EventMetadata) initEventMessage(itr *EventMetadataIterator, publisher
if err != nil {
return err
}
// If the event definition does not specify a message, the value is –1.
if int32(messageID) == -1 {
return nil
}

msg, err := getMessageString(publisher, NilHandle, messageID, templateInserts.Slice())
if err != nil {
return err
return errors.Wrapf(err, "failed to get message string using message "+
"ID %v for for event ID %v", messageID, em.EventID)
}

return em.setMessage(msg)
Expand All @@ -409,9 +414,12 @@ func (em *EventMetadata) setMessage(msg string) error {
msg = sys.RemoveWindowsLineEndings(msg)
tmplID := strconv.Itoa(int(em.EventID))

tmpl, err := template.New(tmplID).Funcs(eventMessageTemplateFuncs).Parse(msg)
tmpl, err := template.New(tmplID).
Delims(leftTemplateDelim, rightTemplateDelim).
Funcs(eventMessageTemplateFuncs).Parse(msg)
if err != nil {
return err
return errors.Wrapf(err, "failed to parse message template for "+
"event ID %v (template='%v')", em.EventID, msg)
}

// One node means there were no parameters so this will optimize that case
Expand All @@ -432,7 +440,7 @@ func (em *EventMetadata) equal(other *EventMetadata) bool {
return false
}

eventDataNamesEqual := func(a, b []eventData) bool {
eventDataNamesEqual := func(a, b []EventData) bool {
if len(a) != len(b) {
return false
}
Expand Down
8 changes: 6 additions & 2 deletions winlogbeat/sys/wineventlog/stringinserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const (
// maxInsertStrings is the maximum number of parameters supported in a
// Windows event message.
maxInsertStrings = 99

leftTemplateDelim = "[{{"
rightTemplateDelim = "}}]"
)

// templateInserts contains EvtVariant values that can be used to substitute
Expand All @@ -45,7 +48,7 @@ type stringInserts struct {
evtVariants [maxInsertStrings]EvtVariant
}

// Pointer returns a pointer the EvtVariant array.
// Slice returns a slice of the full EvtVariant array.
func (si *stringInserts) Slice() []EvtVariant {
return si.evtVariants[:]
}
Expand All @@ -66,7 +69,8 @@ func newTemplateStringInserts() *stringInserts {

for i := 0; i < len(si.evtVariants); i++ {
// Use i+1 to keep our inserts numbered the same as Window's inserts.
strSlice, err := windows.UTF16FromString(`{{eventParam $ ` + strconv.Itoa(i+1) + `}}`)
templateParam := leftTemplateDelim + `eventParam $ ` + strconv.Itoa(i+1) + rightTemplateDelim
strSlice, err := windows.UTF16FromString(templateParam)
if err != nil {
// This will never happen.
panic(err)
Expand Down
2 changes: 2 additions & 0 deletions winlogbeat/sys/wineventlog/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,3 +661,5 @@ func EvtClearLog(session EvtHandle, channelPath string, targetFilePath string) e
//sys _EvtNextEventMetadata(enumerator EvtHandle, flags uint32) (handle EvtHandle, err error) = wevtapi.EvtNextEventMetadata
//sys _EvtGetObjectArrayProperty(objectArray EvtObjectArrayPropertyHandle, propertyID EvtPublisherMetadataPropertyID, arrayIndex uint32, flags uint32, bufferSize uint32, evtVariant *EvtVariant, bufferUsed *uint32) (err error) = wevtapi.EvtGetObjectArrayProperty
//sys _EvtGetObjectArraySize(objectArray EvtObjectArrayPropertyHandle, arraySize *uint32) (err error) = wevtapi.EvtGetObjectArraySize
//sys _EvtOpenPublisherEnum(session EvtHandle, flags uint32) (handle EvtHandle, err error) = wevtapi.EvtOpenPublisherEnum
//sys _EvtNextPublisherId(enumerator EvtHandle, bufferSize uint32, buffer *byte, bufferUsed *uint32) (err error) = wevtapi.EvtNextPublisherId
9 changes: 5 additions & 4 deletions winlogbeat/sys/wineventlog/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import (
)

type eventTemplate struct {
Data []eventData `xml:"data"`
Data []EventData `xml:"data"`
}

type eventData struct {
Name string `xml:"name,attr"`
Type string `xml:"outType,attr"`
type EventData struct {
Name string `xml:"name,attr"`
InType string `xml:"inType,attr"`
OutType string `xml:"outType,attr"`
}

func (t *eventTemplate) Unmarshal(xmlData []byte) error {
Expand Down
41 changes: 41 additions & 0 deletions winlogbeat/sys/wineventlog/wineventlog_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"io"
"reflect"
"runtime"
"sort"
"syscall"

"github.com/elastic/beats/v7/libbeat/common"
Expand Down Expand Up @@ -419,6 +420,46 @@ func FormatEventString(
return common.UTF16ToUTF8Bytes(buffer[:bufferUsed], out)
}

// Publishers returns a sort list of event publishers on the local computer.
func Publishers() ([]string, error) {
publisherEnumerator, err := _EvtOpenPublisherEnum(NilHandle, 0)
if err != nil {
return nil, fmt.Errorf("failed in EvtOpenPublisherEnum: %w", err)
}
defer Close(publisherEnumerator)

var (
publishers []string
bufferUsed uint32
buffer = make([]byte, 1024)
)

loop:
for {
if err = _EvtNextPublisherId(publisherEnumerator, uint32(len(buffer)), &buffer[0], &bufferUsed); err != nil {
switch err {
case ERROR_NO_MORE_ITEMS:
break loop
case ERROR_INSUFFICIENT_BUFFER:
buffer = make([]byte, 2*bufferUsed)
continue loop
default:
return nil, fmt.Errorf("failed in EvtNextPublisherId: %w", err)
}
}

provider, err := sys.UTF16BytesToString(buffer)
if err != nil {
return nil, err
}

publishers = append(publishers, provider)
}

sort.Strings(publishers)
return publishers, nil
}

// offset reads a pointer value from the reader then calculates an offset from
// the start of the buffer to the pointer location. If the pointer value is
// NULL or is outside of the bounds of the buffer then an error is returned.
Expand Down
12 changes: 12 additions & 0 deletions winlogbeat/sys/wineventlog/wineventlog_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,15 @@ func TestChannels(t *testing.T) {
}
}
}

func TestPublishers(t *testing.T) {
publishers, err := Publishers()
if err != nil {
t.Fatal(err)
}

assert.NotEmpty(t, publishers)
for _, p := range publishers {
t.Log(p)
}
}
21 changes: 19 additions & 2 deletions winlogbeat/sys/wineventlog/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b0123c9

Please sign in to comment.