Skip to content

Commit

Permalink
[winlogbeat] Add handling for missing event data types in the experim…
Browse files Browse the repository at this point in the history
…ental API (#41418) (#41512)

* [winlogbeat] Add handling for missing event data types in the experimental API (#40684)

* Check type assertion result before using the result

* Update CHANGELOG.next.asciidoc

* Use sync.OnceValue to lazy init ansi decoder

* Move cached encoder out of function

* Add comment to clarify default cp

* Update strings_windows.go

(cherry picked from commit 36c3d5d)

Co-authored-by: Marc Guasch <marc-gr@users.noreply.github.com>
  • Loading branch information
mergify[bot] and marc-gr authored Nov 5, 2024
1 parent e861780 commit b768b39
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]

*Winlogbeat*

- Add handling for missing `EvtVarType`s in experimental api. {issue}19337[19337] {pull}41418[41418]


*Functionbeat*

Expand Down
24 changes: 24 additions & 0 deletions winlogbeat/sys/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ func RemoveWindowsLineEndings(s string) string {
s = strings.Replace(s, "\r\n", "\n", -1)
return strings.TrimRight(s, "\n")
}

// BinaryToString converts a binary field which is encoded in hexadecimal
// to its string representation. This is equivalent to hex.EncodeToString
// but its output is in uppercase to be equivalent to the windows
// XML formatting of this fields.
func BinaryToString(bin []byte) string {
if len(bin) == 0 {
return ""
}

const hexTable = "0123456789ABCDEF"

size := len(bin) * 2
buffer := make([]byte, size)

j := 0
for _, v := range bin {
buffer[j] = hexTable[v>>4]
buffer[j+1] = hexTable[v&0x0f]
j += 2
}

return string(buffer)
}
6 changes: 6 additions & 0 deletions winlogbeat/sys/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ func TestUTF16BytesToString(t *testing.T) {
assert.Equal(t, input, output)
}

func TestMakeDisplayableBinaryString(t *testing.T) {
input := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}
output := BinaryToString(input)
assert.Equal(t, "0123456789ABCDEF", output)
}

func BenchmarkUTF16BytesToString(b *testing.B) {
utf16Bytes := common.StringToUTF16Bytes("A logon was attempted using explicit credentials.")

Expand Down
53 changes: 53 additions & 0 deletions winlogbeat/sys/strings_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package sys

import (
"sync"

"golang.org/x/sys/windows"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
)

var getCachedANSIDecoder = sync.OnceValue(initANSIDecoder)

func initANSIDecoder() *encoding.Decoder {
ansiCP := windows.GetACP()
for _, enc := range charmap.All {
cm, ok := enc.(*charmap.Charmap)
if !ok {
continue
}
cmID, _ := cm.ID()
if uint32(cmID) != ansiCP {
continue
}
return cm.NewDecoder()
}
// This should never be reached.
// If the ANSI Code Page is not found, we will default to
// Windows1252 Code Page, which is default for ANSI in
// many regions and corresponds to Western European languages.
return charmap.Windows1252.NewDecoder()
}

func ANSIBytesToString(enc []byte) (string, error) {
out, err := getCachedANSIDecoder().Bytes(enc)
return string(out), err
}
20 changes: 19 additions & 1 deletion winlogbeat/sys/wineventlog/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,11 +442,16 @@ func (v EvtVariant) Data(buf []byte) (interface{}, error) {
switch typ {
case EvtVarTypeNull:
return nil, nil
case EvtVarTypeString:
case EvtVarTypeString, EvtVarTypeEvtXml:
addr := unsafe.Pointer(&buf[0])
offset := v.ValueAsUintPtr() - uintptr(addr)
s, err := sys.UTF16BytesToString(buf[offset:])
return s, err
case EvtVarTypeAnsiString:
addr := unsafe.Pointer(&buf[0])
offset := v.ValueAsUintPtr() - uintptr(addr)
s, err := sys.ANSIBytesToString(buf[offset:])
return s, err
case EvtVarTypeSByte:
return int8(v.ValueAsUint8()), nil
case EvtVarTypeByte:
Expand Down Expand Up @@ -476,15 +481,28 @@ func (v EvtVariant) Data(buf []byte) (interface{}, error) {
return false, nil
}
return true, nil
case EvtVarTypeBinary:
addr := unsafe.Pointer(&buf[0])
offset := v.ValueAsUintPtr() - uintptr(addr)
return sys.BinaryToString(buf[offset:]), nil
case EvtVarTypeGuid:
addr := unsafe.Pointer(&buf[0])
offset := v.ValueAsUintPtr() - uintptr(addr)
guid := (*windows.GUID)(unsafe.Pointer(&buf[offset]))
copy := *guid
return copy, nil
case EvtVarTypeSizeT:
return v.ValueAsUintPtr(), nil
case EvtVarTypeFileTime:
ft := (*windows.Filetime)(unsafe.Pointer(&v.Value))
return time.Unix(0, ft.Nanoseconds()).UTC(), nil
case EvtVarTypeSysTime:
st := (*windows.Systemtime)(unsafe.Pointer(&v.Value))
var ft windows.Filetime
if err := sys.SystemTimeToFileTime(st, &ft); err != nil {
return nil, err
}
return time.Unix(0, ft.Nanoseconds()).UTC(), nil
case EvtVarTypeSid:
addr := unsafe.Pointer(&buf[0])
offset := v.ValueAsUintPtr() - uintptr(addr)
Expand Down
40 changes: 40 additions & 0 deletions winlogbeat/sys/zsyscall_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package sys

import (
"fmt"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

var (
modkernel = windows.NewLazySystemDLL("Kernel32.dll")

procSystemTimeToFileTime = modkernel.NewProc("SystemTimeToFileTime")
)

func SystemTimeToFileTime(systemTime *windows.Systemtime, fileTime *windows.Filetime) error {
r1, _, err := syscall.SyscallN(procSystemTimeToFileTime.Addr(), uintptr(unsafe.Pointer(systemTime)), uintptr(unsafe.Pointer(fileTime)))
if r1 == 0 {
return fmt.Errorf("error converting system time to file time: %w", err)
}
return nil
}
42 changes: 42 additions & 0 deletions winlogbeat/sys/zsyscall_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package sys

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"golang.org/x/sys/windows"
)

func TestSystemTimeToFileTime(t *testing.T) {
ts := time.Date(
2024, time.Month(9), 3,
0, 0, 0, 0, time.UTC).UnixNano()
st := windows.Systemtime{
Year: 2024,
Month: 9,
Day: 3,
}
var ft windows.Filetime
if err := SystemTimeToFileTime(&st, &ft); err != nil {
t.Fatal(err)
}
assert.Equal(t, ts, ft.Nanoseconds())
}

0 comments on commit b768b39

Please sign in to comment.