diff --git a/cmd/fibratus/app/list/list.go b/cmd/fibratus/app/list/list.go index 92ab1eff6..055795d7a 100644 --- a/cmd/fibratus/app/list/list.go +++ b/cmd/fibratus/app/list/list.go @@ -21,15 +21,16 @@ package list import ( "bufio" "fmt" + "os" + "path/filepath" + "strings" + "github.com/jedib0t/go-pretty/v6/table" "github.com/rabbitstack/fibratus/internal/bootstrap" "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/filter/fields" "github.com/spf13/cobra" - "os" - "path/filepath" - "strings" ) var Command = &cobra.Command{ @@ -130,7 +131,7 @@ func listEvents(cmd *cobra.Command, args []string) { t.AppendHeader(table.Row{"Name", "Category", "Description"}) t.SetStyle(table.StyleLight) - for _, ev := range event.GetTypesMeta() { + for _, ev := range event.GetTypesInfo() { t.AppendRow(table.Row{ev.Name, ev.Category, ev.Description}) } diff --git a/go.mod b/go.mod index 25f8b279b..b15fa21d4 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 github.com/Microsoft/go-winio v0.4.14 github.com/antchfx/htmlquery v1.2.5 - github.com/bits-and-blooms/bitset v1.13.0 github.com/briandowns/spinner v1.12.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/dustin/go-humanize v1.0.0 diff --git a/go.sum b/go.sum index b3a239c3c..b1ad4f534 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/aws/aws-sdk-go v1.34.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/briandowns/spinner v1.12.0 h1:72O0PzqGJb6G3KgrcIOtL/JAGGZ5ptOMCn9cUHmqsmw= github.com/briandowns/spinner v1.12.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= diff --git a/internal/etw/consumer.go b/internal/etw/consumer.go index b63634970..0044309c6 100644 --- a/internal/etw/consumer.go +++ b/internal/etw/consumer.go @@ -78,8 +78,8 @@ func (c *Consumer) ProcessEvent(r *etw.EventRecord) error { return nil } - if !c.config.EventSource.EventExists(r.ID()) { - eventsUnknown.Add(1) + etype := event.NewTypeFromEventRecord(r) + if etype == event.Unknown { return nil } if event.IsCurrentProcDropped(r.Header.ProcessID) && r.Header.ProviderID != etw.WindowsKernelProcessGUID { @@ -92,13 +92,13 @@ func (c *Consumer) ProcessEvent(r *etw.EventRecord) error { } defer c.approvers.Cleanup(rec) - if c.config.EventSource.ExcludeEvent(rec.ID()) { + if c.config.EventSource.ExcludeEvent(etype) { eventsExcluded.Add(1) return nil } eventsProcessed.Add(1) - evt := event.New(c.sequencer.Get(), rec) + evt := event.New(c.sequencer.Get(), rec, etype) // Dispatch each event to the processor chain. // Processors may further augment the event with diff --git a/internal/etw/processors/fs_windows.go b/internal/etw/processors/fs_windows.go index 0e9a0df19..2027db94e 100644 --- a/internal/etw/processors/fs_windows.go +++ b/internal/etw/processors/fs_windows.go @@ -71,7 +71,7 @@ func newFsProcessor( } func (f *fsProcessor) ProcessEvent(e *event.Event) (*event.Event, bool, error) { - if e.Category == event.File { + if e.Category() == event.File { evt, err := f.processEvent(e) return evt, false, err } @@ -100,7 +100,7 @@ func (f *fsProcessor) processEvent(e *event.Event) (*event.Event, error) { totalRundownFiles.Add(1) f.files[fileObject] = &FileInfo{Name: filepath, Type: fs.GetFileType(filepath, 0)} } - case event.MapFileRundown: + case event.MapViewOfSection: fileKey := e.Params.MustGetUint64(params.FileKey) fileinfo := f.files[fileKey] @@ -144,7 +144,7 @@ func (f *fsProcessor) processEvent(e *event.Event) (*event.Event, error) { // delete file metadata by file object address fileObject := e.Params.MustGetUint64(params.FileObject) delete(f.files, fileObject) - case event.UnmapViewFile: + case event.UnmapViewOfSection: ok, proc := f.psnap.Find(e.PID) addr := e.Params.TryGetAddress(params.FileViewBase) if ok { @@ -157,11 +157,13 @@ func (f *fsProcessor) processEvent(e *event.Event) (*event.Event, error) { totalMapRundownFiles.Add(-1) return e, f.psnap.RemoveMmap(e.PID, addr) + case event.FileOpEnd: + return e, nil default: var fileObject uint64 fileKey := e.Params.MustGetUint64(params.FileKey) - if !e.IsMapViewFile() { + if !e.IsMapViewOfSection() { fileObject = e.Params.MustGetUint64(params.FileObject) } @@ -202,7 +204,7 @@ func (f *fsProcessor) processEvent(e *event.Event) (*event.Event, error) { e.AppendParam(params.FilePath, params.Path, fileinfo.Name) } - if e.IsMapViewFile() { + if e.IsMapViewOfSection() { return e, f.psnap.AddMmap(e) } } diff --git a/internal/etw/processors/fs_windows_test.go b/internal/etw/processors/fs_windows_test.go index 70b841ccd..7e593d2e4 100644 --- a/internal/etw/processors/fs_windows_test.go +++ b/internal/etw/processors/fs_windows_test.go @@ -46,8 +46,7 @@ func TestFsProcessor(t *testing.T) { { "process file rundown", &event.Event{ - Type: event.FileRundown, - Category: event.File, + Type: event.FileRundown, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(124567380264)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -69,9 +68,8 @@ func TestFsProcessor(t *testing.T) { { "process mapped file rundown", &event.Event{ - PID: 10233, - Type: event.MapFileRundown, - Category: event.File, + PID: 10233, + Type: event.MapViewSectionRundown, Params: event.Params{ params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(124567380264)}, params.FileViewSize: {Name: params.FileViewSize, Type: params.Uint64, Value: uint64(3098)}, @@ -99,8 +97,7 @@ func TestFsProcessor(t *testing.T) { { "release file and remove file info", &event.Event{ - Type: event.ReleaseFile, - Category: event.File, + Type: event.ReleaseFile, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(18446738026482168384)}, params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(14446538026482168384)}, @@ -122,9 +119,8 @@ func TestFsProcessor(t *testing.T) { { "unmap view file", &event.Event{ - PID: 10233, - Type: event.UnmapViewFile, - Category: event.File, + PID: 10233, + Type: event.UnmapViewOfSection, Params: event.Params{ params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(124567380264)}, params.FileViewSize: {Name: params.FileViewSize, Type: params.Uint64, Value: uint64(3098)}, @@ -147,8 +143,7 @@ func TestFsProcessor(t *testing.T) { { "process write file", &event.Event{ - Type: event.WriteFile, - Category: event.File, + Type: event.WriteFile, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(18446738026482168384)}, params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(14446538026482168384)}, @@ -173,8 +168,7 @@ func TestFsProcessor(t *testing.T) { { "process write file consult handle snapshotter", &event.Event{ - Type: event.WriteFile, - Category: event.File, + Type: event.WriteFile, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(18446738026482168384)}, params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(14446538026482168384)}, @@ -198,8 +192,7 @@ func TestFsProcessor(t *testing.T) { { "process enum directory", &event.Event{ - Type: event.EnumDirectory, - Category: event.File, + Type: event.EnumDirectory, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(18446738026482168384)}, params.FileKey: {Name: params.FileKey, Type: params.Uint64, Value: uint64(14446538026482168384)}, diff --git a/internal/etw/processors/mem_windows.go b/internal/etw/processors/memory_windows.go similarity index 98% rename from internal/etw/processors/mem_windows.go rename to internal/etw/processors/memory_windows.go index 2e51c4c49..4149a9947 100644 --- a/internal/etw/processors/mem_windows.go +++ b/internal/etw/processors/memory_windows.go @@ -48,7 +48,7 @@ func (m memProcessor) Close() { } func (m memProcessor) ProcessEvent(e *event.Event) (*event.Event, bool, error) { - if e.Category == event.Mem { + if e.Category() == event.Memory { pid := e.Params.MustGetPid() if e.IsVirtualAlloc() { // retrieve info about the range of pages and enrich the event diff --git a/internal/etw/processors/mem_windows_test.go b/internal/etw/processors/memory_windows_test.go similarity index 97% rename from internal/etw/processors/mem_windows_test.go rename to internal/etw/processors/memory_windows_test.go index dcf666b3e..ef671833e 100644 --- a/internal/etw/processors/mem_windows_test.go +++ b/internal/etw/processors/memory_windows_test.go @@ -19,6 +19,9 @@ package processors import ( + "os" + "testing" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/ps" @@ -28,8 +31,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "os" - "testing" ) func TestMemProcessor(t *testing.T) { @@ -47,8 +48,7 @@ func TestMemProcessor(t *testing.T) { { "virtual alloc", &event.Event{ - Type: event.VirtualAlloc, - Category: event.Mem, + Type: event.VirtualAlloc, Params: event.Params{ params.MemRegionSize: {Name: params.MemRegionSize, Type: params.Uint64, Value: uint64(1024)}, params.MemBaseAddress: {Name: params.MemBaseAddress, Type: params.Address, Value: uint64(base)}, @@ -73,8 +73,7 @@ func TestMemProcessor(t *testing.T) { { "virtual free", &event.Event{ - Type: event.VirtualFree, - Category: event.Mem, + Type: event.VirtualFree, Params: event.Params{ params.MemRegionSize: {Name: params.MemRegionSize, Type: params.Uint64, Value: uint64(1024)}, params.MemBaseAddress: {Name: params.MemBaseAddress, Type: params.Address, Value: uint64(base)}, diff --git a/internal/etw/processors/net_windows.go b/internal/etw/processors/net_windows.go index a09001f2b..4c32f955e 100644 --- a/internal/etw/processors/net_windows.go +++ b/internal/etw/processors/net_windows.go @@ -21,7 +21,6 @@ package processors import ( "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" - "github.com/rabbitstack/fibratus/pkg/network" "github.com/rabbitstack/fibratus/pkg/util/ports" ) @@ -38,14 +37,7 @@ func (netProcessor) Name() ProcessorType { return Net } func (n netProcessor) Close() {} func (n *netProcessor) ProcessEvent(e *event.Event) (*event.Event, bool, error) { - if e.Category == event.Net { - if e.IsNetworkTCP() && !e.IsDNS() { - e.AppendEnum(params.NetL4Proto, uint32(network.TCP), network.ProtoNames) - } - if e.IsNetworkUDP() && !e.IsDNS() { - e.AppendEnum(params.NetL4Proto, uint32(network.UDP), network.ProtoNames) - } - + if e.Category() == event.Network { if e.IsDNS() { return e, false, nil } diff --git a/internal/etw/processors/net_windows_test.go b/internal/etw/processors/net_windows_test.go index 6f6e36bc0..94247b8df 100644 --- a/internal/etw/processors/net_windows_test.go +++ b/internal/etw/processors/net_windows_test.go @@ -19,12 +19,14 @@ package processors import ( + "net" + "testing" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/network" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "net" - "testing" ) func TestNetworkProcessor(t *testing.T) { @@ -36,13 +38,13 @@ func TestNetworkProcessor(t *testing.T) { { "send tcpv4", &event.Event{ - Type: event.SendTCPv4, - Category: event.Net, + Type: event.Send, Params: event.Params{ - params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(443)}, - params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, - params.NetSIP: {Name: params.NetSIP, Type: params.IPv4, Value: net.ParseIP("127.0.0.1")}, - params.NetDIP: {Name: params.NetDIP, Type: params.IPv4, Value: net.ParseIP("8.8.8.8")}, + params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(443)}, + params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, + params.NetSIP: {Name: params.NetSIP, Type: params.IPv4, Value: net.ParseIP("127.0.0.1")}, + params.NetDIP: {Name: params.NetDIP, Type: params.IPv4, Value: net.ParseIP("8.8.8.8")}, + params.NetL4Proto: {Name: params.NetL4Proto, Type: params.Enum, Value: uint32(network.TCP), Enum: network.ProtoNames}, }, }, func(e *event.Event, t *testing.T) { @@ -58,13 +60,13 @@ func TestNetworkProcessor(t *testing.T) { { "recv udp6", &event.Event{ - Type: event.RecvUDPv6, - Category: event.Net, + Type: event.Recv, Params: event.Params{ - params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(53)}, - params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, - params.NetSIP: {Name: params.NetSIP, Type: params.IPv4, Value: net.ParseIP("127.0.0.1")}, - params.NetDIP: {Name: params.NetDIP, Type: params.IPv4, Value: net.ParseIP("8.8.8.8")}, + params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(53)}, + params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, + params.NetSIP: {Name: params.NetSIP, Type: params.IPv4, Value: net.ParseIP("127.0.0.1")}, + params.NetDIP: {Name: params.NetDIP, Type: params.IPv4, Value: net.ParseIP("8.8.8.8")}, + params.NetL4Proto: {Name: params.NetL4Proto, Type: params.Enum, Value: uint32(network.UDP), Enum: network.ProtoNames}, }, }, func(e *event.Event, t *testing.T) { diff --git a/internal/etw/processors/registry_windows.go b/internal/etw/processors/registry_windows.go index d3979493f..87b25a579 100644 --- a/internal/etw/processors/registry_windows.go +++ b/internal/etw/processors/registry_windows.go @@ -102,7 +102,7 @@ func newRegistryProcessor(hsnap handle.Snapshotter) Processor { } func (r *registryProcessor) ProcessEvent(e *event.Event) (*event.Event, bool, error) { - if e.Category == event.Registry { + if e.Category() == event.Registry { evt, err := r.processEvent(e) return evt, false, err } diff --git a/internal/etw/processors/registry_windows_test.go b/internal/etw/processors/registry_windows_test.go index 25fd6aa7e..9b75d879c 100644 --- a/internal/etw/processors/registry_windows_test.go +++ b/internal/etw/processors/registry_windows_test.go @@ -47,8 +47,7 @@ func TestRegistryProcessor(t *testing.T) { { "process KCB rundown", &event.Event{ - Type: event.RegKCBRundown, - Category: event.Registry, + Type: event.RegKCBRundown, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.UnicodeString, Value: `\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\bthserv\Parameters`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(18446666033549154696)}, @@ -68,8 +67,7 @@ func TestRegistryProcessor(t *testing.T) { { "process delete KCB", &event.Event{ - Type: event.RegDeleteKCB, - Category: event.Registry, + Type: event.RegDeleteKCB, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.UnicodeString, Value: `\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\bthserv\Parameters`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(18446666033549154696)}, @@ -90,8 +88,7 @@ func TestRegistryProcessor(t *testing.T) { { "full key name", &event.Event{ - Type: event.RegOpenKey, - Category: event.Registry, + Type: event.RegOpenKey, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.Key, Value: `\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\bthserv\Parameters`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(0)}, @@ -109,8 +106,7 @@ func TestRegistryProcessor(t *testing.T) { { "incomplete key name", &event.Event{ - Type: event.RegOpenKey, - Category: event.Registry, + Type: event.RegOpenKey, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.Key, Value: `Pid`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(18446666033549154696)}, @@ -130,9 +126,8 @@ func TestRegistryProcessor(t *testing.T) { { "incomplete key name consult handle snapshotter", &event.Event{ - Type: event.RegOpenKey, - Category: event.Registry, - PID: 23234, + Type: event.RegOpenKey, + PID: 23234, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.Key, Value: `Pid`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(18446666033549154696)}, @@ -153,9 +148,8 @@ func TestRegistryProcessor(t *testing.T) { { "process registry set value", &event.Event{ - Type: event.RegSetValue, - Category: event.Registry, - PID: 23234, + Type: event.RegSetValue, + PID: 23234, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.Key, Value: `\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Control\Windows\Directory`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(0)}, @@ -175,9 +169,8 @@ func TestRegistryProcessor(t *testing.T) { { "process registry set value from internal event", &event.Event{ - Type: event.RegSetValue, - Category: event.Registry, - PID: 23234, + Type: event.RegSetValue, + PID: 23234, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.Key, Value: `\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Control\Windows\Directory`}, params.RegKCB: {Name: params.RegKCB, Type: params.Uint64, Value: uint64(0)}, diff --git a/internal/etw/source.go b/internal/etw/source.go index 65376cac4..db0924c0a 100644 --- a/internal/etw/source.go +++ b/internal/etw/source.go @@ -60,8 +60,6 @@ var ( eventsFailed = expvar.NewMap("eventsource.events.failed") // eventsProcessed counts the number of total processed events eventsProcessed = expvar.NewInt("eventsource.events.processed") - // eventsUnknown counts the number of published events which types are not present in the internal catalog - eventsUnknown = expvar.NewInt("eventsource.events.unknown") // eventsExcluded counts the number of excluded events eventsExcluded = expvar.NewInt("eventsource.events.excluded") // buffersRead amount of buffers fetched from the ETW session @@ -138,7 +136,11 @@ func (e *EventSource) Open(config *config.Config) error { config.EventSource.EnableMemEvents = config.EventSource.EnableMemEvents && (e.r.HasMemEvents || (config.Yara.Enabled && !config.Yara.SkipAllocs)) config.EventSource.EnableDNSEvents = config.EventSource.EnableDNSEvents && e.r.HasDNSEvents config.EventSource.EnableAuditAPIEvents = config.EventSource.EnableAuditAPIEvents && e.r.HasAuditAPIEvents - for _, typ := range event.All() { + + for _, typ := range event.AllTypes() { + if typ.OnlyState() || typ.StateSnapshot() { + continue + } if typ == event.CreateProcess || typ == event.TerminateProcess || typ == event.LoadModule || typ == event.UnloadModule { // always allow fundamental events @@ -146,7 +148,7 @@ func (e *EventSource) Open(config *config.Config) error { } // allow events required for memory/file scanning - if typ == event.MapViewFile && config.Yara.Enabled && !config.Yara.SkipMmaps { + if typ == event.MapViewOfSection && config.Yara.Enabled && !config.Yara.SkipMmaps { continue } if typ == event.VirtualAlloc && config.Yara.Enabled && !config.Yara.SkipAllocs { @@ -179,13 +181,10 @@ func (e *EventSource) Open(config *config.Config) error { // modified value. This data is used to attach various parameters // to the RegSetValue event published by the NT Kernel Logger if config.EventSource.EnableRegistryEvents { - // undocumented ETW feature to enable captured data in RegSetValue events - val := 0x2 - eventFilterDescriptor := etw.EventFilterDescriptor{ - Ptr: uintptr(unsafe.Pointer(&val)), + trace.AddProvider(etw.WindowsKernelRegistryGUID, false, WithKeywords(etw.SetValueKeyword), WithEventFilterDescriptors(etw.EventFilterDescriptor{ + Ptr: uintptr(unsafe.Pointer(&etw.CaptureRegistryValue)), Size: 4, - } - trace.AddProvider(etw.WindowsKernelRegistryGUID, false, WithKeywords(etw.SetValueKeyword), WithEventFilterDescriptors(eventFilterDescriptor)) + })) } if config.EventSource.EnableDNSEvents { diff --git a/internal/etw/source_test.go b/internal/etw/source_test.go index 8582d0479..319b6230e 100644 --- a/internal/etw/source_test.go +++ b/internal/etw/source_test.go @@ -180,9 +180,9 @@ func TestEventSourceEnableFlagsDynamically(t *testing.T) { event.RegSetValue, event.CreateFile, event.RenameFile, - event.MapViewFile, + event.MapViewOfSection, event.OpenProcess, - event.ConnectTCPv4, + event.Connect, }, } cfg := &config.Config{ @@ -222,7 +222,7 @@ func TestEventSourceEnableFlagsDynamically(t *testing.T) { require.False(t, cfg.EventSource.TestDropMask(event.UnloadModule)) require.True(t, cfg.EventSource.TestDropMask(event.WriteFile)) - require.True(t, cfg.EventSource.TestDropMask(event.UnmapViewFile)) + require.True(t, cfg.EventSource.TestDropMask(event.UnmapViewOfSection)) require.False(t, cfg.EventSource.TestDropMask(event.OpenProcess)) } @@ -259,7 +259,7 @@ func TestEventSourceEnableFlagsDynamicallyWithYaraEnabled(t *testing.T) { event.RegSetValue, event.RenameFile, event.OpenProcess, - event.ConnectTCPv4, + event.Connect, }, } cfg := &config.Config{ @@ -300,7 +300,7 @@ func TestEventSourceEnableFlagsDynamicallyWithYaraEnabled(t *testing.T) { require.True(t, flags&etw.VirtualAlloc != 0) require.False(t, cfg.EventSource.TestDropMask(event.CreateFile)) - require.True(t, cfg.EventSource.TestDropMask(event.MapViewFile)) + require.True(t, cfg.EventSource.TestDropMask(event.MapViewOfSection)) require.False(t, cfg.EventSource.TestDropMask(event.VirtualAlloc)) } @@ -481,7 +481,7 @@ func TestEventSourceAllEvents(t *testing.T) { return nil }, func(e *event.Event) bool { - return e.CurrentPid() && (e.Type == event.ConnectTCPv4 || e.Type == event.ConnectTCPv6) + return e.CurrentPid() && e.Type == event.Connect }, false, }, @@ -534,7 +534,7 @@ func TestEventSourceAllEvents(t *testing.T) { return nil }, func(e *event.Event) bool { - return e.CurrentPid() && e.Type == event.MapViewFile && + return e.CurrentPid() && e.Type == event.MapViewOfSection && e.GetParamAsString(params.MemProtect) == "EXECUTE_READWRITE|READONLY" && e.GetParamAsString(params.FileViewSectionType) == "IMAGE" }, @@ -581,7 +581,7 @@ func TestEventSourceAllEvents(t *testing.T) { return sys.NtUnmapViewOfSection(windows.CurrentProcess(), viewBase) }, func(e *event.Event) bool { - return e.CurrentPid() && e.Type == event.UnmapViewFile && + return e.CurrentPid() && e.Type == event.UnmapViewOfSection && e.GetParamAsString(params.MemProtect) == "READONLY" && e.Params.MustGetUint64(params.FileViewBase) == uint64(viewBase) }, @@ -629,7 +629,7 @@ func TestEventSourceAllEvents(t *testing.T) { }, func(e *event.Event) bool { return e.CurrentPid() && e.Type == event.QueryDNS && e.IsDNS() && - e.Type.Subcategory() == event.DNS && + e.Subcategory() == event.DNS && e.GetParamAsString(params.DNSName) == "dns.google" && e.GetParamAsString(params.DNSRR) == "A" }, @@ -643,7 +643,7 @@ func TestEventSourceAllEvents(t *testing.T) { }, func(e *event.Event) bool { return e.CurrentPid() && e.Type == event.ReplyDNS && e.IsDNS() && - e.Type.Subcategory() == event.DNS && + e.Subcategory() == event.DNS && e.GetParamAsString(params.DNSName) == "dns.google" && e.GetParamAsString(params.DNSRR) == "AAAA" && e.GetParamAsString(params.DNSRcode) == "NOERROR" && diff --git a/internal/etw/stackext.go b/internal/etw/stackext.go index a210e3770..8bbde214b 100644 --- a/internal/etw/stackext.go +++ b/internal/etw/stackext.go @@ -22,7 +22,6 @@ import ( "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/sys/etw" - "golang.org/x/sys/windows" ) // StackExtensions manages stack tracing enablement @@ -39,15 +38,8 @@ func NewStackExtensions(config config.EventSourceConfig) *StackExtensions { // AddStackTracing enables stack tracing for the specified event type. func (s *StackExtensions) AddStackTracing(typ event.Type) { - if !s.config.TestDropMask(typ) { - s.ids = append(s.ids, etw.NewClassicEventID(typ.GUID(), typ.HookID())) - } -} - -// AddStackTracingWith enables stack tracing for the specified provider GUID and event hook id. -func (s *StackExtensions) AddStackTracingWith(guid windows.GUID, hookID uint16) { - if !s.config.TestDropMask(event.TypeFromParts(guid, hookID)) { - s.ids = append(s.ids, etw.NewClassicEventID(guid, hookID)) + if !s.config.TestDropMask(typ) && !typ.EventID().IsEmpty() { + s.ids = append(s.ids, typ.EventID()) } } @@ -69,7 +61,7 @@ func (s *StackExtensions) EnableProcessCallstack() { s.AddStackTracing(event.TerminateThread) } if s.config.EnableModuleEvents { - s.AddStackTracingWith(event.ProcessEventGUID, event.LoadModule.HookID()) + s.AddStackTracing(event.LoadModule) } } diff --git a/internal/etw/stackext_test.go b/internal/etw/stackext_test.go index 48b92b2b1..0833f55ec 100644 --- a/internal/etw/stackext_test.go +++ b/internal/etw/stackext_test.go @@ -19,12 +19,13 @@ package etw import ( + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/sys/etw" "github.com/stretchr/testify/assert" - "testing" - "time" ) func TestStackExtensions(t *testing.T) { @@ -50,11 +51,11 @@ func TestStackExtensions(t *testing.T) { exts.EnableMemoryCallstack() assert.Len(t, exts.EventIds(), 7) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ProcessEventGUID, Type: uint8(event.CreateProcess.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ThreadEventGUID, Type: uint8(event.CreateThread.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ThreadEventGUID, Type: uint8(event.TerminateThread.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: uint8(event.CreateFile.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: uint8(event.RenameFile.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: uint8(event.DeleteFile.HookID())}) - assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.MemEventGUID, Type: uint8(event.VirtualAlloc.HookID())}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ProcessEventGUID, Type: event.CreateProcessID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ThreadEventGUID, Type: event.CreateThreadID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.ThreadEventGUID, Type: event.TerminateThreadID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: event.CreateFileID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: event.RenameFileID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.FileEventGUID, Type: event.DeleteFileID}) + assert.Contains(t, exts.EventIds(), etw.ClassicEventID{GUID: event.MemoryEventGUID, Type: event.VirtualAllocID}) } diff --git a/internal/evasion/direct_syscall_test.go b/internal/evasion/direct_syscall_test.go index 80a394b73..097264317 100644 --- a/internal/evasion/direct_syscall_test.go +++ b/internal/evasion/direct_syscall_test.go @@ -19,13 +19,14 @@ package evasion import ( + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/callstack" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/fs" "github.com/stretchr/testify/require" - "testing" - "time" ) func TestDirectSyscall(t *testing.T) { @@ -39,9 +40,7 @@ func TestDirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -60,9 +59,7 @@ func TestDirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -82,9 +79,7 @@ func TestDirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -104,9 +99,7 @@ func TestDirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -123,7 +116,7 @@ func TestDirectSyscall(t *testing.T) { } for _, tt := range tests { - t.Run(tt.evt.Name, func(t *testing.T) { + t.Run(tt.evt.Name(), func(t *testing.T) { eva := NewDirectSyscall() matches, err := eva.Eval(tt.evt) require.NoError(t, err) diff --git a/internal/evasion/indirect_syscall_test.go b/internal/evasion/indirect_syscall_test.go index 7b07a655d..d8d7881f8 100644 --- a/internal/evasion/indirect_syscall_test.go +++ b/internal/evasion/indirect_syscall_test.go @@ -45,9 +45,7 @@ func TestIndirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -73,9 +71,7 @@ func TestIndirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "SetThreadContext", Timestamp: time.Now(), - Category: event.Thread, PS: &pstypes.PS{ Modules: []pstypes.Module{ {Name: "C:\\Windows\\System32\\ntdll.dll", Size: 32358, Checksum: 23123343, BaseAddress: getNtdllAddress(), DefaultBaseAddress: getNtdllAddress()}, @@ -96,9 +92,7 @@ func TestIndirectSyscall(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -115,7 +109,7 @@ func TestIndirectSyscall(t *testing.T) { } for _, tt := range tests { - t.Run(tt.evt.Name, func(t *testing.T) { + t.Run(tt.evt.Name(), func(t *testing.T) { eva := NewIndirectSyscall() matches, err := eva.Eval(tt.evt) require.NoError(t, err) diff --git a/internal/evasion/scanner_test.go b/internal/evasion/scanner_test.go index 1cf73a887..19d5b84fa 100644 --- a/internal/evasion/scanner_test.go +++ b/internal/evasion/scanner_test.go @@ -41,9 +41,7 @@ func TestScannerProcessEvent(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: event.File, Metadata: event.Metadata{}, Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, diff --git a/pkg/aggregator/aggregator_test.go b/pkg/aggregator/aggregator_test.go index b48b1a50b..39e5fd679 100644 --- a/pkg/aggregator/aggregator_test.go +++ b/pkg/aggregator/aggregator_test.go @@ -19,15 +19,16 @@ package aggregator import ( + "net" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/outputs" "github.com/rabbitstack/fibratus/pkg/outputs/console" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "net" - "testing" - "time" ) func TestNewBufferedAggregator(t *testing.T) { @@ -46,7 +47,7 @@ func TestNewBufferedAggregator(t *testing.T) { for i := 0; i < 4; i++ { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, Params: event.Params{ @@ -63,7 +64,7 @@ func TestNewBufferedAggregator(t *testing.T) { for i := 0; i < 2; i++ { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, Seq: uint64(i), diff --git a/pkg/aggregator/transformers/remove/remove_test.go b/pkg/aggregator/transformers/remove/remove_test.go index 0e114be1a..3add65007 100644 --- a/pkg/aggregator/transformers/remove/remove_test.go +++ b/pkg/aggregator/transformers/remove/remove_test.go @@ -19,18 +19,19 @@ package remove import ( + "net" + "testing" + "github.com/rabbitstack/fibratus/pkg/aggregator/transformers" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "net" - "testing" ) func TestTransform(t *testing.T) { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, Params: event.Params{ diff --git a/pkg/aggregator/transformers/rename/rename_test.go b/pkg/aggregator/transformers/rename/rename_test.go index 8a90259f4..d4cc20dd4 100644 --- a/pkg/aggregator/transformers/rename/rename_test.go +++ b/pkg/aggregator/transformers/rename/rename_test.go @@ -19,18 +19,19 @@ package rename import ( + "net" + "testing" + "github.com/rabbitstack/fibratus/pkg/aggregator/transformers" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "net" - "testing" ) func TestTransform(t *testing.T) { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, Params: event.Params{ diff --git a/pkg/aggregator/transformers/tags/tags_test.go b/pkg/aggregator/transformers/tags/tags_test.go index 4d3a84cf4..c6a161daf 100644 --- a/pkg/aggregator/transformers/tags/tags_test.go +++ b/pkg/aggregator/transformers/tags/tags_test.go @@ -32,7 +32,7 @@ import ( func TestTransform(t *testing.T) { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, Params: event.Params{ diff --git a/pkg/aggregator/transformers/trim/trim_test.go b/pkg/aggregator/transformers/trim/trim_test.go index 43270822b..b5dfa31bd 100644 --- a/pkg/aggregator/transformers/trim/trim_test.go +++ b/pkg/aggregator/transformers/trim/trim_test.go @@ -19,26 +19,24 @@ package trim import ( + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/aggregator/transformers" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" - "time" ) func TestTransform(t *testing.T) { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/alertsender/alert.go b/pkg/alertsender/alert.go index eebb3a43b..a785cc9b8 100644 --- a/pkg/alertsender/alert.go +++ b/pkg/alertsender/alert.go @@ -267,8 +267,8 @@ func (a Alert) MarshalJSON() ([]byte, error) { Ancestors []string `json:"ancestors"` } `json:"proc,omitempty"` }{ - Name: e.Name, - Category: string(e.Category), + Name: e.Name(), + Category: e.Category().String(), Timestamp: e.Timestamp, Params: make(map[string]any), Callstack: make([]string, 0, len(e.Callstack)), diff --git a/pkg/alertsender/alert_test.go b/pkg/alertsender/alert_test.go index 2e7b5b08d..4425badfa 100644 --- a/pkg/alertsender/alert_test.go +++ b/pkg/alertsender/alert_test.go @@ -46,13 +46,11 @@ func TestAlertString(t *testing.T) { }, { NewAlertWithEvents("Credential discovery via VaultCmd.exe", "Suspicious vault enumeration via VaultCmd tool", nil, Normal, []*event.Event{{ - Type: event.CreateProcess, - Category: event.Process, + Type: event.CreateProcess, Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost-fake.exe -k RPCSS"}, params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost-fake.exe"}}, - Name: "CreateProcess", - PID: 1023, + PID: 1023, PS: &pstypes.PS{ Name: "svchost.exe", Cmdline: "C:\\Windows\\System32\\svchost.exe", @@ -68,13 +66,11 @@ func TestAlertString(t *testing.T) { }, { NewAlertWithEvents("Credential discovery via VaultCmd.exe", "", nil, Normal, []*event.Event{{ - Type: event.CreateProcess, - Category: event.Process, + Type: event.CreateProcess, Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost-fake.exe -k RPCSS"}, params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost-fake.exe"}}, - Name: "CreateProcess", - PID: 1023, + PID: 1023, PS: &pstypes.PS{ Name: "svchost.exe", Cmdline: "C:\\Windows\\System32\\svchost.exe", @@ -99,13 +95,11 @@ func TestAlertString(t *testing.T) { func TestAlertJSON(t *testing.T) { alert := NewAlertWithEvents("Credential discovery via VaultCmd.exe", "Suspicious vault enumeration via VaultCmd tool", nil, Normal, []*event.Event{{ - Type: event.CreateProcess, - Category: event.Process, + Type: event.CreateProcess, Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost-fake.exe -k RPCSS"}, params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost-fake.exe"}}, - Name: "CreateProcess", - PID: 1023, + PID: 1023, PS: &pstypes.PS{ Name: "svchost.exe", Cmdline: "C:\\Windows\\System32\\svchost.exe", diff --git a/pkg/alertsender/eventlog/eventlog_test.go b/pkg/alertsender/eventlog/eventlog_test.go index 0d9ae3891..7b765d7f2 100644 --- a/pkg/alertsender/eventlog/eventlog_test.go +++ b/pkg/alertsender/eventlog/eventlog_test.go @@ -41,16 +41,13 @@ func TestEventlogSender(t *testing.T) { require.NoError(t, s.Send(alertsender.Alert{ Events: []*event.Event{ { - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -136,16 +133,13 @@ func TestEventlogSender(t *testing.T) { }, }, { - Type: event.CreateProcess, - Tid: 2184, - PID: 1022, - CPU: 2, - Seq: 3, - Name: "CreateProcess", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates a new process", + Type: event.CreateProcess, + Tid: 2184, + PID: 1022, + CPU: 2, + Seq: 3, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe -k RPCSS"}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe"}, diff --git a/pkg/alertsender/mail/renderer_test.go b/pkg/alertsender/mail/renderer_test.go index 751244347..e5438d3a9 100644 --- a/pkg/alertsender/mail/renderer_test.go +++ b/pkg/alertsender/mail/renderer_test.go @@ -19,6 +19,10 @@ package mail import ( + "strings" + "testing" + "time" + "github.com/antchfx/htmlquery" "github.com/rabbitstack/fibratus/pkg/alertsender" "github.com/rabbitstack/fibratus/pkg/event" @@ -30,9 +34,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "strings" - "testing" - "time" ) func TestRenderHTMLTemplate(t *testing.T) { @@ -51,16 +52,13 @@ func TestRenderHTMLTemplate(t *testing.T) { }, Events: []*event.Event{ { - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -146,16 +144,13 @@ func TestRenderHTMLTemplate(t *testing.T) { }, }, { - Type: event.CreateProcess, - Tid: 2184, - PID: 1022, - CPU: 2, - Seq: 3, - Name: "CreateProcess", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates a new process", + Type: event.CreateProcess, + Tid: 2184, + PID: 1022, + CPU: 2, + Seq: 3, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe -k RPCSS"}, params.Exe: {Name: params.Exe, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe"}, diff --git a/pkg/cap/header.go b/pkg/cap/header.go index c1720164c..7df0a4e5c 100644 --- a/pkg/cap/header.go +++ b/pkg/cap/header.go @@ -34,7 +34,7 @@ const magic = 0x6669627261747573 // major represents the major digit of the cap file format. Incrementing the major digit makes older cap readers not // capable to replay the capture file -const major = uint8(2) +const major = uint8(3) // minor represents the minor digit of the cap file format const minor = uint8(0) diff --git a/pkg/cap/version/version_windows.go b/pkg/cap/version/version_windows.go index d2172bbe9..7585230e5 100644 --- a/pkg/cap/version/version_windows.go +++ b/pkg/cap/version/version_windows.go @@ -26,6 +26,8 @@ const ( EvtSecV1 Version = iota + 1 // EvtSecV2 is the v2 of the event section EvtSecV2 + // EvtSecV3 is the v3 of the event section + EvtSecV3 ) const ( diff --git a/pkg/cap/writer_windows.go b/pkg/cap/writer_windows.go index bd381a263..2cc95f989 100644 --- a/pkg/cap/writer_windows.go +++ b/pkg/cap/writer_windows.go @@ -48,7 +48,7 @@ type stats struct { procsWritten uint64 } -func (s *stats) incKevts(evt *event.Event) { +func (s *stats) incEvts(evt *event.Event) { if !evt.Type.OnlyState() { atomic.AddUint64(&s.evtsWritten, 1) } @@ -201,7 +201,7 @@ func (w *writer) Write(evtsc <-chan *event.Event, errs <-chan error) chan error continue } // update stats - w.stats.incKevts(evt) + w.stats.incEvts(evt) w.stats.incBytes(uint64(l)) w.stats.incProcs(evt) case err := <-errs: @@ -223,7 +223,7 @@ func (w *writer) write(b []byte) error { overflowEvents.Add(1) return fmt.Errorf("event size overflow by %d bytes", l-maxKevtSize) } - if err := w.ws(section.Event, capver.EvtSecV2, 0, uint32(l)); err != nil { + if err := w.ws(section.Event, capver.EvtSecV3, 0, uint32(l)); err != nil { evtWriteErrors.Add(1) return err } diff --git a/pkg/cap/writer_windows_test.go b/pkg/cap/writer_windows_test.go index d13907715..590e05def 100644 --- a/pkg/cap/writer_windows_test.go +++ b/pkg/cap/writer_windows_test.go @@ -67,16 +67,13 @@ func TestWrite(t *testing.T) { for i := 0; i < 100; i++ { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: uint8(i / 2), - Seq: uint64(i + 1), - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: uint8(i / 2), + Seq: uint64(i + 1), + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/config/eventsource.go b/pkg/config/eventsource.go index d99ad49a9..2d691a316 100644 --- a/pkg/config/eventsource.go +++ b/pkg/config/eventsource.go @@ -26,7 +26,7 @@ import ( "time" "github.com/rabbitstack/fibratus/pkg/event" - "github.com/rabbitstack/fibratus/pkg/util/bitmask" + "github.com/rabbitstack/fibratus/pkg/util/bitmap" pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" "github.com/spf13/viper" @@ -97,8 +97,7 @@ type EventSourceConfig struct { // ExcludedImages are process image names that will be rejected if they generate a kernel event. ExcludedImages []string `json:"blacklist.images" yaml:"blacklist.images"` - dropMasks *bitmask.Bitmask - allMasks *bitmask.Bitmask + dropBitmap bitmap.Bitmap[event.Type] excludedImages map[string]bool } @@ -121,21 +120,14 @@ func (c *EventSourceConfig) initFromViper(v *viper.Viper) { c.ExcludedEvents = v.GetStringSlice(excludedEvents) c.ExcludedImages = v.GetStringSlice(excludedImages) - c.dropMasks = bitmask.New() - c.allMasks = bitmask.New() - c.excludedImages = make(map[string]bool) for _, name := range c.ExcludedEvents { - if typ := event.NameToType(name); typ != event.UnknownType { - c.dropMasks.Set(typ.ID()) + if typ, ok := event.ParseType(name); ok { + c.dropBitmap.Set(typ) } } - for _, typ := range event.AllWithState() { - c.allMasks.Set(typ.ID()) - } - for _, name := range c.ExcludedImages { c.excludedImages[name] = true } @@ -145,53 +137,34 @@ func (c *EventSourceConfig) initFromViper(v *viper.Viper) { func (c *EventSourceConfig) Init() { c.excludedImages = make(map[string]bool) - if c.dropMasks == nil { - c.dropMasks = bitmask.New() - } for _, name := range c.ExcludedEvents { - for _, typ := range event.NameToTypes(name) { - if typ != event.UnknownType { - c.dropMasks.Set(typ.ID()) - } + if typ, ok := event.ParseType(name); ok { + c.dropBitmap.Set(typ) } } for _, name := range c.ExcludedImages { c.excludedImages[name] = true } - - if c.allMasks == nil { - c.allMasks = bitmask.New() - } - for _, typ := range event.AllWithState() { - c.allMasks.Set(typ.ID()) - } } // SetDropMask inserts the event mask in the bitset to // instruct the given event type should be dropped from // the event stream. func (c *EventSourceConfig) SetDropMask(typ event.Type) { - c.dropMasks.Set(typ.ID()) + c.dropBitmap.Set(typ) } // TestDropMask checks if the specified event type has // the drop mask in the bitset. func (c *EventSourceConfig) TestDropMask(typ event.Type) bool { - return c.dropMasks.IsSet(typ.ID()) -} - -// ExcludeEvent determines whether the supplied short -// event ID exists in the bitset of excluded events. -func (c *EventSourceConfig) ExcludeEvent(id uint) bool { - return c.dropMasks.IsSet(id) + return c.dropBitmap.Has(typ) } -// EventExists determines if the provided event ID exists -// in the internal event catalog by checking the event ID -// bitmask. -func (c *EventSourceConfig) EventExists(id uint) bool { - return c.allMasks.IsSet(id) +// ExcludeEvent determines whether the event type is declared +// in the exclusion list. +func (c *EventSourceConfig) ExcludeEvent(typ event.Type) bool { + return c.dropBitmap.Has(typ) } // ExcludeImage determines whether the process generating event is present in the diff --git a/pkg/config/eventsource_test.go b/pkg/config/eventsource_test.go index 157b88eb7..815fb8ff9 100644 --- a/pkg/config/eventsource_test.go +++ b/pkg/config/eventsource_test.go @@ -56,7 +56,7 @@ func TestEventSourceConfig(t *testing.T) { assert.False(t, c.EventSource.EnableModuleEvents) assert.False(t, c.EventSource.EnableFileIOEvents) - assert.False(t, c.EventSource.ExcludeEvent(event.CreateProcess.ID())) + assert.False(t, c.EventSource.ExcludeEvent(event.CreateProcess)) assert.True(t, c.EventSource.ExcludeImage(&pstypes.PS{Name: "svchost.exe"})) assert.False(t, c.EventSource.ExcludeImage(&pstypes.PS{Name: "explorer.exe"})) diff --git a/pkg/event/batch_test.go b/pkg/event/batch_test.go index e53039d1b..f82d6d91f 100644 --- a/pkg/event/batch_test.go +++ b/pkg/event/batch_test.go @@ -20,6 +20,9 @@ package event import ( "encoding/json" + "testing" + "time" + "github.com/magiconair/properties/assert" "github.com/rabbitstack/fibratus/pkg/event/params" htypes "github.com/rabbitstack/fibratus/pkg/handle/types" @@ -27,22 +30,17 @@ import ( "github.com/rabbitstack/fibratus/pkg/util/va" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" - "testing" - "time" ) func TestBatchMarshalJSON(t *testing.T) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -101,16 +99,13 @@ func TestBatchMarshalJSON(t *testing.T) { } evt1 := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 459, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 459, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -169,16 +164,13 @@ func TestBatchMarshalJSON(t *testing.T) { } evt2 := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 829, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 829, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/event/bitset.go b/pkg/event/bitset.go deleted file mode 100644 index abfb0a157..000000000 --- a/pkg/event/bitset.go +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2021-2022 by Nedim Sabic Sabic - * https://www.fibratus.io - * All Rights Reserved. - * - * Licensed 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 event - -import ( - "github.com/bits-and-blooms/bitset" - "github.com/rabbitstack/fibratus/pkg/util/bitmask" -) - -// BitSetType defines the bitset type -type BitSetType uint8 - -const ( - // BitmaskBitSet designates the mask-based event id bitset - BitmaskBitSet BitSetType = iota + 1 - // TypeBitSet designates the uint16 number space event type bitset - TypeBitSet - // CategoryBitSet designates the event category bitset - CategoryBitSet -) - -// BitSets handles the group of category/event type bitsets -// and the bitmask for evaluating event ids bits. -type BitSets struct { - bitmask *bitmask.Bitmask - cats *bitset.BitSet - types *bitset.BitSet -} - -// SetBit sets the bit dictated by the bitset type. -func (b *BitSets) SetBit(bs BitSetType, typ Type) { - switch bs { - case BitmaskBitSet: - if b.bitmask == nil { - b.bitmask = bitmask.New() - } - b.bitmask.Set(typ.ID()) - - case TypeBitSet: - if b.types == nil { - b.types = bitset.New(uint(MaxTypeID() + 1)) - } - b.types.Set(uint(typ.HookID())) - - case CategoryBitSet: - if b.cats == nil { - b.cats = bitset.New(MaxCategoryIndex + 1) - } - b.cats.Set(uint(typ.Category().Index())) - } -} - -// SetCategoryBit toggles the category bit in the bitset. -func (b *BitSets) SetCategoryBit(c Category) { - if b.cats == nil { - b.cats = bitset.New(MaxCategoryIndex + 1) - } - b.cats.Set(uint(c.Index())) -} - -// IsBitSet checks if any of the populated bitsets -// contain the type, event ID, or category bit. -// This method evaluates first the event type bitset. -// The event type bitset should only be initialized -// if all event types pertain to the same category. -// Otherwise, event id bitset and last category bitset -// are tested for respective bits. -func (b *BitSets) IsBitSet(evt *Event) bool { - if b.types != nil && b.types.Test(uint(evt.Type.HookID())) { - return true - } - return (b.bitmask != nil && b.bitmask.IsSet(evt.Type.ID())) || - (b.cats != nil && b.cats.Test(uint(evt.Category.Index()))) -} - -// IsInitialized checks if the given bitset type is initialized. -func (b *BitSets) IsInitialized(bs BitSetType) bool { - switch bs { - case BitmaskBitSet: - return b.bitmask != nil - case TypeBitSet: - return b.types != nil - case CategoryBitSet: - return b.cats != nil - } - return false -} diff --git a/pkg/event/bitset_test.go b/pkg/event/bitset_test.go deleted file mode 100644 index df69110db..000000000 --- a/pkg/event/bitset_test.go +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2021-2022 by Nedim Sabic Sabic - * https://www.fibratus.io - * All Rights Reserved. - * - * Licensed 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 event - -import ( - "testing" - - "github.com/rabbitstack/fibratus/pkg/util/bitmask" - - "github.com/rabbitstack/fibratus/pkg/sys/etw" - "github.com/stretchr/testify/assert" -) - -func TestBitmask(t *testing.T) { - var tests = []struct { - typ Type - expected bool - }{ - {TerminateThread, true}, - {TerminateProcess, true}, - {CreateThread, true}, - {CreateFile, false}, - {WriteFile, false}, - {LoadModule, false}, - {MapFileRundown, true}, - {ProcessRundown, true}, - } - - b := bitmask.New() - for _, typ := range AllWithState() { - if typ == WriteFile || typ == LoadModule || typ == CreateFile { - continue - } - b.Set(typ.ID()) - } - - for _, tt := range tests { - t.Run(tt.typ.String(), func(t *testing.T) { - assert.Equal(t, tt.expected, b.IsSet(tt.typ.ID())) - }) - } -} - -func TestBitSets(t *testing.T) { - var tests = []struct { - evt *Event - expected bool - }{ - {&Event{Type: TerminateThread}, true}, - {&Event{Type: TerminateProcess}, true}, - {&Event{Type: CreateThread, Category: Thread}, true}, - {&Event{Type: CreateFile}, false}, - {&Event{Type: WriteFile}, false}, - {&Event{Type: LoadModule}, false}, - {&Event{Type: MapFileRundown}, true}, - {&Event{Type: ProcessRundown}, true}, - } - - var bitsets BitSets - - bitsets.SetBit(BitmaskBitSet, TerminateThread) - bitsets.SetBit(TypeBitSet, TerminateProcess) - bitsets.SetBit(CategoryBitSet, CreateThread) - bitsets.SetBit(TypeBitSet, MapFileRundown) - bitsets.SetBit(BitmaskBitSet, ProcessRundown) - - for _, tt := range tests { - t.Run(tt.evt.Type.String(), func(t *testing.T) { - assert.Equal(t, tt.expected, bitsets.IsBitSet(tt.evt)) - }) - } -} - -func BenchmarkBitmask(b *testing.B) { - b.ReportAllocs() - - bm := bitmask.New() - bm.Set(TerminateThread.ID()) - bm.Set(CreateThread.ID()) - bm.Set(TerminateProcess.ID()) - bm.Set(CreateFile.ID()) - - evt := &etw.EventRecord{Header: etw.EventHeader{ProviderID: ThreadEventGUID, EventDescriptor: etw.EventDescriptor{Opcode: 2}}} - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - if !bm.IsSet(evt.ID()) { - panic("mask should be present") - } - } -} - -func BenchmarkStdlibMap(b *testing.B) { - b.ReportAllocs() - - evts := make(map[Type]bool) - evts[TerminateThread] = true - evts[CreateThread] = true - evts[TerminateProcess] = true - evts[CreateFile] = true - - evt := etw.EventRecord{Header: etw.EventHeader{ProviderID: ThreadEventGUID, EventDescriptor: etw.EventDescriptor{Opcode: 2}}} - etype := NewTypeFromEventRecord(&evt) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - if !evts[etype] { - panic("event should be present") - } - } -} diff --git a/pkg/event/category.go b/pkg/event/category.go index 6204a5214..1e807d365 100644 --- a/pkg/event/category.go +++ b/pkg/event/category.go @@ -18,102 +18,99 @@ package event -import ( - "slices" - - "github.com/rabbitstack/fibratus/pkg/util/hashers" -) - // Category is the type alias for event categories -type Category string +type Category uint8 // Subcategory is the type alias for event subcategories -type Subcategory string +type Subcategory uint8 const ( // Registry is the category for registry related events - Registry Category = "registry" + Registry Category = iota + 1 // File is the category for file system events - File Category = "file" - // Net is the category for network events - Net Category = "net" + File + // Network is the category for network events + Network // Process is the category for process events - Process Category = "process" + Process // Thread is the category for thread events - Thread Category = "thread" + Thread // Module is the category for module (dll, exe, sys) events - Module Category = "module" - // Driver is the category for driver events - Driver Category = "driver" - // Mem is the category for memory events - Mem Category = "mem" + Module + // Memory is the category for memory events + Memory // Object the category for object manager events - Object Category = "object" + Object // Other is the category for uncategorized events - Other Category = "other" - // Unknown is the category for events that couldn't match any of the previous categories - Unknown Category = "unknown" + Other + MaxCategory // sentinel ) const ( // DNS designates the DNS (Domain Name Service) event subcategory - DNS Subcategory = "dns" - // None identifies no subcategory - None Subcategory = "none" + DNS Subcategory = iota + 1 + MaxSubcategory // sentinel ) -// Hash obtains the hash of the category string. -func (c Category) Hash() uint32 { - return hashers.FnvUint32([]byte(c)) -} - -// MaxCategoryIndex designates the maximum category index. -const MaxCategoryIndex = 11 - -// Index returns a numerical category index. -func (c Category) Index() uint8 { +// String returns the category string representation. +func (c Category) String() string { switch c { case Registry: - return 1 + return "registry" case File: - return 2 - case Net: - return 3 + return "file" + case Network: + return "network" case Process: - return 4 + return "process" case Thread: - return 5 + return "thread" case Module: - return 6 - case Driver: - return 7 - case Mem: - return 8 + return "module" + case Memory: + return "memory" case Object: - return 9 + return "object" case Other: - return 10 + return "other" default: - return MaxCategoryIndex + return "unknown" } } -// Categories returns all available categories. -func Categories() []string { - return []string{ - string(Registry), - string(File), - string(Net), - string(Process), - string(Thread), - string(Module), - string(Mem), - string(Driver), - string(Other), - string(Unknown), - string(Object), +func (sc Subcategory) String() string { + switch sc { + case DNS: + return "dns" + default: + return "unknown" } } +var categories = map[string]Category{ + "registry": Registry, + "file": File, + "network": Network, + "process": Process, + "thread": Thread, + "module": Module, + "memory": Memory, + "object": Object, + "other": Other, +} + +// NumCategories returns a total number of recognized categories. +func NumCategories() int { return len(categories) } + +// ParseCategory converts the category from the bare string. Returns +// the category and the bool indicating if the conversion succeeded. +func ParseCategory(s string) (Category, bool) { + c, ok := categories[s] + return c, ok +} + // IsCategoryKnown indicates if the category is known given its name. -func IsCategoryKnown(name string) bool { return slices.Contains(Categories(), name) } +func IsCategoryKnown(s string) (exists bool) { + _, exists = ParseCategory(s) + return +} diff --git a/pkg/event/event.go b/pkg/event/event.go index 66ca2905c..ba1b03039 100644 --- a/pkg/event/event.go +++ b/pkg/event/event.go @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 by Nedim Sabic Sabic + * Copyright 2019-2026 by Nedim Sabic Sabic * https://www.fibratus.io * All Rights Reserved. * @@ -86,12 +86,6 @@ type Event struct { CPU uint8 `json:"cpu"` _ uint8 // padding - // Name is the human friendly name of the event. - Name string `json:"name"` - // Category designates the category to which this event pertains. - Category Category `json:"category"` - // Description is the short explanation that describes the purpose of the event. - Description string `json:"description"` // Host is the machine name that reported the generated event. Host string `json:"host"` // Params stores the collection of event parameters. @@ -107,6 +101,26 @@ type Event struct { mmux sync.RWMutex } +// Name returns the human friendly event name. +func (e *Event) Name() string { + return e.Type.String() +} + +// Category designates the category to which this event pertains. +func (e *Event) Category() Category { + return table[e.Type].Category +} + +// Subcategory designates the subcategory to which this event pertains. +func (e *Event) Subcategory() Subcategory { + return table[e.Type].Subcategory +} + +// Description is the short explanation that describes the purpose of the event. +func (e *Event) Description() string { + return table[e.Type].Description +} + // String returns event's string representation. func (e *Event) String() string { e.mmux.RLock() @@ -132,9 +146,9 @@ func (e *Event) String() string { e.Tid, e.Type, e.CPU, - e.Name, - e.Category, - e.Description, + e.Name(), + e.Category(), + e.Description(), e.Host, e.Timestamp, e.Params, @@ -161,9 +175,9 @@ func (e *Event) String() string { e.Tid, e.Type, e.CPU, - e.Name, - e.Category, - e.Description, + e.Name(), + e.Category(), + e.Description(), e.Host, e.Timestamp, e.Params, @@ -191,8 +205,8 @@ func (e *Event) StringShort() string { e.Seq, e.PID, e.Tid, - e.Name, - e.Category, + e.Name(), + e.Category(), e.Host, e.Timestamp, e.Params, @@ -212,8 +226,8 @@ func (e *Event) StringShort() string { e.Seq, e.PID, e.Tid, - e.Name, - e.Category, + e.Name(), + e.Category(), e.Host, e.Timestamp, e.Params, diff --git a/pkg/event/event_windows.go b/pkg/event/event_windows.go index 38f1eb95f..b9825a5b7 100644 --- a/pkg/event/event_windows.go +++ b/pkg/event/event_windows.go @@ -27,6 +27,7 @@ import ( "unsafe" "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/network" "github.com/rabbitstack/fibratus/pkg/sys" "github.com/rabbitstack/fibratus/pkg/sys/etw" "github.com/rabbitstack/fibratus/pkg/util/filetime" @@ -49,13 +50,12 @@ var ( // New constructs a fresh event instance with basic fields and parameters // from the raw ETW event record. -func New(seq uint64, r *etw.EventRecord) *Event { +func New(seq uint64, r *etw.EventRecord, typ Type) *Event { var ( pid = r.Header.ProcessID tid = r.Header.ThreadID cpu = *(*uint8)(unsafe.Pointer(&r.BufferContext.ProcessorIndex[0])) ts = filetime.ToEpoch(r.Header.Timestamp) - typ = NewTypeFromEventRecord(r) ) e := &Event{ @@ -64,8 +64,6 @@ func New(seq uint64, r *etw.EventRecord) *Event { Tid: tid, CPU: cpu, Type: typ, - Category: typ.Category(), - Name: typ.String(), Params: make(map[string]*Param), Timestamp: ts, Host: hostname.Get(), @@ -86,7 +84,7 @@ func (e *Event) RawTimestamp() uint64 { } func (e *Event) adjustPID() { - switch e.Category { + switch e.Category() { case Module: // sometimes the pid present in event header is invalid // but, we can get the valid one from the event parameters @@ -94,16 +92,7 @@ func (e *Event) adjustPID() { e.PID, _ = e.Params.GetPid() } case File: - if !e.IsMapViewFile() && !e.IsUnmapViewFile() { - // take thread id from the event parameters - e.Tid, _ = e.Params.GetTid() - } - switch { - case e.InvalidPid() && e.Type == MapFileRundown: - // a valid pid for map rundown events - // is located in the event parameters - e.PID = e.Params.MustGetPid() - case e.InvalidPid(): + if e.InvalidPid() { // on some Windows versions the value of // the PID is invalid in the event header access := uint32(windows.THREAD_QUERY_LIMITED_INFORMATION) @@ -123,7 +112,7 @@ func (e *Event) adjustPID() { if e.IsCreateProcess() { e.PID, _ = e.Params.GetPid() } - case Net: + case Network: if !e.IsDNS() { e.PID, _ = e.Params.GetPid() } @@ -132,6 +121,12 @@ func (e *Event) adjustPID() { e.PID, _ = e.Params.GetPid() e.Tid, _ = e.Params.GetTid() } + case Memory: + if e.Type == MapViewSectionRundown { + // a valid pid for map rundown events + // is located in the event parameters + e.PID = e.Params.MustGetPid() + } } } @@ -158,23 +153,17 @@ func IsCurrentProcDropped(pid uint32) bool { return DropCurrentProc && pid == cu // IsNetworkTCP determines whether the event pertains to network TCP events. func (e *Event) IsNetworkTCP() bool { - return e.Category == Net && !e.IsNetworkUDP() -} - -// IsNetworkUDP determines whether the event pertains to network UDP events. -func (e *Event) IsNetworkUDP() bool { - return e.Type == RecvUDPv4 || e.Type == RecvUDPv6 || e.Type == SendUDPv4 || e.Type == SendUDPv6 + return e.Category() == Network && network.L4Proto(e.Params.MustGetUint32(params.NetL4Proto)) == network.TCP } // IsDNS determines whether the event is a DNS question/answer. func (e *Event) IsDNS() bool { - return e.Type.Subcategory() == DNS + return e.Subcategory() == DNS } // IsRundown determines if this is a rundown events. func (e *Event) IsRundown() bool { - return e.Type == ProcessRundown || e.Type == ThreadRundown || e.Type == ModuleRundown || - e.Type == FileRundown || e.Type == RegKCBRundown + return e.Type.StateSnapshot() } // IsSuccess checks if the event contains the status parameter @@ -228,8 +217,8 @@ func (e *Event) IsRegCreateKey() bool { return e.Type == RegCreateKey func (e *Event) IsProcessRundown() bool { return e.Type == ProcessRundown } func (e *Event) IsProcessRundownInternal() bool { return e.Type == ProcessRundownInternal } func (e *Event) IsVirtualAlloc() bool { return e.Type == VirtualAlloc } -func (e *Event) IsMapViewFile() bool { return e.Type == MapViewFile } -func (e *Event) IsUnmapViewFile() bool { return e.Type == UnmapViewFile } +func (e *Event) IsMapViewOfSection() bool { return e.Type == MapViewOfSection } +func (e *Event) IsUnmapViewOfSection() bool { return e.Type == UnmapViewOfSection } func (e *Event) IsStackWalk() bool { return e.Type == StackWalk } func (e *Event) IsOpenThread() bool { return e.Type == OpenThread } func (e *Event) IsOpenProcess() bool { return e.Type == OpenProcess } @@ -336,7 +325,7 @@ func (e *Event) RundownKey() uint64 { binary.LittleEndian.PutUint64(b, fileObject) return hashers.FnvUint64(b) - case MapFileRundown: + case MapViewSectionRundown: b := make([]byte, 12) fileKey, _ := e.Params.GetUint64(params.FileKey) binary.LittleEndian.PutUint32(b, e.PID) @@ -362,7 +351,7 @@ func (e *Event) PartialKey() uint64 { switch e.Type { case WriteFile, ReadFile: return e.Params.MustGetUint64(params.FileObject) + uint64(e.PID) - case MapViewFile, UnmapViewFile: + case MapViewOfSection, UnmapViewOfSection: return e.Params.MustGetUint64(params.FileViewBase) + uint64(e.PID) case CreateFile: file, _ := e.Params.GetString(params.FilePath) @@ -378,38 +367,34 @@ func (e *Event) PartialKey() uint64 { tid := e.Params.MustGetUint32(params.ThreadID) access := e.Params.MustGetUint32(params.DesiredAccess) return uint64(tid + access + e.PID) - case AcceptTCPv4, RecvTCPv4, RecvUDPv4: - b := make([]byte, 10) - ip, _ := e.Params.GetIP(params.NetSIP) - port, _ := e.Params.GetUint16(params.NetSport) - binary.LittleEndian.PutUint32(b, e.PID) - binary.LittleEndian.PutUint32(b, binary.BigEndian.Uint32(ip.To4())) - binary.LittleEndian.PutUint16(b, port) - return hashers.FnvUint64(b) - case AcceptTCPv6, RecvTCPv6, RecvUDPv6: - b := make([]byte, 22) + case Accept, Recv: + var b []byte ip, _ := e.Params.GetIP(params.NetSIP) + if ip.To4() != nil { + b = make([]byte, 10) + binary.LittleEndian.PutUint32(b, binary.BigEndian.Uint32(ip.To4())) + } else { + b = make([]byte, 22) + binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[0:8])) + binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[8:16])) + } port, _ := e.Params.GetUint16(params.NetSport) binary.LittleEndian.PutUint32(b, e.PID) - binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[0:8])) - binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[8:16])) binary.LittleEndian.PutUint16(b, port) return hashers.FnvUint64(b) - case ConnectTCPv4, SendTCPv4, SendUDPv4: - b := make([]byte, 10) - ip, _ := e.Params.GetIP(params.NetDIP) - port, _ := e.Params.GetUint16(params.NetDport) - binary.LittleEndian.PutUint32(b, e.PID) - binary.LittleEndian.PutUint32(b, binary.BigEndian.Uint32(ip.To4())) - binary.LittleEndian.PutUint16(b, port) - return hashers.FnvUint64(b) - case ConnectTCPv6, SendTCPv6, SendUDPv6: - b := make([]byte, 22) + case Connect, Send: + var b []byte ip, _ := e.Params.GetIP(params.NetDIP) + if ip.To4() != nil { + b = make([]byte, 10) + binary.LittleEndian.PutUint32(b, binary.BigEndian.Uint32(ip.To4())) + } else { + b = make([]byte, 22) + binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[0:8])) + binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[8:16])) + } port, _ := e.Params.GetUint16(params.NetDport) binary.LittleEndian.PutUint32(b, e.PID) - binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[0:8])) - binary.LittleEndian.PutUint64(b, binary.BigEndian.Uint64(ip.To16()[8:16])) binary.LittleEndian.PutUint16(b, port) return hashers.FnvUint64(b) case RegOpenKey, RegQueryKey, RegQueryValue, @@ -523,21 +508,21 @@ func (e *Event) Summary() string { case RegQueryValue: key := e.GetParamAsString(params.RegPath) return printSummary(e, fmt.Sprintf("queried %s value", key)) - case AcceptTCPv4, AcceptTCPv6: + case Accept: ip, _ := e.Params.GetIP(params.NetSIP) port, _ := e.Params.GetUint16(params.NetSport) return printSummary(e, fmt.Sprintf("accepted connection from %v and %d port", ip, port)) - case ConnectTCPv4, ConnectTCPv6: + case Connect: ip, _ := e.Params.GetIP(params.NetDIP) port, _ := e.Params.GetUint16(params.NetDport) return printSummary(e, fmt.Sprintf("connected to %v and %d port", ip, port)) - case SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6: + case Send: ip, _ := e.Params.GetIP(params.NetDIP) port, _ := e.Params.GetUint16(params.NetDport) size, _ := e.Params.GetUint32(params.NetSize) return printSummary(e, fmt.Sprintf("sent %d bytes to %v and %d port", size, ip, port)) - case RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6: + case Recv: ip, _ := e.Params.GetIP(params.NetSIP) port, _ := e.Params.GetUint16(params.NetSport) size, _ := e.Params.GetUint32(params.NetSize) @@ -549,10 +534,10 @@ func (e *Event) Summary() string { case VirtualFree: addr := e.GetParamAsString(params.MemBaseAddress) return printSummary(e, fmt.Sprintf("released memory at %s address", addr)) - case MapViewFile: + case MapViewOfSection: sec := e.GetParamAsString(params.FileViewSectionType) return printSummary(e, fmt.Sprintf("mapped view of %s section", sec)) - case UnmapViewFile: + case UnmapViewOfSection: sec := e.GetParamAsString(params.FileViewSectionType) return printSummary(e, fmt.Sprintf("unmapped view of %s section", sec)) case QueryDNS: diff --git a/pkg/event/event_windows_test.go b/pkg/event/event_windows_test.go index 573c2c511..a12094ab2 100644 --- a/pkg/event/event_windows_test.go +++ b/pkg/event/event_windows_test.go @@ -19,41 +19,25 @@ package event import ( + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/fs" pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" - "time" ) -func TestEventIsNetworkTCP(t *testing.T) { - e1 := Event{Type: AcceptTCPv4, Category: Net} - e2 := Event{Type: SendUDPv6, Category: Net} - assert.True(t, e1.IsNetworkTCP()) - assert.False(t, e2.IsNetworkTCP()) -} - -func TestEventIsNetworkUDP(t *testing.T) { - e1 := Event{Type: RecvUDPv4} - e2 := Event{Type: SendTCPv6} - assert.True(t, e1.IsNetworkUDP()) - assert.False(t, e2.IsNetworkUDP()) -} - func TestEventSummary(t *testing.T) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, diff --git a/pkg/event/formatter.go b/pkg/event/formatter.go index d1181ada7..7a3e0361b 100644 --- a/pkg/event/formatter.go +++ b/pkg/event/formatter.go @@ -209,13 +209,10 @@ func (f *ColorFormatter) colourTag(tag string, e *Event) string { case seq: // sequence number is ok to render as dim gray return colorizer.SpanDim(colorizer.Span(colorizer.Gray, strconv.FormatUint(e.Seq, 10))) - case ts: return f.colourTimestamp(e) - case cpu: return colorizer.Span(colorizer.Yellow, strconv.FormatUint(uint64(e.CPU), 10)) - case proc: // render process name with bold green as it is the most important // identity anchor on the line. Analysts scan for it first. @@ -224,85 +221,70 @@ func (f *ColorFormatter) colourTag(tag string, e *Event) string { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.SpanBold(colorizer.Green, ps.Name) - case pid: return colorizer.Span(colorizer.Green, strconv.FormatUint(uint64(e.PID), 10)) - case ppid: ps := e.PS if ps == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.Green, strconv.FormatUint(uint64(ps.Ppid), 10)) - case tid: return colorizer.Span(colorizer.Green, strconv.FormatUint(uint64(e.Tid), 10)) - case exe: ps := e.PS if ps == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.White, ps.Exe) - case pexe: ps := e.PS if ps == nil || ps.Parent == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.White, ps.Parent.Exe) - case cmd: ps := e.PS if ps == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.White, ps.Cmdline) - case pcmd: ps := e.PS if ps == nil || ps.Parent == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.White, ps.Parent.Cmdline) - case cwd: ps := e.PS if ps == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.White, ps.Cwd) - case sid: ps := e.PS if ps == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.Gray, ps.SID) - case pproc: ps := e.PS if ps == nil || ps.Parent == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.Green, ps.Parent.Name) - case typ: return e.Type.color() - case cat: - return colorizer.Span(colorizer.Magenta, string(e.Category)) - + return colorizer.Span(colorizer.Magenta, e.Category().String()) case parameters: return e.Params.Colorize() - case pe: ps := e.PS if ps == nil || ps.PE == nil { return colorizer.Span(colorizer.Gray, "N/A") } return colorizer.Span(colorizer.Magenta, ps.PE.String()) - case cstack: return fmt.Sprintf("\n%s", e.Callstack.Colorize()) } diff --git a/pkg/event/formatter_test.go b/pkg/event/formatter_test.go index 044a4c128..657dba714 100644 --- a/pkg/event/formatter_test.go +++ b/pkg/event/formatter_test.go @@ -19,13 +19,14 @@ package event import ( + "github.com/rabbitstack/fibratus/pkg/event/params" htypes "github.com/rabbitstack/fibratus/pkg/handle/types" pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" "github.com/stretchr/testify/assert" - kpars "github.com/rabbitstack/fibratus/pkg/event/params" - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" ) func TestTemplateUnknownField(t *testing.T) { @@ -54,10 +55,10 @@ func TestFormat(t *testing.T) { template := "{{ .Seq }} {{.CPU}} - ({{.Type}}) -- pid: {{ .Params.Pid }} ({{.Params}}) {{ .Meta }}" f, err := NewFormatter(template) require.NoError(t, err) - params := Params{ - kpars.ProcessID: {Name: kpars.ProcessID, Type: kpars.PID, Value: uint32(876)}, + pars := Params{ + params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(876)}, } - s := f.Format(&Event{CPU: uint8(4), Name: "CreateProcess", Seq: uint64(1999), Params: params, Metadata: map[MetadataKey]any{"key1": "value1"}}) + s := f.Format(&Event{CPU: uint8(4), Type: CreateProcess, Seq: uint64(1999), Params: pars, Metadata: map[MetadataKey]any{"key1": "value1"}}) assert.Equal(t, "1999 4 - (CreateProcess) -- pid: 876 (pid➜ 876) key1: value1", string(s)) } @@ -65,14 +66,13 @@ func TestFormatPS(t *testing.T) { template := "{{ .Seq }} {{ .Process }} ({{ .Cwd }}) {{ .Ppid }} ({{ .Sid }})" f, err := NewFormatter(template) require.NoError(t, err) - params := Params{ - kpars.ProcessID: {Name: kpars.ProcessID, Type: kpars.PID, Value: uint32(876)}, + pars := Params{ + params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(876)}, } s := f.Format(&Event{ CPU: uint8(4), - Name: "CreateProcess", Seq: uint64(1999), - Params: params, + Params: pars, PS: &pstypes.PS{ Name: "cmd.exe", Cwd: "C:/Windows/System32", @@ -92,47 +92,86 @@ func TestNormalizeTemplate(t *testing.T) { } func TestIsTemplateBalanced(t *testing.T) { - ok, pos := isTemplateBalanced("{{ .Seq }} {{.CPU}}") - require.True(t, ok) - assert.Equal(t, -1, pos) - - ok, pos = isTemplateBalanced("{{ .Seq }} ({{.CPU}}) [] {{.Type}}") - require.True(t, ok) - assert.Equal(t, -1, pos) - - ok, pos = isTemplateBalanced("{{ .Seq }} {.CPU}} {{.Type}}") - require.False(t, ok) - assert.Equal(t, 2, pos) - - ok, pos = isTemplateBalanced("{.Seq}") - require.False(t, ok) - assert.Equal(t, 1, pos) - - ok, pos = isTemplateBalanced("{{ .Seq }} .CPU }}") - require.False(t, ok) - assert.Equal(t, 2, pos) - - ok, pos = isTemplateBalanced("{{{ .Seq }} {{.CPU}} {{} {{ .Params }} { .Params.pid}}") - require.False(t, ok) - assert.Equal(t, 1, pos) - - ok, pos = isTemplateBalanced("{{ .Seq }} {{.CPU}} {{} {{ .Params }} { .Params.pid}}") - require.False(t, ok) - assert.Equal(t, 3, pos) - - ok, pos = isTemplateBalanced("({{ .Seq }}) {{.CPU}} {{}} {{ .Params }} { .Params.pid}}") - require.False(t, ok) - assert.Equal(t, 5, pos) - - ok, pos = isTemplateBalanced("{{ .Seq } {{.CPU}} {.Type}}") - require.False(t, ok) - assert.Equal(t, 1, pos) + tests := []struct { + name string + input string + wantOK bool + wantPos int + }{ + { + name: "balanced templates", + input: "{{ .Seq }} {{.CPU}}", + wantOK: true, + wantPos: -1, + }, + { + name: "balanced templates with other delimiters", + input: "{{ .Seq }} ({{.CPU}}) [] {{.Type}}", + wantOK: true, + wantPos: -1, + }, + { + name: "single opening brace", + input: "{{ .Seq }} {.CPU}} {{.Type}}", + wantOK: false, + wantPos: 2, + }, + { + name: "single template", + input: "{.Seq}", + wantOK: false, + wantPos: 1, + }, + { + name: "unmatched closing braces", + input: "{{ .Seq }} .CPU }}", + wantOK: false, + wantPos: 2, + }, + { + name: "triple opening brace", + input: "{{{ .Seq }} {{.CPU}} {{} {{ .Params }} { .Params.pid}}", + wantOK: false, + wantPos: 1, + }, + { + name: "empty template", + input: "{{ .Seq }} {{.CPU}} {{} {{ .Params }} { .Params.pid}}", + wantOK: false, + wantPos: 3, + }, + { + name: "empty template with other delimiters", + input: "({{ .Seq }}) {{.CPU}} {{}} {{ .Params }} { .Params.pid}}", + wantOK: false, + wantPos: 5, + }, + { + name: "malformed closing delimiter", + input: "{{ .Seq } {{.CPU}} {.Type}}", + wantOK: false, + wantPos: 1, + }, + { + name: "unmatched closing brace", + input: "{{ .Seq }} {{.CPU}} {.Type}}", + wantOK: false, + wantPos: 3, + }, + { + name: "malformed template in complex input", + input: "{{ .Seq }} {{.CPU}} - ({{.Type}}) -- pid: {{]} {{ .Params.Pid }} ({{.Params}}) {{ .Meta }}", + wantOK: false, + wantPos: 4, + }, + } - ok, pos = isTemplateBalanced("{{ .Seq }} {{.CPU}} {.Type}}") - require.False(t, ok) - assert.Equal(t, 3, pos) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ok, pos := isTemplateBalanced(tt.input) - ok, pos = isTemplateBalanced("{{ .Seq }} {{.CPU}} - ({{.Type}}) -- pid: {{]} {{ .Params.Pid }} ({{.Params}}) {{ .Meta }}") - require.False(t, ok) - assert.Equal(t, 4, pos) + require.Equal(t, tt.wantOK, ok) + assert.Equal(t, tt.wantPos, pos) + }) + } } diff --git a/pkg/event/formatter_windows.go b/pkg/event/formatter_windows.go index c441ae650..3c55f3f54 100644 --- a/pkg/event/formatter_windows.go +++ b/pkg/event/formatter_windows.go @@ -33,9 +33,9 @@ func (f *Formatter) Format(evt *Event) []byte { tid: strconv.FormatUint(uint64(evt.Tid), 10), seq: strconv.FormatUint(evt.Seq, 10), cpu: strconv.FormatUint(uint64(evt.CPU), 10), - typ: evt.Name, - cat: evt.Category, - desc: evt.Description, + typ: evt.Name(), + cat: evt.Category(), + desc: evt.Description(), host: evt.Host, meta: evt.Metadata.String(), parameters: evt.Params.String(), diff --git a/pkg/event/marshaller_test.go b/pkg/event/marshaller_test.go index 37be2a3e6..2906b7f62 100644 --- a/pkg/event/marshaller_test.go +++ b/pkg/event/marshaller_test.go @@ -49,16 +49,13 @@ func TestMarshaller(t *testing.T) { require.NoError(t, err) evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: now, - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: now, + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -77,7 +74,7 @@ func TestMarshaller(t *testing.T) { b := evt.MarshalRaw() require.NotEmpty(t, b) - clone, err := NewFromCapture(b, capver.EvtSecV2) + clone, err := NewFromCapture(b, capver.EvtSecV3) require.NoError(t, err) assert.Equal(t, uint64(2), clone.Seq) @@ -85,9 +82,9 @@ func TestMarshaller(t *testing.T) { assert.Equal(t, uint32(2484), clone.Tid) assert.Equal(t, CreateFile, clone.Type) assert.Equal(t, uint8(1), clone.CPU) - assert.Equal(t, "CreateFile", clone.Name) - assert.Equal(t, File, clone.Category) - assert.Equal(t, "Creates or opens a new file, directory, I/O device, pipe, console", clone.Description) + assert.Equal(t, "CreateFile", clone.Name()) + assert.Equal(t, File, clone.Category()) + assert.Equal(t, "Creates or opens a new file, directory, I/O device, pipe, console", clone.Description()) assert.Equal(t, "archrabbit", clone.Host) assert.Equal(t, now, clone.Timestamp) @@ -108,16 +105,13 @@ func TestMarshaller(t *testing.T) { func TestEventMarshalJSON(t *testing.T) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -225,16 +219,13 @@ func TestUnmarshalHugeHandles(t *testing.T) { require.NoError(t, err) evt := &Event{ - Type: CreateProcess, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateProcess", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates a new process", + Type: CreateProcess, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -278,7 +269,7 @@ func TestUnmarshalHugeHandles(t *testing.T) { } s := evt.MarshalRaw() - clone, err := NewFromCapture(s, capver.EvtSecV2) + clone, err := NewFromCapture(s, capver.EvtSecV3) require.NoError(t, err) require.NotNil(t, clone) } @@ -287,16 +278,13 @@ func TestEventMarshalJSONMultiple(t *testing.T) { for i := 0; i < 10; i++ { seq := uint64(i + 1) evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: seq, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: seq, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -367,16 +355,13 @@ func TestEventMarshalJSONMultiple(t *testing.T) { func BenchmarkEventMarshalJSON(b *testing.B) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -451,16 +436,13 @@ func BenchmarkEventMarshalJSON(b *testing.B) { func BenchmarkEventMarshalJSONStdlib(b *testing.B) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -537,16 +519,13 @@ func BenchmarkEventMarshalJSONStdlib(b *testing.B) { func BenchmarkMarshal(b *testing.B) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -565,16 +544,13 @@ func BenchmarkMarshal(b *testing.B) { func BenchmarkUnmarshal(b *testing.B) { evt := &Event{ - Type: CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -586,11 +562,11 @@ func BenchmarkUnmarshal(b *testing.B) { buf := evt.MarshalRaw() b.ReportAllocs() for i := 0; i < b.N; i++ { - ke, err := NewFromCapture(buf, capver.EvtSecV2) + evt, err := NewFromCapture(buf, capver.EvtSecV3) if err != nil { b.Fatal(err) } - if ke.Name == "" { + if evt.Name() == "" { b.Fatal("invalid unmarshal byte slice") } } diff --git a/pkg/event/marshaller_windows.go b/pkg/event/marshaller_windows.go index 5ded48295..1473e454b 100644 --- a/pkg/event/marshaller_windows.go +++ b/pkg/event/marshaller_windows.go @@ -64,21 +64,11 @@ func (e *Event) MarshalRaw() []byte { b = append(b, bytes.WriteUint32(e.Tid)...) // write type and CPU - b = append(b, e.Type[:]...) + b = append(b, bytes.WriteUint16(uint16(e.Type))...) b = append(b, e.CPU) // for the string fields we have to write the length prior to // the string buffer itself, so we can decode the string correctly - // - // write event name - b = append(b, bytes.WriteUint16(uint16(len(e.Name)))...) - b = append(b, e.Name...) - // write category - b = append(b, bytes.WriteUint16(uint16(len(e.Category)))...) - b = append(b, e.Category...) - // write description - b = append(b, bytes.WriteUint16(uint16(len(e.Description)))...) - b = append(b, e.Description...) // write host name b = append(b, bytes.WriteUint16(uint16(len(e.Host)))...) b = append(b, e.Host...) @@ -201,8 +191,6 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { e.PID = bytes.ReadUint32(b[8:]) e.Tid = bytes.ReadUint32(b[12:]) - // read type and CPU - var typ Type // set start index depending // on event section version var idx uint32 @@ -211,41 +199,25 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { idx = 33 case capver.EvtSecV2: idx = 34 + case capver.EvtSecV3: + idx = 18 } - copy(typ[:], b[16:idx]) - e.Type = typ + + e.Type = Type(bytes.ReadUint16(b[16:idx])) e.CPU = b[idx : idx+1][0] idx++ // increment index var offset uint32 - // read event name + // read host name l := bytes.ReadUint16(b[inc(idx, 0):]) buf := b[inc(idx, 2):] offset = uint32(l) - e.Name = string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l]) - - // read category - l = bytes.ReadUint16(b[inc(idx, 2)+offset:]) - buf = b[inc(idx, 4)+offset:] - offset += uint32(l) - e.Category = Category(string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l])) - - // read description - l = bytes.ReadUint16(b[inc(idx, 4)+offset:]) - buf = b[inc(idx, 6)+offset:] - offset += uint32(l) - e.Description = string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l]) - - // read host name - l = bytes.ReadUint16(b[inc(idx, 6)+offset:]) - buf = b[inc(idx, 8)+offset:] - offset += uint32(l) e.Host = string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l]) // read timestamp - l = bytes.ReadUint16(b[inc(idx, 8)+offset:]) - buf = b[inc(idx, 10)+offset:] + l = bytes.ReadUint16(b[inc(idx, 2)+offset:]) + buf = b[inc(idx, 4)+offset:] offset += uint32(l) if len(buf) > 0 { var err error @@ -256,87 +228,87 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { } // read parameters - nparams := bytes.ReadUint16(b[inc(idx, 10)+offset:]) + nparams := bytes.ReadUint16(b[inc(idx, 4)+offset:]) // accumulates the offset of all parameter name and value lengths var poffset uint32 for i := 0; i < int(nparams); i++ { - // read Param type - typ := bytes.ReadUint16(b[inc(idx, 12)+offset+poffset:]) - // read Param name - kparamNameLength := uint32(bytes.ReadUint16(b[inc(idx, 14)+offset+poffset:])) - buf = b[inc(idx, 16)+offset+poffset:] - kparamName := string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:kparamNameLength:kparamNameLength]) + // read param type + typ := bytes.ReadUint16(b[inc(idx, 6)+offset+poffset:]) + // read param name + paramNameLength := uint32(bytes.ReadUint16(b[inc(idx, 8)+offset+poffset:])) + buf = b[inc(idx, 10)+offset+poffset:] + kparamName := string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:paramNameLength:paramNameLength]) - pi := inc(idx, 16) // parameter index + pi := inc(idx, 10) // parameter index var val params.Value switch params.Type(typ) { case params.AnsiString, params.UnicodeString, params.Path: // read string parameter - l := bytes.ReadUint16(b[pi+offset+kparamNameLength+poffset:]) - buf = b[inc(idx, 18)+offset+kparamNameLength+poffset:] + l := bytes.ReadUint16(b[pi+offset+paramNameLength+poffset:]) + buf = b[inc(idx, 12)+offset+paramNameLength+poffset:] if len(buf) > 0 { val = string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l]) } // increment parameter offset by string by type length + name length bytes + length of // the string parameter + string parameter size - poffset += kparamNameLength + 6 + uint32(l) + poffset += paramNameLength + 6 + uint32(l) case params.Uint64, params.Address, params.Flags64: - val = bytes.ReadUint64(b[pi+offset+kparamNameLength+poffset:]) + val = bytes.ReadUint64(b[pi+offset+paramNameLength+poffset:]) // increment parameter offset by type length + name length sizes + size of uint64 - poffset += kparamNameLength + 4 + 8 + poffset += paramNameLength + 4 + 8 case params.Int64: - val = int64(bytes.ReadUint64(b[pi+offset+kparamNameLength+poffset:])) + val = int64(bytes.ReadUint64(b[pi+offset+paramNameLength+poffset:])) // increment parameter offset by type length + name length sizes + size of int64 - poffset += kparamNameLength + 4 + 8 + poffset += paramNameLength + 4 + 8 case params.Double: - val = float64(bytes.ReadUint64(b[pi+offset+kparamNameLength+poffset:])) - poffset += kparamNameLength + 4 + 8 + val = float64(bytes.ReadUint64(b[pi+offset+paramNameLength+poffset:])) + poffset += paramNameLength + 4 + 8 case params.Float: - val = float32(bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:])) - poffset += kparamNameLength + 4 + 4 + val = float32(bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:])) + poffset += paramNameLength + 4 + 4 case params.IPv4: - val = ip.ToIPv4(bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:])) + val = ip.ToIPv4(bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:])) // // increment by IPv4 length - poffset += kparamNameLength + 4 + 4 + poffset += paramNameLength + 4 + 4 case params.IPv6: - val = ip.ToIPv6(b[pi+offset+kparamNameLength+poffset : pi+offset+kparamNameLength+poffset+16]) + val = ip.ToIPv6(b[pi+offset+paramNameLength+poffset : pi+offset+paramNameLength+poffset+16]) // increment by IPv6 length - poffset += kparamNameLength + 4 + 16 + poffset += paramNameLength + 4 + 16 case params.PID, params.TID: - val = bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:]) - poffset += kparamNameLength + 4 + 4 + val = bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:]) + poffset += paramNameLength + 4 + 4 case params.Int32: - val = int32(bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:])) - poffset += kparamNameLength + 4 + 4 + val = int32(bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:])) + poffset += paramNameLength + 4 + 4 case params.Uint32, params.Enum, params.Flags, params.Status: - val = bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:]) - poffset += kparamNameLength + 4 + 4 + val = bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:]) + poffset += paramNameLength + 4 + 4 case params.Uint16, params.Port: - val = bytes.ReadUint16(b[pi+offset+kparamNameLength+poffset:]) - poffset += kparamNameLength + 4 + 2 + val = bytes.ReadUint16(b[pi+offset+paramNameLength+poffset:]) + poffset += paramNameLength + 4 + 2 case params.Int16: - val = int16(bytes.ReadUint16(b[pi+offset+kparamNameLength+poffset:])) - poffset += kparamNameLength + 4 + 2 + val = int16(bytes.ReadUint16(b[pi+offset+paramNameLength+poffset:])) + poffset += paramNameLength + 4 + 2 case params.Uint8: - val = b[pi+offset+kparamNameLength+poffset : pi+offset+kparamNameLength+poffset+1][0] - poffset += kparamNameLength + 4 + 1 + val = b[pi+offset+paramNameLength+poffset : pi+offset+paramNameLength+poffset+1][0] + poffset += paramNameLength + 4 + 1 case params.Int8: - val = int8(b[pi+offset+kparamNameLength+poffset : pi+offset+kparamNameLength+poffset+1][0]) - poffset += kparamNameLength + 4 + 1 + val = int8(b[pi+offset+paramNameLength+poffset : pi+offset+paramNameLength+poffset+1][0]) + poffset += paramNameLength + 4 + 1 case params.Bool: - v := b[pi+offset+kparamNameLength+poffset : pi+offset+kparamNameLength+poffset+1][0] + v := b[pi+offset+paramNameLength+poffset : pi+offset+paramNameLength+poffset+1][0] if v == 1 { val = true } else { val = false } - poffset += kparamNameLength + 4 + 1 + poffset += paramNameLength + 4 + 1 case params.Time: // read ts length - l := bytes.ReadUint16(b[pi+offset+kparamNameLength+poffset:]) - buf = b[inc(idx, 18)+offset+kparamNameLength+poffset:] + l := bytes.ReadUint16(b[pi+offset+paramNameLength+poffset:]) + buf = b[inc(idx, 12)+offset+paramNameLength+poffset:] if len(buf) > 0 { var err error val, err = time.Parse(time.RFC3339Nano, string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:l:l])) @@ -344,19 +316,19 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { unmarshalTimestampErrors.Add(1) } } - poffset += kparamNameLength + 6 + uint32(l) + poffset += paramNameLength + 6 + uint32(l) case params.Slice: // read slice element type - typ := b[pi+offset+kparamNameLength+poffset] + typ := b[pi+offset+paramNameLength+poffset] // read slice size - l := bytes.ReadUint16(b[inc(idx, 17)+offset+kparamNameLength+poffset:]) + l := bytes.ReadUint16(b[inc(idx, 11)+offset+paramNameLength+poffset:]) var off uint32 switch typ { case 's': s := make([]string, l) for i := 0; i < int(l); i++ { - size := bytes.ReadUint16(b[inc(idx, 19)+offset+kparamNameLength+poffset+off:]) - buf := b[inc(idx, 22)+offset+kparamNameLength+poffset+off:] + size := bytes.ReadUint16(b[inc(idx, 13)+offset+paramNameLength+poffset+off:]) + buf := b[inc(idx, 15)+offset+paramNameLength+poffset+off:] s[i] = string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:size:size]) off += 2 + uint32(size) } @@ -364,19 +336,19 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { case '8': v := make([]uint64, l) for i := 0; i < int(l); i++ { - bytes.ReadUint64(b[inc(idx, 22)+offset+kparamNameLength+poffset+off:]) + bytes.ReadUint64(b[inc(idx, 16)+offset+paramNameLength+poffset+off:]) off += 8 } val = v } - poffset += kparamNameLength + 4 + 1 + 2 + off + poffset += paramNameLength + 4 + 1 + 2 + off case params.Binary, params.SID, params.WbemSID: - l := bytes.ReadUint32(b[pi+offset+kparamNameLength+poffset:]) - buf = b[inc(idx, 18)+offset+kparamNameLength+poffset:] + l := bytes.ReadUint32(b[pi+offset+paramNameLength+poffset:]) + buf = b[inc(idx, 12)+offset+paramNameLength+poffset:] if len(buf) > 0 { val = buf[:l] } - poffset += kparamNameLength + 8 + l + poffset += paramNameLength + 8 + l } if val != nil { @@ -387,16 +359,16 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { offset += poffset // read metadata tags - ntags := bytes.ReadUint16(b[inc(idx, 12)+offset:]) + ntags := bytes.ReadUint16(b[inc(idx, 6)+offset:]) var moffset uint32 for i := 0; i < int(ntags); i++ { // read key - klen := uint32(bytes.ReadUint16(b[inc(idx, 14)+offset+moffset:])) - buf = b[inc(idx, 16)+offset+moffset:] + klen := uint32(bytes.ReadUint16(b[inc(idx, 8)+offset+moffset:])) + buf = b[inc(idx, 10)+offset+moffset:] key := string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:klen:klen]) // read value - vlen := uint32(bytes.ReadUint16(b[inc(idx, 16)+offset+klen+moffset:])) - buf = b[inc(idx, 18)+offset+klen+moffset:] + vlen := uint32(bytes.ReadUint16(b[inc(idx, 10)+offset+klen+moffset:])) + buf = b[inc(idx, 12)+offset+klen+moffset:] value := string((*[1<<30 - 1]byte)(unsafe.Pointer(&buf[0]))[:vlen:vlen]) // increment the offset by the length of the key + length value + size of uint16 * 2 // that corresponds to bytes storing the lengths of keys/values @@ -409,9 +381,9 @@ func (e *Event) UnmarshalRaw(b []byte, ver capver.Version) error { offset += moffset // read process state - sec := section.Read(b[inc(idx, 14)+offset:]) + sec := section.Read(b[inc(idx, 8)+offset:]) if sec.Size() != 0 { - ps, err := ptypes.NewFromCapture(b[inc(idx, 24)+offset:], sec) + ps, err := ptypes.NewFromCapture(b[inc(idx, 18)+offset:], sec) if err != nil { return err } @@ -441,9 +413,9 @@ func (e *Event) MarshalJSON() []byte { js.writeObjectField("tid").writeUint32(e.Tid).writeMore() js.writeObjectField("cpu").writeUint8(e.CPU).writeMore() - js.writeObjectField("name").writeString(e.Name).writeMore() - js.writeObjectField("category").writeString(string(e.Category)).writeMore() - js.writeObjectField("description").writeString(e.Description).writeMore() + js.writeObjectField("name").writeString(e.Name()).writeMore() + js.writeObjectField("category").writeString(e.Category().String()).writeMore() + js.writeObjectField("description").writeString(e.Description()).writeMore() js.writeObjectField("host").writeString(e.Host).writeMore() timestamp := make([]byte, 0) diff --git a/pkg/event/metainfo_windows.go b/pkg/event/metainfo_windows.go index 9e7a7b941..4817fbcd2 100644 --- a/pkg/event/metainfo_windows.go +++ b/pkg/event/metainfo_windows.go @@ -23,229 +23,147 @@ import ( "slices" ) +// Flags represents the event flags +type Flags uint8 + +const ( + // OnlyState indicates the event produces internal state + // and is never published to the event stream. + OnlyState Flags = 1 << 1 + + // StateSnapshot indicates that the event is published once + // at startup time and populates the internal state. + StateSnapshot Flags = 1 << 2 + + // WaitStack indicates that the event awaits the stack walk + // event that carries call stack return addresses. + WaitStack Flags = 1 << 3 +) + // Info describes the event meta info such as human-readable name, category and description. type Info struct { // Name is the human-readable representation of the event (e.g. CreateProcess, DeleteFile). Name string - // Category designates the category to which event pertains. (e.g. process, net) + // Category designates the category to which event pertains. (e.g. process, network) Category Category + // Subcategory designates the event subcategory if any. For example, the network category + // can be further subcategorized, such as DNS subcategory. + Subcategory Subcategory + // Source describes the event source origin for this event. For example, if it was captured + // from the NT Kernel Logger or a different event source. + Source Source // Description is the short explanation that describes the purpose of the event. Description string + // Flags describes additional properties of the event. + Flags Flags } -var events = map[Type]Info{ - CreateProcess: {"CreateProcess", Process, "Creates a new process and its primary thread"}, - TerminateProcess: {"TerminateProcess", Process, "Terminates the process and all of its threads"}, - OpenProcess: {"OpenProcess", Process, "Opens the process handle"}, - CreateThread: {"CreateThread", Thread, "Creates a thread to execute within the virtual address space of the calling process"}, - TerminateThread: {"TerminateThread", Thread, "Terminates a thread within the process"}, - OpenThread: {"OpenThread", Thread, "Opens the thread handle"}, - SetThreadContext: {"SetThreadContext", Thread, "Sets the thread context"}, - ReadFile: {"ReadFile", File, "Reads data from the file or I/O device"}, - WriteFile: {"WriteFile", File, "Writes data to the file or I/O device"}, - CreateFile: {"CreateFile", File, "Creates or opens a file or I/O device"}, - CloseFile: {"CloseFile", File, "Closes the file handle"}, - DeleteFile: {"DeleteFile", File, "Removes the file from the file system"}, - RenameFile: {"RenameFile", File, "Changes the file name"}, - SetFileInformation: {"SetFileInformation", File, "Sets the file meta information"}, - EnumDirectory: {"EnumDirectory", File, "Enumerates a directory or dispatches a directory change notification to registered listeners"}, - RegCreateKey: {"RegCreateKey", Registry, "Creates a registry key or opens it if the key already exists"}, - RegOpenKey: {"RegOpenKey", Registry, "Opens the registry key"}, - RegCloseKey: {"RegCloseKey", Registry, "Closes the registry key"}, - RegSetValue: {"RegSetValue", Registry, "Sets the data for the value of a registry key"}, - RegQueryValue: {"RegQueryValue", Registry, "Reads the data for the value of a registry key"}, - RegQueryKey: {"RegQueryKey", Registry, "Enumerates subkeys of the parent key"}, - RegDeleteKey: {"RegDeleteKey", Registry, "Removes the registry key"}, - RegDeleteValue: {"RegDeleteValue", Registry, "Removes the registry value"}, - AcceptTCPv4: {"Accept", Net, "Accepts the connection request from the socket queue"}, - AcceptTCPv6: {"Accept", Net, "Accepts the connection request from the socket queue"}, - SendTCPv4: {"Send", Net, "Sends data over the wire"}, - SendTCPv6: {"Send", Net, "Sends data over the wire"}, - SendUDPv4: {"Send", Net, "Sends data over the wire"}, - SendUDPv6: {"Send", Net, "Sends data over the wire"}, - RecvTCPv4: {"Recv", Net, "Receives data from the socket"}, - RecvTCPv6: {"Recv", Net, "Receives data from the socket"}, - RecvUDPv4: {"Recv", Net, "Receives data from the socket"}, - RecvUDPv6: {"Recv", Net, "Receives data from the socket"}, - ConnectTCPv4: {"Connect", Net, "Connects establishes a connection to the socket"}, - ConnectTCPv6: {"Connect", Net, "Connects establishes a connection to the socket"}, - DisconnectTCPv4: {"Disconnect", Net, "Terminates data reception on the socket"}, - DisconnectTCPv6: {"Disconnect", Net, "Terminates data reception on the socket"}, - ReconnectTCPv4: {"Reconnect", Net, "Reconnects to the socket"}, - ReconnectTCPv6: {"Reconnect", Net, "Reconnects to the socket"}, - RetransmitTCPv4: {"Retransmit", Net, "Retransmits unacknowledged TCP segments"}, - RetransmitTCPv6: {"Retransmit", Net, "Retransmits unacknowledged TCP segments"}, - LoadModule: {"LoadModule", Module, "Loads the module into the address space of the calling process"}, - UnloadModule: {"UnloadModule", Module, "Unloads the module from the address space of the calling process"}, - VirtualAlloc: {"VirtualAlloc", Mem, "Reserves, commits, or changes the state of a region of memory within the process virtual address space"}, - VirtualFree: {"VirtualFree", Mem, "Releases or decommits a region of memory within the process virtual address space"}, - MapViewFile: {"MapViewFile", File, "Maps a view of a file mapping into the address space of a calling process"}, - UnmapViewFile: {"UnmapViewFile", File, "Unmaps a mapped view of a file from the calling process's address space"}, - QueryDNS: {"QueryDns", Net, "Sends a DNS query to the name server"}, - ReplyDNS: {"ReplyDNS", Net, "Receives the response from the DNS server"}, - CreateSymbolicLinkObject: {"CreateSymbolicLinkObject", Object, "Creates the symbolic link within the object manager directory"}, -} +var table = [MaxEvent]Info{ + CreateProcess: {Name: "CreateProcess", Category: Process, Source: SystemLogger, Description: "Creates a new process and its primary thread", Flags: WaitStack}, + TerminateProcess: {Name: "TerminateProcess", Category: Process, Source: SystemLogger, Description: "Terminates the process and all of its threads"}, + OpenProcess: {Name: "OpenProcess", Category: Process, Source: SecurityTelemetryLogger, Description: "Opens the process handle"}, + ProcessRundown: {Name: "ProcessRundown", Category: Process, Source: SecurityTelemetryLogger, Description: "Builds the snapshot state of running processes in the system.", Flags: OnlyState | StateSnapshot}, + CreateProcessInternal: {Name: "CreateProcessInternal", Category: Process, Source: SystemLogger, Description: "Only purpose of this event is to enrich the process state with some extra attributes. Never published to the event stream", Flags: OnlyState}, + ProcessRundownInternal: {Name: "ProcessRundownInternal", Category: Process, Source: SecurityTelemetryLogger, Description: "Opens the process handle", Flags: OnlyState | StateSnapshot}, -var types = map[string]Type{ - "CreateProcess": CreateProcess, - "TerminateProcess": TerminateProcess, - "OpenProcess": OpenProcess, - "CreateThread": CreateThread, - "TerminateThread": TerminateThread, - "OpenThread": OpenThread, - "SetThreadContext": SetThreadContext, - "LoadModule": LoadModule, - "UnloadModule": UnloadModule, - "CreateFile": CreateFile, - "CloseFile": CloseFile, - "ReadFile": ReadFile, - "WriteFile": WriteFile, - "SetFileInformation": SetFileInformation, - "DeleteFile": DeleteFile, - "RenameFile": RenameFile, - "EnumDirectory": EnumDirectory, - "RegCreateKey": RegCreateKey, - "RegOpenKey": RegOpenKey, - "RegSetValue": RegSetValue, - "RegQueryValue": RegQueryValue, - "RegQueryKey": RegQueryKey, - "RegDeleteKey": RegDeleteKey, - "RegDeleteValue": RegDeleteValue, - "RegCloseKey": RegCloseKey, - "AcceptTCP4": AcceptTCPv4, - "AcceptTCP6": AcceptTCPv6, - "SendTCP4": SendTCPv4, - "SendTCP6": SendTCPv6, - "SendUDP4": SendUDPv4, - "SendUDP6": SendUDPv6, - "RecvTCP4": RecvTCPv4, - "RecvTCP6": RecvTCPv6, - "RecvUDP4": RecvUDPv4, - "RecvUDP6": RecvUDPv6, - "ConnectTCP4": ConnectTCPv4, - "ConnectTCP6": ConnectTCPv6, - "ReconnectTCP4": ReconnectTCPv4, - "ReconnectTCP6": ReconnectTCPv6, - "DisconnectTCP4": DisconnectTCPv4, - "DisconnectTCP6": DisconnectTCPv6, - "RetransmitTCP4": RetransmitTCPv4, - "RetransmitTCP6": RetransmitTCPv6, - "VirtualAlloc": VirtualAlloc, - "VirtualFree": VirtualFree, - "MapViewFile": MapViewFile, - "UnmapViewFile": UnmapViewFile, - "QueryDns": QueryDNS, - "ReplyDns": ReplyDNS, - "CreateSymbolicLinkObject": CreateSymbolicLinkObject, -} + CreateThread: {Name: "CreateThread", Category: Thread, Source: SystemLogger, Description: "Creates a thread to execute within the virtual address space of the calling process", Flags: WaitStack}, + TerminateThread: {Name: "TerminateThread", Category: Thread, Source: SystemLogger, Description: "Terminates a thread within the process", Flags: WaitStack}, + OpenThread: {Name: "OpenThread", Category: Thread, Source: SecurityTelemetryLogger, Description: "Opens the thread handle"}, + SetThreadContext: {Name: "SetThreadContext", Category: Thread, Source: SecurityTelemetryLogger, Description: "Sets the thread context"}, + StackWalk: {Name: "StackWalk", Category: Thread, Source: SystemLogger, Description: "Delivers call stack return addresses. Never published to the event stream", Flags: OnlyState}, + ThreadRundown: {Name: "ThreadRundown", Category: Thread, Source: SystemLogger, Description: "Builds the snapshot state of running threads in the system", Flags: OnlyState | StateSnapshot}, -// All returns all event types. -func All() []Type { - s := make([]Type, 0, len(types)) - for _, typ := range types { - s = append(s, typ) - } - return s -} + UnloadModule: {Name: "UnloadModule", Category: Module, Source: SystemLogger, Description: "Unloads the module from the address space of the calling process"}, + LoadModule: {Name: "LoadModule", Category: Module, Source: SystemLogger, Description: "Loads the module into the address space of the calling process", Flags: WaitStack}, + ModuleRundown: {Name: "ModuleRundown", Category: Module, Source: SystemLogger, Description: "Builds the snapshot of loaded modules in the system", Flags: OnlyState | StateSnapshot}, + LoadModuleInternal: {Name: "LoadModuleInternal", Category: Module, Source: SecurityTelemetryLogger, Description: "Only purpose is to populate the module state. Never published to the event stream", Flags: OnlyState}, -// AllWithState returns all event types + -// event types used for state management. -func AllWithState() []Type { - s := All() - - s = append(s, ProcessRundown) - s = append(s, ThreadRundown) - s = append(s, ModuleRundown) - s = append(s, FileRundown) - s = append(s, RegKCBRundown) - s = append(s, RegCreateKCB) - s = append(s, RegDeleteKCB) - s = append(s, FileOpEnd) - s = append(s, ReleaseFile) - s = append(s, MapFileRundown) - s = append(s, StackWalk) - s = append(s, CreateProcessInternal) - s = append(s, ProcessRundownInternal) - s = append(s, LoadModuleInternal) - s = append(s, RegSetValueInternal) - - return s -} + RegCreateKey: {Name: "RegCreateKey", Category: Registry, Source: SystemLogger, Description: "Creates a registry key or opens it if the key already exists", Flags: WaitStack}, + RegOpenKey: {Name: "RegOpenKey", Category: Registry, Source: SystemLogger, Description: "Opens the registry key"}, + RegDeleteKey: {Name: "RegDeleteKey", Category: Registry, Source: SystemLogger, Description: "Removes the registry key", Flags: WaitStack}, + RegQueryKey: {Name: "RegQueryKey", Category: Registry, Source: SystemLogger, Description: "Enumerates subkeys of the parent key"}, + RegSetValue: {Name: "RegSetValue", Category: Registry, Source: SystemLogger, Description: "Sets the data for the value of a registry key", Flags: WaitStack}, + RegSetValueInternal: {Name: "RegSetValueInternal", Category: Registry, Source: SecurityTelemetryLogger, Description: "Closes the registry key", Flags: OnlyState}, + RegDeleteValue: {Name: "RegDeleteValue", Category: Registry, Source: SystemLogger, Description: "Removes the registry value", Flags: WaitStack}, + RegQueryValue: {Name: "RegQueryValue", Category: Registry, Source: SystemLogger, Description: "Reads the data for the value of a registry key"}, + RegCloseKey: {Name: "RegCloseKey", Category: Registry, Source: SystemLogger, Description: "Closes the registry key. Never published to the event stream", Flags: OnlyState}, + RegCreateKCB: {Name: "RegCreateKCB", Category: Registry, Source: SystemLogger, Description: "Create the Key Control Block. Never published to the event stream", Flags: OnlyState}, + RegDeleteKCB: {Name: "RegDeleteKCB", Category: Registry, Source: SystemLogger, Description: "Removes the Key Control Block. Never published to the event stream", Flags: OnlyState}, + RegKCBRundown: {Name: "RegKCBRundown", Category: Registry, Source: SystemLogger, Description: "Builds the snapshot of existing Key Control Block objects", Flags: OnlyState | StateSnapshot}, -// MaxTypeID returns the maximum event type (hook id) value. -func MaxTypeID() uint16 { - types := AllWithState() - ids := make([]uint16, len(types)) - for i, t := range types { - ids[i] = t.HookID() - } - return slices.Max(ids) -} + CreateFile: {Name: "CreateFile", Category: File, Source: SystemLogger, Description: "Creates or opens a new file, directory, I/O device, pipe, console"}, + ReleaseFile: {Name: "ReleaseFile", Category: File, Source: SystemLogger, Description: "Closes the last handle to the file object. Never published to the event stream", Flags: OnlyState}, + CloseFile: {Name: "CloseFile", Category: File, Source: SystemLogger, Description: "Closes the file handle. Never published to the event stream", Flags: OnlyState}, + ReadFile: {Name: "ReadFile", Category: File, Source: SystemLogger, Description: "Reads data from the file or I/O device"}, + WriteFile: {Name: "WriteFile", Category: File, Source: SystemLogger, Description: "Writes data to the file or I/O device"}, + SetFileInformation: {Name: "SetFileInformation", Category: File, Source: SystemLogger, Description: "Sets the file meta information"}, + DeleteFile: {Name: "DeleteFile", Category: File, Source: SystemLogger, Description: "Removes the file from the file system", Flags: WaitStack}, + RenameFile: {Name: "RenameFile", Category: File, Source: SystemLogger, Description: "Changes the file name", Flags: WaitStack}, + EnumDirectory: {Name: "EnumDirectory", Category: File, Source: SystemLogger, Description: "Enumerates a directory or dispatches a directory change notification to registered listeners"}, + FileRundown: {Name: "FileRundown", Category: File, Source: SystemLogger, Description: "Builds the snapshot of existing file objects", Flags: OnlyState | StateSnapshot}, + FileOpEnd: {Name: "FileOpEnd", Category: File, Source: SystemLogger, Description: "Reports the I/O request packet status. Never published to the event stream", Flags: OnlyState}, -// TypeToEventInfo maps the event type to the structure storing detailed information about the event. -func TypeToEventInfo(typ Type) Info { - if info, ok := events[typ]; ok { - return info - } - return Info{Name: "N/A", Category: Unknown} + Accept: {Name: "Accept", Category: Network, Source: SystemLogger, Description: "Accepts the connection request from the socket queue"}, + Send: {Name: "Send", Category: Network, Source: SystemLogger, Description: "Sends data over the wire"}, + Recv: {Name: "Recv", Category: Network, Source: SystemLogger, Description: "Receives data from the socket"}, + Connect: {Name: "Connect", Category: Network, Source: SystemLogger, Description: "Connects establishes a connection to the socket"}, + Disconnect: {Name: "Disconnect", Category: Network, Source: SystemLogger, Description: "Terminates data reception on the socket"}, + Reconnect: {Name: "Reconnect", Category: Network, Source: SystemLogger, Description: "Reconnects to the socket"}, + Retransmit: {Name: "Retransmit", Category: Network, Source: SystemLogger, Description: "Retransmits unacknowledged TCP segments"}, + QueryDNS: {Name: "QueryDns", Category: Network, Subcategory: DNS, Source: SecurityTelemetryLogger, Description: "Sends a DNS query to the name server"}, + ReplyDNS: {Name: "ReplyDNS", Category: Network, Subcategory: DNS, Source: SecurityTelemetryLogger, Description: "Receives the response from the DNS server"}, + + MapViewOfSection: {Name: "MapViewOfSection", Category: Memory, Source: SystemLogger, Description: "Maps a view of a file mapping into the address space of a calling process"}, + UnmapViewOfSection: {Name: "UnmapViewOfSection", Category: Memory, Source: SystemLogger, Description: "Unmaps a mapped view of a file from the calling process's address space"}, + MapViewSectionRundown: {Name: "MapViewSectionRundown", Category: Memory, Source: SystemLogger, Description: "Builds the snapshot of existing memory section views", Flags: OnlyState | StateSnapshot}, + VirtualAlloc: {Name: "VirtualAlloc", Category: Memory, Source: SystemLogger, Description: "Reserves, commits, or changes the state of a region of memory within the process virtual address space", Flags: WaitStack}, + VirtualFree: {Name: "VirtualFree", Category: Memory, Source: SystemLogger, Description: "Releases or decommits a region of memory within the process virtual address space"}, + + CreateSymbolicLinkObject: {Name: "CreateSymbolicLinkObject", Category: Object, Source: SecurityTelemetryLogger, Description: "Creates the symbolic link within the object manager directory"}, } -// NameToType converts a human-readable event name to its internal type representation. -func NameToType(name string) Type { - if typ, ok := types[name]; ok { - return typ +// All returns all event types. +func AllTypes() []Type { + types := make([]Type, 0) + for i := range table { + if Type(i) == Unknown { + continue + } + types = append(types, Type(i)) } - return UnknownType + return types } -// NameToTypes maps the event name to internal type representations, specifically, network -// events that have multiple internal types for a single event name. For example, the Accept -// event name has AcceptTCP4 and AcceptTCP6 types. -func NameToTypes(name string) []Type { - switch name { - case "Accept": - return []Type{AcceptTCPv4, AcceptTCPv6} - case "Send": - return []Type{SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6} - case "Recv": - return []Type{RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6} - case "Connect": - return []Type{ConnectTCPv4, ConnectTCPv6} - case "Reconnect": - return []Type{ReconnectTCPv4, ReconnectTCPv6} - case "Disconnect": - return []Type{DisconnectTCPv4, DisconnectTCPv6} - case "Retransmit": - return []Type{RetransmitTCPv4, RetransmitTCPv6} - default: - return []Type{NameToType(name)} - } +// GetTypeInfo returns metadata about the specified event type. +func GetTypeInfo(typ Type) Info { + return table[typ] } -// GetTypesMeta returns event types metadata. -func GetTypesMeta() []Info { - typs := make([]Info, 0) -outer: - for _, ev := range events { - for _, typ := range typs { - if typ.Name == ev.Name { - continue outer - } - } - typs = append(typs, ev) - } - slices.SortFunc(typs, func(a, b Info) int { +// GetTypesInfo returns event types metadata excluding only-state events. +func GetTypesInfo() []Info { + t := table[:] + t = slices.DeleteFunc(t, func(info Info) bool { + return info.Flags&OnlyState != 0 || info.Name == "" + }) + slices.SortFunc(t, func(a, b Info) int { return cmp.Or(cmp.Compare(a.Category, b.Category), cmp.Compare(a.Name, b.Name)) }) - return typs + return t } -// IsKnown indicates if the event type is known given the event name. -func IsKnown(name string) bool { - for _, evt := range GetTypesMeta() { - if evt.Name == name { - return true - } +// NameToType converts a human-readable event name to its internal type representation. +func ParseType(s string) (Type, bool) { + i := slices.IndexFunc(table[:], func(info Info) bool { + return info.Name == s + }) + if i == -1 { + return Unknown, false } - return false + return Type(i), true +} + +// IsKnown indicates if the event type is known given the event name. +func IsTypeKnown(s string) (exists bool) { + _, exists = ParseType(s) + return } diff --git a/pkg/event/metainfo_windows_test.go b/pkg/event/metainfo_windows_test.go index e23be0d5c..3c5096555 100644 --- a/pkg/event/metainfo_windows_test.go +++ b/pkg/event/metainfo_windows_test.go @@ -19,28 +19,33 @@ package event import ( - "github.com/stretchr/testify/assert" "testing" -) - -func TestEventNameToType(t *testing.T) { - typ := NameToType("CreateProcess") - assert.Equal(t, CreateProcess, typ) + "github.com/stretchr/testify/assert" +) - typ = NameToType("CreateRemoteThread") - assert.Equal(t, UnknownType, typ) +func TestParseType(t *testing.T) { + var tests = []struct { + name string + expectedType Type + }{ + {"CreateProcess", CreateProcess}, + {"CreateRemoteThread", Unknown}, + {"FileOpEnd", FileOpEnd}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + etype, _ := ParseType(tt.name) + assert.Equal(t, tt.expectedType, etype) + }) + } } func TestEventToEventInfo(t *testing.T) { - info := TypeToEventInfo(CreateProcess) + info := GetTypeInfo(CreateProcess) assert.Equal(t, "CreateProcess", info.Name) assert.Equal(t, Process, info.Category) assert.Equal(t, "Creates a new process and its primary thread", info.Description) - - info = TypeToEventInfo(UnknownType) - assert.Equal(t, "N/A", info.Name) - assert.Equal(t, Unknown, info.Category) - assert.Empty(t, info.Description) } diff --git a/pkg/event/param_decoder_windows.go b/pkg/event/param_decoder_windows.go index 813247a70..6f55d224a 100644 --- a/pkg/event/param_decoder_windows.go +++ b/pkg/event/param_decoder_windows.go @@ -25,6 +25,7 @@ import ( "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/fs" + "github.com/rabbitstack/fibratus/pkg/network" "github.com/rabbitstack/fibratus/pkg/sys/etw" "github.com/rabbitstack/fibratus/pkg/util/filetime" "github.com/rabbitstack/fibratus/pkg/util/key" @@ -149,8 +150,8 @@ func (d *ParamDecoder) DecodeRegSetValueInternal(r *etw.EventRecord, e *Event) { // DecodeFile decodes file I/O operations such as file creation, access, // or file metadata manipulation. func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { - switch r.Header.EventDescriptor.Opcode { - case CreateFileID: + switch e.Type { + case CreateFile: // typedef struct _PERFINFO_FILE_CREATE { // LONG_PTR Irp; // ULONG_PTR FileObject; @@ -173,7 +174,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.NTStatus, params.Status, status) e.AppendEnum(params.FileOperation, disposition, fs.FileCreateDispositions) e.AppendParam(params.Callstack, params.Slice, r.ReadEventHeaderFileExtendedDataItemsCallstack()) - case FileOpEndID: + case FileOpEnd: // typedef struct _PERFINFO_FILE_OPERATION_END { // ULONG_PTR Irp; // ULONG_PTR ExtraInformation; @@ -182,7 +183,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileIrpPtr, params.Address, r.ReadUint64(0)) e.AppendParam(params.FileExtraInfo, params.Address, r.ReadUint64(8)) e.AppendParam(params.NTStatus, params.Status, r.ReadUint32(16)) - case MapViewFileID, UnmapViewFileID, MapFileRundownID: + case MapViewOfSection, UnmapViewOfSection, MapViewSectionRundown: e.AppendParam(params.FileViewBase, params.Address, r.ReadUint64(0)) e.AppendParam(params.FileKey, params.Address, r.ReadUint64(8)) e.AppendParam(params.MemProtect, params.Flags, uint32(r.ReadUint64(16)>>32), WithFlags(ViewProtectionFlags)) @@ -190,7 +191,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileViewSize, params.Uint64, r.ReadUint64(24)) e.AppendParam(params.FileOffset, params.Uint64, r.ReadUint64(32)) e.AppendParam(params.ProcessID, params.PID, r.ReadUint32(40)) - case SetFileInformationID, DeleteFileID, RenameFileID: + case SetFileInformation, DeleteFile, RenameFile: // DeleteFile, RenameFile, and SetFileInformation share the same layout // typedef struct _PERFINFO_FILE_INFORMATION { // ULONG_PTR Irp; @@ -206,7 +207,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileExtraInfo, params.Uint64, r.ReadUint64(24)) e.AppendParam(params.ThreadID, params.TID, r.ReadUint32(32)) e.AppendParam(params.FileInfoClass, params.Enum, r.ReadUint32(36), WithEnum(fs.FileInfoClasses)) - case ReleaseFileID, CloseFileID: + case ReleaseFile, CloseFile: // typedef struct _PERFINFO_FILE_SIMPLE_OPERATION { // ULONG_PTR Irp; // ULONG_PTR FileObject; @@ -217,7 +218,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileObject, params.Address, r.ReadUint64(8)) e.AppendParam(params.FileKey, params.Address, r.ReadUint64(16)) e.AppendParam(params.ThreadID, params.TID, r.ReadUint32(24)) - case ReadFileID, WriteFileID: + case ReadFile, WriteFile: // typedef struct _PERFINFO_FILE_READ_WRITE { // ULONGLONG Offset; // ULONG_PTR Irp; @@ -234,7 +235,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileKey, params.Address, r.ReadUint64(24)) e.AppendParam(params.ThreadID, params.TID, r.ReadUint32(32)) e.AppendParam(params.FileIoSize, params.Uint32, r.ReadUint32(34)) - case EnumDirectoryID: + case EnumDirectory: // typedef struct _PERFINFO_FILE_DIRENUM { // ULONG_PTR Irp; // ULONG_PTR FileObject; @@ -253,7 +254,7 @@ func (d *ParamDecoder) DecodeFile(r *etw.EventRecord, e *Event) { e.AppendParam(params.FileInfoClass, params.Enum, r.ReadUint32(32), WithEnum(fs.FileInfoClasses)) // skip FileIndex (uint32) e.AppendParam(params.FilePath, params.UnicodeString, r.ConsumeUTF16String(40)) - case FileRundownID: + case FileRundown: e.AppendParam(params.FileObject, params.Address, r.ReadUint64(0)) e.AppendParam(params.FilePath, params.DOSPath, r.ConsumeUTF16String(8)) } @@ -527,6 +528,12 @@ func (d *ParamDecoder) DecodeNetwork(r *etw.EventRecord, e *Event) { e.AppendParam(params.NetDport, params.Port, r.ReadUint16(16)) e.AppendParam(params.NetSport, params.Port, r.ReadUint16(18)) } + + if r.Header.ProviderID == NetworkTCPEventGUID { + e.AppendEnum(params.NetL4Proto, uint32(network.TCP), network.ProtoNames) + } else { + e.AppendEnum(params.NetL4Proto, uint32(network.UDP), network.ProtoNames) + } } // DecodeDNS decodes DNS query/reply event payloads. diff --git a/pkg/event/param_decoder_windows_test.go b/pkg/event/param_decoder_windows_test.go index 014c6023b..46110e69c 100644 --- a/pkg/event/param_decoder_windows_test.go +++ b/pkg/event/param_decoder_windows_test.go @@ -26,6 +26,7 @@ import ( "github.com/rabbitstack/fibratus/pkg/sys/etw" "github.com/rabbitstack/fibratus/pkg/util/va" "github.com/stretchr/testify/assert" + "golang.org/x/sys/windows" ) func TestDecodeRegistry(t *testing.T) { @@ -141,6 +142,7 @@ func TestDecodeFile(t *testing.T) { var tests = []struct { name string opcode uint8 + event *Event buf []byte assertions func(t *testing.T, e *Event) }{ @@ -158,6 +160,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, "Success", e.GetParamAsString(params.NTStatus)) assert.Contains(t, e.Params, params.Callstack) }, + event: &Event{Params: make(Params), Type: CreateFile}, buf: []byte{ 200, 7, 94, 150, 141, 215, 255, 255, 80, 102, 11, 146, 141, 215, 255, 255, @@ -196,6 +199,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint32(0), e.Params.MustGetUint32(params.NTStatus)) assert.Equal(t, uint64(0x28), e.Params.MustGetUint64(params.FileExtraInfo)) }, + event: &Event{Params: make(Params), Type: FileOpEnd}, buf: []byte{ 248, 240, 61, 151, 141, 215, 255, 255, 40, 0, 0, 0, @@ -204,7 +208,7 @@ func TestDecodeFile(t *testing.T) { }, }, { - name: "MapViewFile", opcode: MapViewFileID, + name: "MapViewOfSection", opcode: MapViewOfSectionID, assertions: func(t *testing.T, e *Event) { assert.Len(t, e.Params, 7) assert.Equal(t, uint64(0xffffb58b75fb7e10), e.Params.MustGetUint64(params.FileKey)) @@ -215,6 +219,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0x191ab210000), e.Params.MustGetUint64(params.FileViewBase)) assert.Equal(t, uint64(4096), e.Params.MustGetUint64(params.FileViewSize)) }, + event: &Event{Params: make(Params), Type: MapViewOfSection}, buf: []byte{ 0, 0, 33, 171, 145, 1, 0, 0, 16, 126, 251, 117, 139, 181, 255, 255, @@ -225,7 +230,7 @@ func TestDecodeFile(t *testing.T) { }, }, { - name: "UnmapViewFile", opcode: UnmapViewFileID, + name: "UnmapViewOfSection", opcode: UnmapViewOfSectionID, assertions: func(t *testing.T, e *Event) { assert.Len(t, e.Params, 7) assert.Equal(t, uint64(0xffffb58bc1f91010), e.Params.MustGetUint64(params.FileKey)) @@ -236,6 +241,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0x1675e410000), e.Params.MustGetUint64(params.FileViewBase)) assert.Equal(t, uint64(921600), e.Params.MustGetUint64(params.FileViewSize)) }, + event: &Event{Params: make(Params), Type: UnmapViewOfSection}, buf: []byte{ 0, 0, 65, 94, 103, 1, 0, 0, 16, 16, 249, 193, 139, 181, 255, 255, @@ -256,6 +262,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0xffffd78d9b6470f8), e.Params.MustGetUint64(params.FileIrpPtr)) assert.Equal(t, uint32(16404), e.Params.MustGetTid()) }, + event: &Event{Params: make(Params), Type: SetFileInformation}, buf: []byte{ 248, 112, 100, 155, 141, 215, 255, 255, 128, 55, 64, 118, 141, 215, 255, 255, @@ -276,6 +283,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0xffffd78d7c5860f8), e.Params.MustGetUint64(params.FileIrpPtr)) assert.Equal(t, uint32(13656), e.Params.MustGetTid()) }, + event: &Event{Params: make(Params), Type: DeleteFile}, buf: []byte{ 248, 96, 88, 124, 141, 215, 255, 255, 128, 125, 108, 155, 141, 215, 255, 255, @@ -294,6 +302,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0xffffd78d7ca0b0f8), e.Params.MustGetUint64(params.FileIrpPtr)) assert.Equal(t, uint32(3096), e.Params.MustGetTid()) }, + event: &Event{Params: make(Params), Type: ReleaseFile}, buf: []byte{ 248, 176, 160, 124, 141, 215, 255, 255, 176, 82, 69, 155, 141, 215, 255, 255, @@ -312,6 +321,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(573440), e.Params.MustGetUint64(params.FileOffset)) assert.Equal(t, uint32(1073741824), e.Params.MustGetUint32(params.FileIoSize)) }, + event: &Event{Params: make(Params), Type: WriteFile}, buf: []byte{ 0, 192, 8, 0, 0, 0, 0, 0, 8, 106, 120, 154, 141, 215, 255, 255, @@ -332,6 +342,7 @@ func TestDecodeFile(t *testing.T) { assert.Equal(t, uint64(0xffff8084da3e7788), e.Params.MustGetUint64(params.FileIrpPtr)) assert.Equal(t, uint32(12860), e.Params.MustGetTid()) }, + event: &Event{Params: make(Params), Type: EnumDirectory}, buf: []byte{ 136, 119, 62, 218, 132, 128, 255, 255, 144, 201, 67, 203, 132, 128, 255, 255, @@ -347,6 +358,7 @@ func TestDecodeFile(t *testing.T) { assert.Len(t, e.Params, 2) assert.Equal(t, `\Device\HarddiskVolume3\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\Microsoft-Windows-TerminalServices-AppServerClient-Opt-WOW64-Package~31bf3856ad364e35~wow64~~10.0.26100.8115.cat`, e.Params.MustGetString(params.FilePath)) }, + event: &Event{Params: make(Params), Type: FileRundown}, buf: []byte{ 80, 71, 18, 158, 139, 181, 255, 255, 92, 0, 68, 0, 101, 0, 118, 0, @@ -407,9 +419,8 @@ func TestDecodeFile(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := initEventRecord(tt.opcode, 0, tt.buf) - e := &Event{Params: make(Params)} - paramDecoder.DecodeFile(r, e) - tt.assertions(t, e) + paramDecoder.DecodeFile(r, tt.event) + tt.assertions(t, tt.event) }) } } @@ -830,18 +841,20 @@ func TestDecodeNetwork(t *testing.T) { name string opcode uint8 buf []byte + providerID windows.GUID assertions func(t *testing.T, e *Event) }{ { name: "SendTCPv4", opcode: SendV4ID, assertions: func(t *testing.T, e *Event) { - assert.Len(t, e.Params, 6) + assert.Len(t, e.Params, 7) assert.Equal(t, "172.64.148.235", e.GetParamAsString(params.NetDIP)) assert.Equal(t, uint16(443), e.Params.MustGetUint16(params.NetDport)) assert.Equal(t, "192.168.1.44", e.GetParamAsString(params.NetSIP)) assert.Equal(t, uint16(61552), e.Params.MustGetUint16(params.NetSport)) assert.Equal(t, uint32(12448), e.Params.MustGetPid()) assert.Equal(t, uint32(28), e.Params.MustGetUint32(params.NetSize)) + assert.Equal(t, "TCP", e.GetParamAsString(params.NetL4Proto)) }, buf: []byte{ 160, 48, 0, 0, @@ -854,17 +867,19 @@ func TestDecodeNetwork(t *testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, }, + providerID: NetworkTCPEventGUID, }, { name: "ConnectTCPv4", opcode: ConnectTCPv4ID, assertions: func(t *testing.T, e *Event) { - assert.Len(t, e.Params, 6) + assert.Len(t, e.Params, 7) assert.Equal(t, "151.101.193.91", e.GetParamAsString(params.NetDIP)) assert.Equal(t, uint16(443), e.Params.MustGetUint16(params.NetDport)) assert.Equal(t, "192.168.1.44", e.GetParamAsString(params.NetSIP)) assert.Equal(t, uint16(61931), e.Params.MustGetUint16(params.NetSport)) assert.Equal(t, uint32(12448), e.Params.MustGetPid()) assert.Equal(t, uint32(0), e.Params.MustGetUint32(params.NetSize)) + assert.Equal(t, "TCP", e.GetParamAsString(params.NetL4Proto)) }, buf: []byte{ 160, 48, 0, 0, @@ -878,17 +893,19 @@ func TestDecodeNetwork(t *testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, }, + providerID: NetworkTCPEventGUID, }, { name: "RecvUDPv6", opcode: RecvV6ID, assertions: func(t *testing.T, e *Event) { - assert.Len(t, e.Params, 6) + assert.Len(t, e.Params, 7) assert.Equal(t, "ff02::c", e.GetParamAsString(params.NetDIP)) assert.Equal(t, uint16(1900), e.Params.MustGetUint16(params.NetDport)) assert.Equal(t, "fe80::1", e.GetParamAsString(params.NetSIP)) assert.Equal(t, uint16(56797), e.Params.MustGetUint16(params.NetSport)) assert.Equal(t, uint32(5128), e.Params.MustGetPid()) assert.Equal(t, uint32(127), e.Params.MustGetUint32(params.NetSize)) + assert.Equal(t, "UDP", e.GetParamAsString(params.NetL4Proto)) }, buf: []byte{ 8, 20, 0, 0, @@ -904,12 +921,13 @@ func TestDecodeNetwork(t *testing.T) { 221, 221, 0, 0, 0, 0, 0, 0, 0, 0, }, + providerID: NetworkUDPEventGUID, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := initEventRecord(tt.opcode, 0, tt.buf) + r := initEventRecord(tt.opcode, 0, tt.buf, withProviderID(tt.providerID)) e := &Event{Params: make(Params)} paramDecoder.DecodeNetwork(r, e) tt.assertions(t, e) @@ -960,10 +978,28 @@ func TestDecodeDNS(t *testing.T) { assert.Equal(t, "AAAA", e.GetParamAsString(params.DNSRR)) } -func initEventRecord(opcode uint8, id uint16, buf []byte) *etw.EventRecord { +type option func(*options) + +type options struct { + providerID windows.GUID +} + +func withProviderID(id windows.GUID) option { + return func(o *options) { + o.providerID = id + } +} + +func initEventRecord(opcode uint8, id uint16, buf []byte, opts ...option) *etw.EventRecord { + var options options + for _, opt := range opts { + opt(&options) + } + return &etw.EventRecord{ Header: etw.EventHeader{ - ProcessID: 13440, + ProcessID: 13440, + ProviderID: options.providerID, EventDescriptor: etw.EventDescriptor{ Opcode: opcode, ID: id, diff --git a/pkg/event/param_windows.go b/pkg/event/param_windows.go index 691505629..7d718eb0e 100644 --- a/pkg/event/param_windows.go +++ b/pkg/event/param_windows.go @@ -213,14 +213,14 @@ var paramDecoder = &ParamDecoder{} // version number which helps us determine when the event // schema changes in order to parse new fields. func (e *Event) decodeParams(r *etw.EventRecord) { - switch r.Header.ProviderID { - case RegistryEventGUID: + switch r.Header.ProviderID.Data1 { + case RegistryEventGUID.Data1: paramDecoder.DecodeRegistry(r, e) - case FileEventGUID: + case FileEventGUID.Data1: paramDecoder.DecodeFile(r, e) - case StackWalkEventGUID: + case StackWalkEventGUID.Data1: paramDecoder.DecodeStackwalk(r, e) - case AuditAPIEventGUID: + case AuditAPIEventGUID.Data1: switch r.Header.EventDescriptor.ID { case OpenProcessID: paramDecoder.DecodeOpenProcess(r, e) @@ -231,21 +231,21 @@ func (e *Event) decodeParams(r *etw.EventRecord) { case CreateSymbolicLinkObjectID: paramDecoder.DecodeCreateSymbolicLinkObject(r, e) } - case MemEventGUID: + case MemoryEventGUID.Data1: paramDecoder.DecodeMemory(r, e) - case NetworkTCPEventGUID, NetworkUDPEventGUID: + case NetworkTCPEventGUID.Data1, NetworkUDPEventGUID.Data1: paramDecoder.DecodeNetwork(r, e) - case DNSEventGUID: + case DNSEventGUID.Data1: paramDecoder.DecodeDNS(r, e) - case ProcessEventGUID: + case ProcessEventGUID.Data1: paramDecoder.DecodeProcess(r, e) - case ModuleEventGUID: + case ModuleEventGUID.Data1: paramDecoder.DecodeModule(r, e) - case ThreadEventGUID: + case ThreadEventGUID.Data1: paramDecoder.DecodeThread(r, e) - case RegistryKernelEventGUID: + case RegistryKernelEventGUID.Data1: paramDecoder.DecodeRegSetValueInternal(r, e) - case ProcessKernelEventGUID: + case ProcessKernelEventGUID.Data1: switch r.Header.EventDescriptor.ID { case CreateProcessInternalID, ProcessRundownInternalID: paramDecoder.DecodeProcessInternal(r, e) diff --git a/pkg/event/queue.go b/pkg/event/queue.go index a580e253d..f76460ca1 100644 --- a/pkg/event/queue.go +++ b/pkg/event/queue.go @@ -109,7 +109,7 @@ func (q *Queue) Close() { q.decorator.Stop() } func (q *Queue) Push(e *Event) error { if q.stackEnrichment { // store pending event for callstack enrichment - if e.Type.CanEnrichStack() { + if e.Type.WaitStack() { q.decorator.Push(e) return nil } diff --git a/pkg/event/queue_test.go b/pkg/event/queue_test.go index cd3fed7a0..c1ab2345b 100644 --- a/pkg/event/queue_test.go +++ b/pkg/event/queue_test.go @@ -70,9 +70,7 @@ func TestQueuePush(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: File, Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -97,9 +95,7 @@ func TestQueuePush(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: File, Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -124,9 +120,7 @@ func TestQueuePush(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: File, Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -152,9 +146,7 @@ func TestQueuePush(t *testing.T) { PID: 859, CPU: 1, Seq: 2, - Name: "CreateFile", Timestamp: time.Now(), - Category: File, Params: Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, diff --git a/pkg/event/stackwalk.go b/pkg/event/stackwalk.go index 752ae39ad..c6a7fb7ae 100644 --- a/pkg/event/stackwalk.go +++ b/pkg/event/stackwalk.go @@ -221,7 +221,7 @@ func (s *StackwalkDecorator) flush() []error { if evt.PS != nil { stackwalkFlushesProcs.Add(evt.PS.Name, 1) } - stackwalkFlushesEvents.Add(evt.Name, 1) + stackwalkFlushesEvents.Add(evt.Name(), 1) } return errs diff --git a/pkg/event/types_windows.go b/pkg/event/types_windows.go index c80687f01..d67e29bc5 100644 --- a/pkg/event/types_windows.go +++ b/pkg/event/types_windows.go @@ -19,11 +19,8 @@ package event import ( - "encoding/binary" - "github.com/rabbitstack/fibratus/pkg/sys/etw" "github.com/rabbitstack/fibratus/pkg/util/colorizer" - "github.com/rabbitstack/fibratus/pkg/util/hashers" "golang.org/x/sys/windows" ) @@ -34,13 +31,13 @@ const ( // SystemLogger event is emitted by the system provider. SystemLogger Source = iota // SecurityTelemetryLogger event is emitted by the combination of multiple providers. - // Most notably, DNS, thread pool, and kernel audit API providers are in charge of - // publishing the events. + // Most notably, DNS, and kernel audit API providers are in charge of publishing the + // events. SecurityTelemetryLogger ) -// Type identifies an event type. It comprises the event GUID + hook ID to uniquely identify the event -type Type [18]byte +// Type identifies an event type. +type Type uint16 var ( // ProcessEventGUID represents process provider event GUID @@ -57,8 +54,8 @@ var ( NetworkTCPEventGUID = windows.GUID{Data1: 0x9a280ac0, Data2: 0xc8e0, Data3: 0x11d1, Data4: [8]byte{0x84, 0xe2, 0x0, 0xc0, 0x4f, 0xb9, 0x98, 0xa2}} // NetworkUDPEventGUID represents network UDP provider event GUID NetworkUDPEventGUID = windows.GUID{Data1: 0xbf3a50c5, Data2: 0xa9c9, Data3: 0x4988, Data4: [8]byte{0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80}} - // MemEventGUID represents memory provider event GUID - MemEventGUID = windows.GUID{Data1: 0x3d6fa8d3, Data2: 0xfe05, Data3: 0x11d0, Data4: [8]byte{0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}} + // MemoryEventGUID represents memory provider event GUID + MemoryEventGUID = windows.GUID{Data1: 0x3d6fa8d3, Data2: 0xfe05, Data3: 0x11d0, Data4: [8]byte{0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}} // AuditAPIEventGUID represents audit API calls event GUID AuditAPIEventGUID = windows.GUID{Data1: 0xe02a841c, Data2: 0x75a3, Data3: 0x4fa7, Data4: [8]byte{0xaf, 0xc8, 0xae, 0x09, 0xcf, 0x9b, 0x7f, 0x23}} // DNSEventGUID represents DNS provider event GUID @@ -71,6 +68,65 @@ var ( StackWalkEventGUID = windows.GUID{Data1: 0xdef2fe46, Data2: 0x7bd6, Data3: 0x4b80, Data4: [8]byte{0xbd, 0x94, 0xf5, 0x7f, 0xe2, 0x0d, 0x0c, 0xe3}} ) +const ( + Unknown Type = iota + CreateProcess + TerminateProcess + ProcessRundown + OpenProcess + CreateProcessInternal // only purpose of this event is to enrich the process state with some extra attributes + ProcessRundownInternal // populates the snapshotter for events running in the Security Telemetry session + CreateThread + TerminateThread + ThreadRundown + OpenThread + SetThreadContext + StackWalk + UnloadModule + LoadModule + ModuleRundown + LoadModuleInternal // only purpose is to populate the module state for events running in the Security Telemetry session + RegCreateKey + RegOpenKey + RegDeleteKey + RegQueryKey + RegSetValue + RegSetValueInternal // internal event that is used to enrich the corresponding public RegSetValue event with captured data + RegDeleteValue + RegQueryValue + RegCloseKey + RegCreateKCB + RegDeleteKCB + RegKCBRundown + CreateFile + ReleaseFile + CloseFile + ReadFile + WriteFile + SetFileInformation + DeleteFile + RenameFile + EnumDirectory + FileRundown + FileOpEnd + Accept + Send + Recv + Connect + Disconnect + Reconnect + Retransmit + QueryDNS + ReplyDNS + MapViewOfSection + UnmapViewOfSection + MapViewSectionRundown + VirtualAlloc + VirtualFree + CreateSymbolicLinkObject + MaxEvent // sentinel +) + const ( CreateProcessID uint8 = 1 CreateProcessInternalID uint16 = 1 @@ -90,20 +146,20 @@ const ( LoadModuleInternalID uint16 = 5 LoadModuleID uint8 = 10 - FileRundownID uint8 = 36 - MapViewFileID uint8 = 37 - UnmapViewFileID uint8 = 38 - MapFileRundownID uint8 = 39 - CreateFileID uint8 = 64 - ReleaseFileID uint8 = 65 - CloseFileID uint8 = 66 - ReadFileID uint8 = 67 - WriteFileID uint8 = 68 - SetFileInformationID uint8 = 69 - DeleteFileID uint8 = 70 - RenameFileID uint8 = 71 - EnumDirectoryID uint8 = 72 - FileOpEndID uint8 = 76 + FileRundownID uint8 = 36 + MapViewOfSectionID uint8 = 37 + UnmapViewOfSectionID uint8 = 38 + MapViewSectionRundownID uint8 = 39 + CreateFileID uint8 = 64 + ReleaseFileID uint8 = 65 + CloseFileID uint8 = 66 + ReadFileID uint8 = 67 + WriteFileID uint8 = 68 + SetFileInformationID uint8 = 69 + DeleteFileID uint8 = 70 + RenameFileID uint8 = 71 + EnumDirectoryID uint8 = 72 + FileOpEndID uint8 = 76 RegCreateKeyID uint8 = 10 RegOpenKeyID uint8 = 11 @@ -144,530 +200,211 @@ const ( StackWalkID uint8 = 32 ) -var ( - // CreateProcess identifies process creation kernel events - CreateProcess = pack(ProcessEventGUID, uint16(CreateProcessID)) - // TerminateProcess identifies process termination kernel events - TerminateProcess = pack(ProcessEventGUID, uint16(TerminateProcessID)) - // ProcessRundown represents the start data collection process event that enumerates processes that are currently running at the time the kernel session starts - ProcessRundown = pack(ProcessEventGUID, uint16(ProcessRundownID)) - // OpenProcess identifies the kernel events that are triggered when the process handle is acquired - OpenProcess = pack(AuditAPIEventGUID, OpenProcessID) - // CreateProcessInternal identifies the process creation event emitted by the Microsoft Windows Kernel Process provider. - // The only purpose of this event is to enrich the process state with some extra attributes, and populates the snapshotter - // for events running in the Security Telemetry session that might miss process lookups because the core NT Kernel Provider - // hasn't still published the CreateProcess or ProcessRundown event - CreateProcessInternal = pack(ProcessKernelEventGUID, CreateProcessInternalID) - // ProcessRundownInternal same as above but for process rundown events originating from the Microsoft Windows Kernel Process provider. - ProcessRundownInternal = pack(ProcessKernelEventGUID, ProcessRundownInternalID) - - // CreateThread identifies thread creation kernel events - CreateThread = pack(ThreadEventGUID, uint16(CreateThreadID)) - // TerminateThread identifies thread termination kernel events - TerminateThread = pack(ThreadEventGUID, uint16(TerminateThreadID)) - // ThreadRundown represents the start data collection thread event that enumerates threads that are currently running at the time the kernel session starts - ThreadRundown = pack(ThreadEventGUID, uint16(ThreadRundownID)) - // OpenThread identifies the kernel events that are triggered when the process acquires a thread handle - OpenThread = pack(AuditAPIEventGUID, OpenThreadID) - // SetThreadContext identifies the kernel event that is fired when the thread context is changed - SetThreadContext = pack(AuditAPIEventGUID, SetThreadContextID) - - // MapViewFile represents events that map a view of a file mapping into the address space of a calling process - MapViewFile = pack(FileEventGUID, uint16(MapViewFileID)) - // UnmapViewFile represents events that unmap a view of a file mapping from the address space of a calling process - UnmapViewFile = pack(FileEventGUID, uint16(UnmapViewFileID)) - // MapFileRundown represents the event that is emitted at the start of the tracing session to enumerate I/O mapped files - MapFileRundown = pack(FileEventGUID, uint16(MapFileRundownID)) - - // FileRundown events are generated by kernel rundown logger to enumerate all open files on the start of the kernel session - FileRundown = pack(FileEventGUID, uint16(FileRundownID)) - // CreateFile represents events that create/open a file or I/O device - CreateFile = pack(FileEventGUID, uint16(CreateFileID)) - // ReleaseFile represents events that occur when the last file handle is disposed - ReleaseFile = pack(FileEventGUID, uint16(ReleaseFileID)) - // CloseFile represents events that dispose existing kernel file objects - CloseFile = pack(FileEventGUID, uint16(CloseFileID)) - // ReadFile represents events that read data from the file or I/O device - ReadFile = pack(FileEventGUID, uint16(ReadFileID)) - // WriteFile represents events that write data to the file or I/O device - WriteFile = pack(FileEventGUID, uint16(WriteFileID)) - // SetFileInformation represents events that set file information - SetFileInformation = pack(FileEventGUID, uint16(SetFileInformationID)) - // DeleteFile identifies file deletion events - DeleteFile = pack(FileEventGUID, uint16(DeleteFileID)) - // RenameFile identifies events that are responsible for renaming files - RenameFile = pack(FileEventGUID, uint16(RenameFileID)) - // EnumDirectory identifies enumerate directory and directory notification events - EnumDirectory = pack(FileEventGUID, uint16(EnumDirectoryID)) - // FileOpEnd signals the finalization of the file operation - FileOpEnd = pack(FileEventGUID, uint16(FileOpEndID)) - - // RegCreateKey represents registry key creation kernel events - RegCreateKey = pack(RegistryEventGUID, uint16(RegCreateKeyID)) - // RegOpenKey represents registry open key kernel events - RegOpenKey = pack(RegistryEventGUID, uint16(RegOpenKeyID)) - // RegCloseKey represents registry close key kernel event. - RegCloseKey = pack(RegistryEventGUID, uint16(RegCloseKeyID)) - // RegDeleteKey represents registry key deletion kernel events - RegDeleteKey = pack(RegistryEventGUID, uint16(RegDeleteKeyID)) - // RegQueryKey represents registry query key kernel events - RegQueryKey = pack(RegistryEventGUID, uint16(RegQueryKeyID)) - // RegSetValue represents registry set value kernel events - RegSetValue = pack(RegistryEventGUID, uint16(RegSetValueID)) - // RegDeleteValue are kernel events for registry value removals - RegDeleteValue = pack(RegistryEventGUID, uint16(RegDeleteValueID)) - // RegQueryValue are kernel events for registry value queries - RegQueryValue = pack(RegistryEventGUID, uint16(RegQueryValueID)) - // RegCreateKCB represents kernel events for KCB (Key Control Block) creation requests - RegCreateKCB = pack(RegistryEventGUID, uint16(RegCreateKCBID)) - // RegDeleteKCB represents kernel events for KCB(Key Control Block) closures - RegDeleteKCB = pack(RegistryEventGUID, uint16(RegDeleteKCBID)) - // RegKCBRundown enumerates the registry keys open at the start of the kernel session. - RegKCBRundown = pack(RegistryEventGUID, uint16(RegKCBRundownID)) - // RegSetValueInternal is the internal event that is used to - // enrich the corresponding public RegSetValue event with - // extra attributes - RegSetValueInternal = pack(RegistryKernelEventGUID, RegSetValueInternalID) - - // UnloadModule represents unload module kernel events - UnloadModule = pack(ModuleEventGUID, uint16(UnloadModuleID)) - // ModuleRundown represents kernel events that is triggered to enumerate all loaded modules - ModuleRundown = pack(ModuleEventGUID, uint16(ModuleRundownID)) - // LoadModule represents module load kernel events that are triggered when a DLL or executable file is loaded - LoadModule = pack(ModuleEventGUID, uint16(LoadModuleID)) - // LoadModuleInternal same as for process internal event originating from the Microsoft Windows Kernel Process provider - LoadModuleInternal = pack(ProcessKernelEventGUID, LoadModuleInternalID) - - // AcceptTCPv4 represents the TCPv4 kernel events for accepting connection requests from the socket queue. - AcceptTCPv4 = pack(NetworkTCPEventGUID, uint16(AcceptTCPv4ID)) - // AcceptTCPv6 represents the TCPv6 kernel events for accepting connection requests from the socket queue. - AcceptTCPv6 = pack(NetworkTCPEventGUID, uint16(AcceptTCPv6ID)) - // SendTCPv4 represents the TCPv4 kernel events for sending data to the connected socket. - SendTCPv4 = pack(NetworkTCPEventGUID, uint16(SendV4ID)) - // SendTCPv6 represents the TCPv6 kernel events for sending data to the connected socket. - SendTCPv6 = pack(NetworkTCPEventGUID, uint16(SendV6ID)) - // SendUDPv4 represents the UDPv4 kernel events for sending datagrams to connectionless sockets. - SendUDPv4 = pack(NetworkUDPEventGUID, uint16(SendV4ID)) - // SendUDPv6 represents the UDPv6 kernel events for sending datagrams to connectionless sockets. - SendUDPv6 = pack(NetworkUDPEventGUID, uint16(SendV6ID)) - // RecvTCPv4 represents the TCP IPv4 network receive event. - RecvTCPv4 = pack(NetworkTCPEventGUID, uint16(RecvV4ID)) - // RecvTCPv6 represents the TCP IPv6 network receive event. - RecvTCPv6 = pack(NetworkTCPEventGUID, uint16(RecvV6ID)) - // RecvUDPv4 represents the UDP IPv4 network receive event. - RecvUDPv4 = pack(NetworkUDPEventGUID, uint16(RecvV4ID)) - // RecvUDPv6 represents the UDP IPv6 network receive event. - RecvUDPv6 = pack(NetworkUDPEventGUID, uint16(RecvV6ID)) - // ConnectTCPv4 represents the TCP IPv4 network connect event. - ConnectTCPv4 = pack(NetworkTCPEventGUID, uint16(ConnectTCPv4ID)) - // ConnectTCPv6 represents the TCP IPv6 network connect event. - ConnectTCPv6 = pack(NetworkTCPEventGUID, uint16(ConnectTCPv6ID)) - // DisconnectTCPv4 is the TCP IPv4 network disconnect event. - DisconnectTCPv4 = pack(NetworkTCPEventGUID, uint16(DisconnectTCPv4ID)) - // DisconnectTCPv6 is the TCP IPv6 network disconnect event. - DisconnectTCPv6 = pack(NetworkTCPEventGUID, uint16(DisconnectTCPv6ID)) - // ReconnectTCPv4 is the TCP IPv4 network reconnect event. - ReconnectTCPv4 = pack(NetworkTCPEventGUID, uint16(ReconnectTCPv4ID)) - // ReconnectTCPv6 is the TCP IPv6 network reconnect event. - ReconnectTCPv6 = pack(NetworkTCPEventGUID, uint16(ReconnectTCPv6ID)) - // RetransmitTCPv4 is the TCP IPv4 network retransmit event. - RetransmitTCPv4 = pack(NetworkTCPEventGUID, uint16(RetransmitTCPv4ID)) - // RetransmitTCPv6 is the TCP IPv6 network retransmit event. - RetransmitTCPv6 = pack(NetworkTCPEventGUID, uint16(RetransmitTCPv6ID)) - - // VirtualAlloc represents virtual memory allocation event - VirtualAlloc = pack(MemEventGUID, uint16(VirtualAllocID)) - // VirtualFree represents virtual memory release event - VirtualFree = pack(MemEventGUID, uint16(VirtualFreeID)) - - // QueryDNS represents DNS query events - QueryDNS = pack(DNSEventGUID, QueryDNSID) - // ReplyDNS represents the DNS response events - ReplyDNS = pack(DNSEventGUID, ReplyDNSID) - - // StackWalk represents stack walk event with the collection of return addresses - StackWalk = pack(StackWalkEventGUID, uint16(StackWalkID)) - - // CreateSymbolicLinkObject represents the event emitted by the object manager when the new symbolic link is created within the object manager directory - CreateSymbolicLinkObject = pack(AuditAPIEventGUID, CreateSymbolicLinkObjectID) - - // UnknownType designates unknown event type - UnknownType = pack(windows.GUID{}, 0) -) - -// NewTypeFromEventRecord creates a new event type from ETW event record. -func NewTypeFromEventRecord(ev *etw.EventRecord) Type { - return pack(ev.Header.ProviderID, ev.HookID()) -} - -// String returns the string representation of the event type. Returns an empty string -// if the event type is not recognized. -func (t Type) String() string { - switch t { - case CreateProcess, CreateProcessInternal: - return "CreateProcess" - case TerminateProcess: - return "TerminateProcess" - case ProcessRundown, ProcessRundownInternal: - return "ProcessRundown" - case OpenProcess: - return "OpenProcess" - case CreateThread: - return "CreateThread" - case TerminateThread: - return "TerminateThread" - case ThreadRundown: - return "ThreadRundown" - case OpenThread: - return "OpenThread" - case SetThreadContext: - return "SetThreadContext" - case CreateFile: - return "CreateFile" - case CloseFile: - return "CloseFile" - case ReleaseFile: - return "ReleaseFile" - case ReadFile: - return "ReadFile" - case WriteFile: - return "WriteFile" - case SetFileInformation: - return "SetFileInformation" - case DeleteFile: - return "DeleteFile" - case RenameFile: - return "RenameFile" - case EnumDirectory: - return "EnumDirectory" - case FileOpEnd: - return "FileOpEnd" - case FileRundown: - return "FileRundown" - case MapViewFile: - return "MapViewFile" - case UnmapViewFile: - return "UnmapViewFile" - case MapFileRundown: - return "MapFileRundown" - case RegKCBRundown: - return "RegKCBRundown" - case RegOpenKey: - return "RegOpenKey" - case RegCloseKey: - return "RegCloseKey" - case RegCreateKey: - return "RegCreateKey" - case RegDeleteKey: - return "RegDeleteKey" - case RegDeleteValue: - return "RegDeleteValue" - case RegQueryKey: - return "RegQueryKey" - case RegQueryValue: - return "RegQueryValue" - case RegCreateKCB: - return "RegCreateKCB" - case RegSetValue, RegSetValueInternal: - return "RegSetValue" - case LoadModule, LoadModuleInternal: - return "LoadModule" - case UnloadModule: - return "UnloadModule" - case ModuleRundown: - return "ModuleRundown" - case AcceptTCPv4, AcceptTCPv6: - return "Accept" - case SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6: - return "Send" - case RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6: - return "Recv" - case ConnectTCPv4, ConnectTCPv6: - return "Connect" - case ReconnectTCPv4, ReconnectTCPv6: - return "Reconnect" - case DisconnectTCPv4, DisconnectTCPv6: - return "Disconnect" - case RetransmitTCPv4, RetransmitTCPv6: - return "Retransmit" - case VirtualAlloc: - return "VirtualAlloc" - case VirtualFree: - return "VirtualFree" - case QueryDNS: - return "QueryDns" - case ReplyDNS: - return "ReplyDns" - case StackWalk: - return "StackWalk" - case CreateSymbolicLinkObject: - return "CreateSymbolicLinkObject" - default: - return "" +// NewTypeFromEventRecord derives the event type from the Data1 member of the provider GUID +// and the opcode/event ID integer. +// Go only jump-tables switches over integer types and only when case values are reasonably +// dense. A switch over provider ID which is GUID struct compiles to a sequential chain of +// struct-equality compares instead of a jump table. +// So we split the GUID into a fast-reject key and switch on that instead. The first member of +// the GUID (Data1) is a dense uint32 integer and is certainly unique across all providers. +func NewTypeFromEventRecord(r *etw.EventRecord) Type { + switch r.Header.ProviderID.Data1 { + case RegistryEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case RegCreateKeyID: + return RegCreateKey + case RegOpenKeyID: + return RegOpenKey + case RegDeleteKeyID: + return RegDeleteKey + case RegQueryKeyID: + return RegQueryKey + case RegSetValueID: + return RegSetValue + case RegDeleteValueID: + return RegDeleteValue + case RegQueryValueID: + return RegQueryValue + case RegCreateKCBID: + return RegCreateKCB + case RegDeleteKCBID: + return RegDeleteKCB + case RegKCBRundownID: + return RegKCBRundown + case RegCloseKeyID: + return RegCloseKey + } + case FileEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case FileRundownID: + return FileRundown + case MapViewOfSectionID: + return MapViewOfSection + case UnmapViewOfSectionID: + return UnmapViewOfSection + case MapViewSectionRundownID: + return MapViewSectionRundown + case CreateFileID: + return CreateFile + case ReleaseFileID: + return ReleaseFile + case CloseFileID: + return CloseFile + case ReadFileID: + return ReadFile + case WriteFileID: + return WriteFile + case SetFileInformationID: + return SetFileInformation + case DeleteFileID: + return DeleteFile + case RenameFileID: + return RenameFile + case EnumDirectoryID: + return EnumDirectory + case FileOpEndID: + return FileOpEnd + } + case AuditAPIEventGUID.Data1: + switch r.Header.EventDescriptor.ID { + case OpenProcessID: + return OpenProcess + case OpenThreadID: + return OpenThread + case SetThreadContextID: + return SetThreadContext + case CreateSymbolicLinkObjectID: + return CreateSymbolicLinkObject + } + case StackWalkEventGUID.Data1: + return StackWalk + case MemoryEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case VirtualAllocID: + return VirtualAlloc + case VirtualFreeID: + return VirtualFree + } + case NetworkTCPEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case AcceptTCPv4ID, AcceptTCPv6ID: + return Accept + case SendV4ID, SendV6ID: + return Send + case RecvV4ID, RecvV6ID: + return Recv + case ConnectTCPv4ID, ConnectTCPv6ID: + return Connect + case DisconnectTCPv4ID, DisconnectTCPv6ID: + return Disconnect + case ReconnectTCPv4ID, ReconnectTCPv6ID: + return Reconnect + case RetransmitTCPv4ID, RetransmitTCPv6ID: + return Retransmit + } + case NetworkUDPEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case SendV4ID, SendV6ID: + return Send + case RecvV4ID, RecvV6ID: + return Recv + } + case DNSEventGUID.Data1: + switch r.Header.EventDescriptor.ID { + case QueryDNSID: + return QueryDNS + case ReplyDNSID: + return ReplyDNS + } + case ProcessEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case CreateProcessID: + return CreateProcess + case TerminateProcessID: + return TerminateProcess + case ProcessRundownID: + return ProcessRundown + } + case ProcessKernelEventGUID.Data1: + switch r.Header.EventDescriptor.ID { + case CreateProcessInternalID: + return CreateProcessInternal + case ProcessRundownInternalID: + return ProcessRundownInternal + case LoadModuleInternalID: + return LoadModuleInternal + } + case ModuleEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case UnloadModuleID: + return UnloadModule + case ModuleRundownID: + return ModuleRundown + case LoadModuleID: + return LoadModule + } + case ThreadEventGUID.Data1: + switch r.Header.EventDescriptor.Opcode { + case CreateThreadID: + return CreateThread + case TerminateThreadID: + return TerminateThread + case ThreadRundownID: + return ThreadRundown + } } + return Unknown } -// Category determines the category to which the event type pertains. -func (t Type) Category() Category { - switch t { - case CreateProcess, CreateProcessInternal, TerminateProcess, OpenProcess, ProcessRundown, ProcessRundownInternal: - return Process - case CreateThread, TerminateThread, OpenThread, SetThreadContext, ThreadRundown, StackWalk: - return Thread - case LoadModule, UnloadModule, ModuleRundown, LoadModuleInternal: - return Module - case CreateFile, ReadFile, WriteFile, EnumDirectory, DeleteFile, RenameFile, CloseFile, SetFileInformation, - FileRundown, FileOpEnd, ReleaseFile, MapViewFile, UnmapViewFile, MapFileRundown: - return File - case RegCreateKey, RegDeleteKey, RegOpenKey, RegCloseKey, RegQueryKey, RegQueryValue, RegSetValue, RegDeleteValue, - RegKCBRundown, RegDeleteKCB, RegCreateKCB, RegSetValueInternal: - return Registry - case AcceptTCPv4, AcceptTCPv6, - ConnectTCPv4, ConnectTCPv6, - ReconnectTCPv4, ReconnectTCPv6, - RetransmitTCPv4, RetransmitTCPv6, - DisconnectTCPv4, DisconnectTCPv6, - SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6, - RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6, - QueryDNS, ReplyDNS: - return Net - case VirtualAlloc, VirtualFree: - return Mem - case CreateSymbolicLinkObject: - return Object - default: - return Unknown - } -} +// String returns the event type string representation. +func (t Type) String() string { return table[t].Name } -// Subcategory determines the event subcategory, if any. -func (t Type) Subcategory() Subcategory { - switch t { - case QueryDNS, ReplyDNS: - return DNS - default: - return None - } -} +// OnlyState determines whether the event type is solely used for state management. +func (t Type) OnlyState() bool { return table[t].Flags&OnlyState != 0 } -// Description returns a brief description of the event type. -func (t Type) Description() string { +// IsRundown indicates if this type represents a rundown event that seeds the state. +func (t Type) StateSnapshot() bool { return table[t].Flags&StateSnapshot != 0 } + +// WaitStack determines if the event waits for call stack return addresses. +func (t Type) WaitStack() bool { return table[t].Flags&WaitStack != 0 } + +// EventID produces a native ETW classic event identifier to +// indicate which event types are enabled for stack walk tracing. +func (t Type) EventID() etw.ClassicEventID { switch t { case CreateProcess: - return "Creates a new process and its primary thread" - case TerminateProcess: - return "Terminates the process and all of its threads" - case OpenProcess: - return "Opens the process handle" + return etw.ClassicEventID{GUID: ProcessEventGUID, Type: CreateProcessID} case CreateThread: - return "Creates a thread to execute within the virtual address space of the calling process" + return etw.ClassicEventID{GUID: ThreadEventGUID, Type: CreateThreadID} case TerminateThread: - return "Terminates a thread within the process" - case OpenThread: - return "Opens the thread handle" - case SetThreadContext: - return "Sets the thread context" - case ReadFile: - return "Reads data from the file or I/O device" - case WriteFile: - return "Writes data to the file or I/O device" + return etw.ClassicEventID{GUID: ThreadEventGUID, Type: TerminateThreadID} + case LoadModule: + return etw.ClassicEventID{GUID: ProcessEventGUID, Type: LoadModuleID} case CreateFile: - return "Creates or opens a file or I/O device" - case CloseFile: - return "Closes the file handle" + return etw.ClassicEventID{GUID: FileEventGUID, Type: CreateFileID} case DeleteFile: - return "Removes the file from the file system" + return etw.ClassicEventID{GUID: FileEventGUID, Type: DeleteFileID} case RenameFile: - return "Changes the file name" - case SetFileInformation: - return "Sets the file meta information" - case EnumDirectory: - return "Enumerates a directory or dispatches a directory change notification to registered listeners" - case MapViewFile: - return "Maps a view of a file mapping into the address space of a calling process" - case UnmapViewFile: - return "Unmaps a mapped view of a file from the calling process's address space" + return etw.ClassicEventID{GUID: FileEventGUID, Type: RenameFileID} case RegCreateKey: - return "Creates a registry key or opens it if the key already exists" - case RegOpenKey: - return "Opens the registry key" - case RegCloseKey: - return "Closes the registry key" - case RegSetValue: - return "Sets the data for the value of a registry key" - case RegQueryValue: - return "Reads the data for the value of a registry key" - case RegQueryKey: - return "Enumerates subkeys of the parent key" + return etw.ClassicEventID{GUID: RegistryEventGUID, Type: RegCreateKeyID} case RegDeleteKey: - return "Removes the registry key" + return etw.ClassicEventID{GUID: RegistryEventGUID, Type: RegDeleteKeyID} + case RegSetValue: + return etw.ClassicEventID{GUID: RegistryEventGUID, Type: RegSetValueID} case RegDeleteValue: - return "Removes the registry value" - case AcceptTCPv4, AcceptTCPv6: - return "Accepts the connection request from the socket queue" - case ConnectTCPv4, ConnectTCPv6: - return "Connects establishes a connection to the socket" - case DisconnectTCPv4, DisconnectTCPv6: - return "Terminates data reception on the socket" - case ReconnectTCPv4, ReconnectTCPv6: - return "Reconnects to the socket" - case RetransmitTCPv4, RetransmitTCPv6: - return "Retransmits unacknowledged TCP segments" - case SendTCPv4, SendUDPv4, SendTCPv6, SendUDPv6: - return "Sends data over the wire" - case RecvTCPv4, RecvUDPv4, RecvTCPv6, RecvUDPv6: - return "Receives data from the socket" - case LoadModule: - return "Loads the module into the address space of the calling process" - case UnloadModule: - return "Unloads the module from the address space of the calling process" + return etw.ClassicEventID{GUID: RegistryEventGUID, Type: RegDeleteValueID} case VirtualAlloc: - return "Reserves, commits, or changes the state of a region of memory within the process virtual address space" - case VirtualFree: - return "Releases or decommits a region of memory within the process virtual address space" - case QueryDNS: - return "Sends a DNS query to the name server" - case ReplyDNS: - return "Receives the response from the DNS server" - case CreateSymbolicLinkObject: - return "Creates the symbolic link within the object manager directory" + return etw.ClassicEventID{GUID: MemoryEventGUID, Type: VirtualAllocID} default: - return "" - } -} - -// Hash calculates the hash number of the event type. -func (t Type) Hash() uint32 { - if t == UnknownType { - return 0 - } - return hashers.FnvUint32([]byte(t.String())) -} - -// Exists determines whether particular event type exists. -func (t Type) Exists() bool { - return t.String() != "" -} - -// OnlyState determines whether the event type is solely used for state management. -func (t Type) OnlyState() bool { - switch t { - case ProcessRundown, - ProcessRundownInternal, - CreateProcessInternal, - ThreadRundown, - ModuleRundown, - LoadModuleInternal, - FileRundown, - RegKCBRundown, - FileOpEnd, - ReleaseFile, - MapFileRundown, - RegCreateKCB, - RegDeleteKCB, - RegSetValueInternal: - return true - default: - return false - } -} - -// CanEnrichStack determines if the event can be enriched with a callstack. -func (t Type) CanEnrichStack() bool { - switch t { - case CreateProcess, - CreateThread, - TerminateThread, - LoadModule, - RegCreateKey, - RegDeleteKey, - RegSetValue, - RegDeleteValue, - DeleteFile, - RenameFile, - VirtualAlloc: - return true - default: - return false - } -} - -// UnmarshalYAML converts the Type name to Type array type. -func (t *Type) UnmarshalYAML(unmarshal func(interface{}) error) error { - var typ string - err := unmarshal(&typ) - if err != nil { - return err - } - *t = NameToType(typ) - return nil -} - -// GUID returns the event GUID from the raw event type. -func (t *Type) GUID() windows.GUID { - return windows.GUID{ - Data1: binary.BigEndian.Uint32(t[0:4]), - Data2: binary.BigEndian.Uint16(t[4:6]), - Data3: binary.BigEndian.Uint16(t[6:8]), - Data4: [8]byte{t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]}, - } -} - -// HookID returns the event operation code (hook ID) from the raw event type. -func (t *Type) HookID() uint16 { - return binary.BigEndian.Uint16(t[16:]) -} - -// ID is an unsigned integer that uniquely -// identifies the event. Handy for bitmask -// operations. -func (t Type) ID() uint { - id := uint(t[0])<<56 | - uint(t[1])<<48 | - uint(t[2])<<40 | - uint(t[3])<<32 | - uint(t[4])<<24 | - uint(t[5])<<16 | - uint(t.HookID()) - return id -} - -// Source designates the provenance of this event type. -func (t Type) Source() Source { - switch t.GUID() { - case AuditAPIEventGUID, DNSEventGUID, ProcessKernelEventGUID, RegistryKernelEventGUID: - return SecurityTelemetryLogger - default: - return SystemLogger - } -} - -// TypeFromParts builds the event type from provider GUID and hook ID. -func TypeFromParts(g windows.GUID, id uint16) Type { return pack(g, id) } - -// pack merges event provider GUID and the hook ID into `Type` array. -// The type provides a convenient way for comparing event types. -func pack(g windows.GUID, id uint16) Type { - return [18]byte{ - byte(g.Data1 >> 24), byte(g.Data1 >> 16), byte(g.Data1 >> 8), byte(g.Data1), - byte(g.Data2 >> 8), byte(g.Data2), - byte(g.Data3 >> 8), byte(g.Data3), - g.Data4[0], - g.Data4[1], - g.Data4[2], - g.Data4[3], - g.Data4[4], - g.Data4[5], - g.Data4[6], - g.Data4[7], - byte(id >> 8), byte(id), + return etw.ClassicEventID{} } } // color return the colorized event type to render by the color formatter. func (t Type) color() string { switch t { - case CreateFile, ReadFile, CloseFile, SetFileInformation, MapViewFile, UnmapViewFile: + case CreateFile, ReadFile, CloseFile, SetFileInformation: return colorizer.SpanBold(colorizer.Cyan, t.String()) case RenameFile: return colorizer.SpanBold(colorizer.Amber, t.String()) @@ -693,18 +430,17 @@ func (t Type) color() string { return colorizer.SpanBold(colorizer.Amber, t.String()) case LoadModule, UnloadModule: return colorizer.SpanBold(colorizer.Magenta, t.String()) - case SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6, - RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6: + case Send, Recv: return colorizer.SpanBold(colorizer.Blue, t.String()) - case ConnectTCPv4, ConnectTCPv6: + case Connect: return colorizer.SpanBold(colorizer.Teal, t.String()) - case DisconnectTCPv4, DisconnectTCPv6: + case Disconnect: return colorizer.SpanBold(colorizer.Blue, t.String()) - case AcceptTCPv4, AcceptTCPv6: + case Accept: return colorizer.SpanBold(colorizer.Teal, t.String()) case QueryDNS, ReplyDNS: return colorizer.SpanBold(colorizer.Indigo, t.String()) - case VirtualAlloc, VirtualFree: + case VirtualAlloc, VirtualFree, MapViewOfSection, UnmapViewOfSection: return colorizer.SpanBold(colorizer.Magenta, t.String()) case CreateSymbolicLinkObject: return colorizer.SpanBold(colorizer.Lavender, t.String()) @@ -729,21 +465,19 @@ func (t Type) arrow() string { var clr uint8 switch t { case TerminateProcess, TerminateThread, DeleteFile, RegDeleteKey, - RegDeleteValue, UnloadModule, VirtualFree, UnmapViewFile: + RegDeleteValue, UnloadModule, VirtualFree, UnmapViewOfSection: clr = colorizer.Red case CreateProcess, CreateFile, WriteFile, RenameFile, SetFileInformation, - RegCreateKey, RegSetValue, CreateThread, SetThreadContext, VirtualAlloc, MapViewFile, - ConnectTCPv4, ConnectTCPv6, AcceptTCPv4, AcceptTCPv6, - SendTCPv4, SendTCPv6, SendUDPv4, SendUDPv6: + RegCreateKey, RegSetValue, CreateThread, SetThreadContext, VirtualAlloc, + MapViewOfSection, Connect, Accept, Send: clr = colorizer.Amber - case ReadFile, EnumDirectory, LoadModule, RegOpenKey, RegQueryKey, RegQueryValue, OpenProcess, - OpenThread, RecvTCPv4, RecvTCPv6, RecvUDPv4, RecvUDPv6: + case ReadFile, EnumDirectory, LoadModule, RegOpenKey, RegQueryKey, RegQueryValue, + OpenProcess, OpenThread, Recv: clr = colorizer.Teal case QueryDNS, ReplyDNS: clr = colorizer.Indigo default: clr = colorizer.Gray } - return colorizer.SpanBold(clr, "› ") } diff --git a/pkg/event/types_windows_test.go b/pkg/event/types_windows_test.go index 84863c4db..c20a83668 100644 --- a/pkg/event/types_windows_test.go +++ b/pkg/event/types_windows_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 by Nedim Sabic Sabic + * Copyright 2019-2026 by Nedim Sabic Sabic * https://www.fibratus.io * All Rights Reserved. * @@ -22,126 +22,442 @@ import ( "testing" "github.com/rabbitstack/fibratus/pkg/sys/etw" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/sys/windows" ) -func TestEventTypePackAllBytes(t *testing.T) { - assert.Equal(t, byte(0x3d), CreateProcess[0]) - assert.Equal(t, byte(0x6f), CreateProcess[1]) - assert.Equal(t, byte(0xa8), CreateProcess[2]) - assert.Equal(t, byte(0xd0), CreateProcess[3]) - - assert.Equal(t, byte(0xfe), CreateProcess[4]) - assert.Equal(t, byte(0x05), CreateProcess[5]) - - assert.Equal(t, byte(0x11), CreateProcess[6]) - assert.Equal(t, byte(0xd0), CreateProcess[7]) - - assert.Equal(t, byte(0x9d), CreateProcess[8]) - assert.Equal(t, byte(0xda), CreateProcess[9]) - assert.Equal(t, byte(0x0), CreateProcess[10]) - assert.Equal(t, byte(0xc0), CreateProcess[11]) - assert.Equal(t, byte(0x4f), CreateProcess[12]) - assert.Equal(t, byte(0xd7), CreateProcess[13]) - assert.Equal(t, byte(0xba), CreateProcess[14]) - assert.Equal(t, byte(0x7c), CreateProcess[15]) - assert.Equal(t, byte(0x0), CreateProcess[16]) - assert.Equal(t, byte(0x1), CreateProcess[17]) - - assert.Equal(t, byte(0x0b), QueryDNS[16]) - assert.Equal(t, byte(0xbe), QueryDNS[17]) -} - -func TestEventTypeComparison(t *testing.T) { - var tests = []struct { - name string - ktyp Type - wants Type +func TestNewTypeFromEventRecord(t *testing.T) { + tests := []struct { + name string + provider windows.GUID + id uint16 + opcode uint8 + want Type }{ + // Registry { - "equals CreateProcess", - pack(windows.GUID{Data1: 0x3d6fa8d0, Data2: 0xfe05, Data3: 0x11d0, Data4: [8]byte{0x9d, 0xda, 0x0, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}}, 1), - CreateProcess, + name: "Registry/CreateKey", + provider: RegistryEventGUID, + opcode: RegCreateKeyID, + want: RegCreateKey, }, { - "equals TerminateProcess", - pack(windows.GUID{Data1: 0x3d6fa8d0, Data2: 0xfe05, Data3: 0x11d0, Data4: [8]byte{0x9d, 0xda, 0x0, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}}, 2), - TerminateProcess, + name: "Registry/OpenKey", + provider: RegistryEventGUID, + opcode: RegOpenKeyID, + want: RegOpenKey, + }, + { + name: "Registry/DeleteKey", + provider: RegistryEventGUID, + opcode: RegDeleteKeyID, + want: RegDeleteKey, + }, + { + name: "Registry/QueryKey", + provider: RegistryEventGUID, + opcode: RegQueryKeyID, + want: RegQueryKey, + }, + { + name: "Registry/SetValue", + provider: RegistryEventGUID, + opcode: RegSetValueID, + want: RegSetValue, + }, + { + name: "Registry/DeleteValue", + provider: RegistryEventGUID, + opcode: RegDeleteValueID, + want: RegDeleteValue, + }, + { + name: "Registry/QueryValue", + provider: RegistryEventGUID, + opcode: RegQueryValueID, + want: RegQueryValue, + }, + { + name: "Registry/CreateKCB", + provider: RegistryEventGUID, + opcode: RegCreateKCBID, + want: RegCreateKCB, + }, + { + name: "Registry/DeleteKCB", + provider: RegistryEventGUID, + opcode: RegDeleteKCBID, + want: RegDeleteKCB, + }, + { + name: "Registry/KCBRundown", + provider: RegistryEventGUID, + opcode: RegKCBRundownID, + want: RegKCBRundown, + }, + { + name: "Registry/CloseKey", + provider: RegistryEventGUID, + opcode: RegCloseKeyID, + want: RegCloseKey, }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - lhs, rhs := tt.ktyp, tt.wants - assert.Equal(t, lhs, rhs) - }) - } -} + // File + { + name: "File/FileRundown", + provider: FileEventGUID, + opcode: FileRundownID, + want: FileRundown, + }, + { + name: "File/MapViewOfSection", + provider: FileEventGUID, + opcode: MapViewOfSectionID, + want: MapViewOfSection, + }, + { + name: "File/UnmapViewOfSection", + provider: FileEventGUID, + opcode: UnmapViewOfSectionID, + want: UnmapViewOfSection, + }, + { + name: "File/MapViewSectionRundown", + provider: FileEventGUID, + opcode: MapViewSectionRundownID, + want: MapViewSectionRundown, + }, + { + name: "File/CreateFile", + provider: FileEventGUID, + opcode: CreateFileID, + want: CreateFile, + }, + { + name: "File/ReleaseFile", + provider: FileEventGUID, + opcode: ReleaseFileID, + want: ReleaseFile, + }, + { + name: "File/CloseFile", + provider: FileEventGUID, + opcode: CloseFileID, + want: CloseFile, + }, + { + name: "File/ReadFile", + provider: FileEventGUID, + opcode: ReadFileID, + want: ReadFile, + }, + { + name: "File/WriteFile", + provider: FileEventGUID, + opcode: WriteFileID, + want: WriteFile, + }, + { + name: "File/SetFileInformation", + provider: FileEventGUID, + opcode: SetFileInformationID, + want: SetFileInformation, + }, + { + name: "File/DeleteFile", + provider: FileEventGUID, + opcode: DeleteFileID, + want: DeleteFile, + }, + { + name: "File/RenameFile", + provider: FileEventGUID, + opcode: RenameFileID, + want: RenameFile, + }, + { + name: "File/EnumDirectory", + provider: FileEventGUID, + opcode: EnumDirectoryID, + want: EnumDirectory, + }, + { + name: "File/FileOpEnd", + provider: FileEventGUID, + opcode: FileOpEndID, + want: FileOpEnd, + }, -func TestNewEventTypeFromEventRecord(t *testing.T) { - assert.Equal(t, CreateProcess, NewTypeFromEventRecord(&etw.EventRecord{ - Header: etw.EventHeader{ - ProviderID: windows.GUID{Data1: 0x3d6fa8d0, Data2: 0xfe05, Data3: 0x11d0, Data4: [8]byte{0x9d, 0xda, 0x0, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}}, - EventDescriptor: etw.EventDescriptor{ - Opcode: 1, - }, - }, - })) - assert.Equal(t, OpenProcess, NewTypeFromEventRecord(&etw.EventRecord{ - Header: etw.EventHeader{ - ProviderID: windows.GUID{Data1: 0xe02a841c, Data2: 0x75a3, Data3: 0x4fa7, Data4: [8]byte{0xaf, 0xc8, 0xae, 0x09, 0xcf, 0x9b, 0x7f, 0x23}}, - EventDescriptor: etw.EventDescriptor{ - ID: 5, - }, - }, - })) -} + // Audit API -- these use EventDescriptor.ID, not Opcode. + { + name: "AuditAPI/OpenProcess", + provider: AuditAPIEventGUID, + id: OpenProcessID, + want: OpenProcess, + }, + { + name: "AuditAPI/OpenThread", + provider: AuditAPIEventGUID, + id: OpenThreadID, + want: OpenThread, + }, + { + name: "AuditAPI/SetThreadContext", + provider: AuditAPIEventGUID, + id: SetThreadContextID, + want: SetThreadContext, + }, + { + name: "AuditAPI/CreateSymbolicLinkObject", + provider: AuditAPIEventGUID, + id: CreateSymbolicLinkObjectID, + want: CreateSymbolicLinkObject, + }, -func TestEventTypeExists(t *testing.T) { - require.True(t, AcceptTCPv4.Exists()) - require.True(t, AcceptTCPv6.Exists()) -} + // Stack walk + { + name: "StackWalk", + provider: StackWalkEventGUID, + want: StackWalk, + }, -func TestGUIDAndHookIDFromEventType(t *testing.T) { - var tests = []struct { - Type Type - opcode uint16 - guid windows.GUID - }{ + // Memory + { + name: "Memory/VirtualAlloc", + provider: MemoryEventGUID, + opcode: VirtualAllocID, + want: VirtualAlloc, + }, + { + name: "Memory/VirtualFree", + provider: MemoryEventGUID, + opcode: VirtualFreeID, + want: VirtualFree, + }, + + // TCP { - LoadModule, - 10, - windows.GUID{Data1: 0x2cb15d1d, Data2: 0x5fc1, Data3: 0x11d2, Data4: [8]byte{0xab, 0xe1, 0x0, 0xa0, 0xc9, 0x11, 0xf5, 0x18}}, + name: "TCP/AcceptIPv4", + provider: NetworkTCPEventGUID, + opcode: AcceptTCPv4ID, + want: Accept, }, { - WriteFile, - 68, - windows.GUID{Data1: 0x90cbdc39, Data2: 0x4a3e, Data3: 0x11d1, Data4: [8]byte{0x84, 0xf4, 0x0, 0x0, 0xf8, 0x04, 0x64, 0xe3}}, + name: "TCP/AcceptIPv6", + provider: NetworkTCPEventGUID, + opcode: AcceptTCPv6ID, + want: Accept, + }, + { + name: "TCP/SendIPv4", + provider: NetworkTCPEventGUID, + opcode: SendV4ID, + want: Send, + }, + { + name: "TCP/SendIPv6", + provider: NetworkTCPEventGUID, + opcode: SendV6ID, + want: Send, + }, + { + name: "TCP/RecvIPv4", + provider: NetworkTCPEventGUID, + opcode: RecvV4ID, + want: Recv, + }, + { + name: "TCP/RecvIPv6", + provider: NetworkTCPEventGUID, + opcode: RecvV6ID, + want: Recv, + }, + { + name: "TCP/ConnectIPv4", + provider: NetworkTCPEventGUID, + opcode: ConnectTCPv4ID, + want: Connect, + }, + { + name: "TCP/ConnectIPv6", + provider: NetworkTCPEventGUID, + opcode: ConnectTCPv6ID, + want: Connect, + }, + { + name: "TCP/DisconnectIPv4", + provider: NetworkTCPEventGUID, + opcode: DisconnectTCPv4ID, + want: Disconnect, + }, + { + name: "TCP/DisconnectIPv6", + provider: NetworkTCPEventGUID, + opcode: DisconnectTCPv6ID, + want: Disconnect, + }, + { + name: "TCP/ReconnectIPv4", + provider: NetworkTCPEventGUID, + opcode: ReconnectTCPv4ID, + want: Reconnect, + }, + { + name: "TCP/ReconnectIPv6", + provider: NetworkTCPEventGUID, + opcode: ReconnectTCPv6ID, + want: Reconnect, + }, + { + name: "TCP/RetransmitIPv4", + provider: NetworkTCPEventGUID, + opcode: RetransmitTCPv4ID, + want: Retransmit, + }, + { + name: "TCP/RetransmitIPv6", + provider: NetworkTCPEventGUID, + opcode: RetransmitTCPv6ID, + want: Retransmit, + }, + + // UDP + { + name: "UDP/SendIPv4", + provider: NetworkUDPEventGUID, + opcode: SendV4ID, + want: Send, + }, + { + name: "UDP/SendIPv6", + provider: NetworkUDPEventGUID, + opcode: SendV6ID, + want: Send, + }, + { + name: "UDP/RecvIPv4", + provider: NetworkUDPEventGUID, + opcode: RecvV4ID, + want: Recv, + }, + { + name: "UDP/RecvIPv6", + provider: NetworkUDPEventGUID, + opcode: RecvV6ID, + want: Recv, + }, + + // DNS -- uses EventDescriptor.ID. + { + name: "DNS/Query", + provider: DNSEventGUID, + id: QueryDNSID, + want: QueryDNS, + }, + { + name: "DNS/Reply", + provider: DNSEventGUID, + id: ReplyDNSID, + want: ReplyDNS, + }, + + // Process + { + name: "Process/CreateProcess", + provider: ProcessEventGUID, + opcode: CreateProcessID, + want: CreateProcess, + }, + { + name: "Process/TerminateProcess", + provider: ProcessEventGUID, + opcode: TerminateProcessID, + want: TerminateProcess, + }, + { + name: "Process/ProcessRundown", + provider: ProcessEventGUID, + opcode: ProcessRundownID, + want: ProcessRundown, + }, + + // Process kernel -- uses EventDescriptor.ID. + { + name: "ProcessKernel/CreateProcessInternal", + provider: ProcessKernelEventGUID, + id: CreateProcessInternalID, + want: CreateProcessInternal, + }, + { + name: "ProcessKernel/ProcessRundownInternal", + provider: ProcessKernelEventGUID, + id: ProcessRundownInternalID, + want: ProcessRundownInternal, + }, + { + name: "ProcessKernel/LoadModuleInternal", + provider: ProcessKernelEventGUID, + id: LoadModuleInternalID, + want: LoadModuleInternal, + }, + + // Module + { + name: "Module/UnloadModule", + provider: ModuleEventGUID, + opcode: UnloadModuleID, + want: UnloadModule, + }, + { + name: "Module/ModuleRundown", + provider: ModuleEventGUID, + opcode: ModuleRundownID, + want: ModuleRundown, + }, + { + name: "Module/LoadModule", + provider: ModuleEventGUID, + opcode: LoadModuleID, + want: LoadModule, + }, + + // Thread + { + name: "Thread/CreateThread", + provider: ThreadEventGUID, + opcode: CreateThreadID, + want: CreateThread, + }, + { + name: "Thread/TerminateThread", + provider: ThreadEventGUID, + opcode: TerminateThreadID, + want: TerminateThread, + }, + { + name: "Thread/ThreadRundown", + provider: ThreadEventGUID, + opcode: ThreadRundownID, + want: ThreadRundown, }, } for _, tt := range tests { - t.Run(tt.Type.String(), func(t *testing.T) { - assert.Equal(t, tt.guid.String(), tt.Type.GUID().String()) - assert.Equal(t, tt.opcode, tt.Type.HookID()) - }) - } -} + t.Run(tt.name, func(t *testing.T) { + r := &etw.EventRecord{ + Header: etw.EventHeader{ + ProviderID: tt.provider, + EventDescriptor: etw.EventDescriptor{ + ID: tt.id, + Opcode: tt.opcode, + }, + }, + } -func TestIDEquality(t *testing.T) { - evt := etw.EventRecord{Header: etw.EventHeader{ProviderID: ThreadEventGUID, EventDescriptor: etw.EventDescriptor{Opcode: 1}}} - typ := CreateThread - require.Equal(t, typ.ID(), evt.ID()) -} + got := NewTypeFromEventRecord(r) -func TestEventTypeIDCollision(t *testing.T) { - ids := make(map[uint]Type) - for _, typ := range AllWithState() { - if etype, ok := ids[typ.ID()]; ok { - t.Fatalf("id collision for %s event type. Mapped event type: %s", typ.String(), etype.String()) - } - ids[typ.ID()] = typ + if got != tt.want { + t.Fatalf("NewTypeFromEventRecord() = %v, want %v", got, tt.want) + } + }) } } diff --git a/pkg/filament/dict.go b/pkg/filament/dict.go index 967ec7dcf..0150d334b 100644 --- a/pkg/filament/dict.go +++ b/pkg/filament/dict.go @@ -62,9 +62,9 @@ func newEventDict(evt *event.Event) (*cpython.Dict, error) { dict.Insert(pid, cpython.NewPyObjectFromValue(evt.PID)) dict.Insert(tid, cpython.NewPyObjectFromValue(evt.Tid)) dict.Insert(cpu, cpython.NewPyObjectFromValue(evt.CPU)) - dict.Insert(name, cpython.NewPyObjectFromValue(evt.Name)) - dict.Insert(cat, cpython.NewPyObjectFromValue(string(evt.Category))) - dict.Insert(desc, cpython.NewPyObjectFromValue(evt.Description)) + dict.Insert(name, cpython.NewPyObjectFromValue(evt.Name())) + dict.Insert(cat, cpython.NewPyObjectFromValue(evt.Category().String())) + dict.Insert(desc, cpython.NewPyObjectFromValue(evt.Description())) dict.Insert(host, cpython.NewPyObjectFromValue(evt.Host)) dict.Insert(ts, cpython.NewPyObjectFromValue(evt.Timestamp)) diff --git a/pkg/filament/dict_test.go b/pkg/filament/dict_test.go index 7d714cf25..308c240ca 100644 --- a/pkg/filament/dict_test.go +++ b/pkg/filament/dict_test.go @@ -22,14 +22,15 @@ package filament import ( + "net" + "testing" + "time" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/filament/cpython" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "net" - "testing" - "time" ) func TestProduceEventDict(t *testing.T) { @@ -41,16 +42,15 @@ func TestProduceEventDict(t *testing.T) { defer cpython.Finalize() now := time.Now() evt := &event.Event{ - Seq: uint64(12456738026482168384), - Tid: 2484, - PID: 859, - CPU: 1, - Name: "CreateFile", - Timestamp: now, - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Seq: uint64(12456738026482168384), + Tid: 2484, + PID: 859, + CPU: 1, + Type: event.CreateFile, + Timestamp: now, + Host: "archrabbit", } + dict, err := newEventDict(evt) require.NoError(t, err) require.NotNil(t, dict) @@ -80,7 +80,7 @@ func TestProduceEventDictWithIPAddresses(t *testing.T) { defer cpython.Finalize() evt := &event.Event{ - Name: "Send", + Type: event.Send, Tid: 2484, PID: 859, Params: event.Params{ @@ -112,15 +112,13 @@ func BenchmarkTestProduceEventDict(b *testing.B) { defer cpython.Finalize() evt := &event.Event{ - Seq: uint64(12456738026482168384), - Tid: 2484, - PID: 859, - CPU: 1, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Seq: uint64(12456738026482168384), + Tid: 2484, + PID: 859, + CPU: 1, + Type: event.CreateFile, + Timestamp: time.Now(), + Host: "archrabbit", } for i := 0; i < b.N; i++ { diff --git a/pkg/filament/filament_test.go b/pkg/filament/filament_test.go index 0e457e361..57e57c3b6 100644 --- a/pkg/filament/filament_test.go +++ b/pkg/filament/filament_test.go @@ -72,10 +72,8 @@ func TestOnNextEvent(t *testing.T) { Type: event.RegCreateKey, Tid: 2484, PID: 859, - Name: "RegCreateKey", Host: "archrabbit", CPU: uint8(i / 2), - Category: event.Registry, Seq: uint64(i), Timestamp: time.Now(), Params: event.Params{ @@ -105,7 +103,7 @@ func TestFilamentFilter(t *testing.T) { require.NotNil(t, filament) defer filament.Close() require.NotNil(t, filament.Filter()) - kpars := event.Params{ + pars := event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe -k RPCSS"}, params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost.exe"}, params.ProcessID: {Name: params.ProcessID, Type: params.Uint32, Value: uint32(1234)}, @@ -114,8 +112,7 @@ func TestFilamentFilter(t *testing.T) { evt := &event.Event{ Type: event.CreateProcess, - Params: kpars, - Name: "CreateProcess", + Params: pars, } require.True(t, filament.Filter().Eval(evt)) diff --git a/pkg/filter/accessor.go b/pkg/filter/accessor.go index 52e7ee717..ea14a98f8 100644 --- a/pkg/filter/accessor.go +++ b/pkg/filter/accessor.go @@ -77,11 +77,11 @@ func (*evtAccessor) Get(f Field, evt *event.Event) (params.Value, error) { case fields.EvtCPU, fields.KevtCPU: return evt.CPU, nil case fields.EvtName, fields.KevtName: - return evt.Name, nil + return evt.Name(), nil case fields.EvtCategory, fields.KevtCategory: - return string(evt.Category), nil + return evt.Category().String(), nil case fields.EvtDesc, fields.KevtDesc: - return evt.Description, nil + return evt.Description(), nil case fields.EvtHost, fields.KevtHost: return evt.Host, nil case fields.EvtTime, fields.KevtTime: diff --git a/pkg/filter/accessor_windows.go b/pkg/filter/accessor_windows.go index 0f2062416..e39cd59ef 100644 --- a/pkg/filter/accessor_windows.go +++ b/pkg/filter/accessor_windows.go @@ -76,7 +76,7 @@ type psAccessor struct { func (psAccessor) SetFields([]Field) {} func (psAccessor) SetSegments([]fields.Segment) {} func (psAccessor) IsFieldAccessible(e *event.Event) bool { - return e.PS != nil || e.Category == event.Process + return e.PS != nil || e.Category() == event.Process } func newPSAccessor(psnap psnap.Snapshotter) Accessor { return &psAccessor{psnap: psnap} } @@ -475,7 +475,7 @@ type threadAccessor struct{} func (threadAccessor) SetFields([]Field) {} func (threadAccessor) SetSegments([]fields.Segment) {} func (threadAccessor) IsFieldAccessible(e *event.Event) bool { - return !e.Callstack.IsEmpty() || e.Category == event.Thread + return !e.Callstack.IsEmpty() || e.Category() == event.Thread } func newThreadAccessor() Accessor { @@ -676,7 +676,9 @@ type fileAccessor struct{} func (fileAccessor) SetFields(fields []Field) {} func (fileAccessor) SetSegments([]fields.Segment) {} -func (fileAccessor) IsFieldAccessible(e *event.Event) bool { return e.Category == event.File } +func (fileAccessor) IsFieldAccessible(e *event.Event) bool { + return e.Category() == event.File || e.Category() == event.Memory +} func newFileAccessor() Accessor { return &fileAccessor{} @@ -770,7 +772,7 @@ func (moduleAccessor) SetFields(fields []Field) {} func (moduleAccessor) SetSegments([]fields.Segment) {} func (moduleAccessor) IsFieldAccessible(e *event.Event) bool { - return e.Category == event.Module + return e.Category() == event.Module } func newModuleAccessor() Accessor { @@ -868,7 +870,7 @@ type registryAccessor struct{} func (registryAccessor) SetFields([]Field) {} func (registryAccessor) SetSegments([]fields.Segment) {} func (registryAccessor) IsFieldAccessible(e *event.Event) bool { - return e.Category == event.Registry + return e.Category() == event.Registry } func newRegistryAccessor() Accessor { @@ -920,7 +922,7 @@ func (n *networkAccessor) SetFields(flds []Field) { func (networkAccessor) SetSegments([]fields.Segment) {} func (networkAccessor) IsFieldAccessible(e *event.Event) bool { - return e.Category == event.Net + return e.Category() == event.Network } func newNetworkAccessor() Accessor { return &networkAccessor{} } @@ -1205,7 +1207,7 @@ type memAccessor struct{} func (memAccessor) SetFields([]Field) {} func (memAccessor) SetSegments([]fields.Segment) {} -func (memAccessor) IsFieldAccessible(e *event.Event) bool { return e.Category == event.Mem } +func (memAccessor) IsFieldAccessible(e *event.Event) bool { return e.Category() == event.Memory } func newMemAccessor() Accessor { return &memAccessor{} @@ -1236,7 +1238,7 @@ type dnsAccessor struct{} func (dnsAccessor) SetFields([]Field) {} func (dnsAccessor) SetSegments([]fields.Segment) {} func (dnsAccessor) IsFieldAccessible(e *event.Event) bool { - return e.Type.Subcategory() == event.DNS + return e.Subcategory() == event.DNS } func newDNSAccessor() Accessor { diff --git a/pkg/filter/accessor_windows_test.go b/pkg/filter/accessor_windows_test.go index 6d52f0717..0035e7f52 100644 --- a/pkg/filter/accessor_windows_test.go +++ b/pkg/filter/accessor_windows_test.go @@ -79,67 +79,67 @@ func TestIsFieldAccessible(t *testing.T) { }{ { newEventAccessor(), - &event.Event{Type: event.QueryDNS, Category: event.Net}, + &event.Event{Type: event.QueryDNS}, true, }, { newPSAccessor(nil), - &event.Event{Type: event.CreateProcess, Category: event.Process}, + &event.Event{Type: event.CreateProcess}, true, }, { newPSAccessor(nil), - &event.Event{PS: &ptypes.PS{}, Type: event.CreateFile, Category: event.File}, + &event.Event{PS: &ptypes.PS{}, Type: event.CreateFile}, true, }, { newPSAccessor(nil), - &event.Event{Type: event.SetThreadContext, Category: event.Thread}, + &event.Event{Type: event.SetThreadContext}, false, }, { newThreadAccessor(), - &event.Event{Type: event.SetThreadContext, Category: event.Thread}, + &event.Event{Type: event.SetThreadContext}, true, }, { newThreadAccessor(), - &event.Event{Type: event.CreateProcess, Category: event.Process, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, + &event.Event{Type: event.CreateProcess, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, true, }, { newThreadAccessor(), - &event.Event{Type: event.RegSetValue, Category: event.Registry, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, + &event.Event{Type: event.RegSetValue, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, true, }, { newRegistryAccessor(), - &event.Event{Type: event.RegSetValue, Category: event.Registry, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, + &event.Event{Type: event.RegSetValue, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, true, }, { newNetworkAccessor(), - &event.Event{Type: event.RegSetValue, Category: event.Registry, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, + &event.Event{Type: event.RegSetValue, Callstack: []callstack.Frame{{Addr: 0x7ffb5c1d0396, Offset: 0x61, Symbol: "CreateProcessW", Module: "C:\\WINDOWS\\System32\\KERNELBASE.dll"}}}, false, }, { newNetworkAccessor(), - &event.Event{Type: event.ConnectTCPv6, Category: event.Net}, + &event.Event{Type: event.Connect}, true, }, { newDNSAccessor(), - &event.Event{Type: event.ReplyDNS, Category: event.Net}, + &event.Event{Type: event.ReplyDNS}, true, }, { newModuleAccessor(), - &event.Event{Type: event.LoadModule, Category: event.Module}, + &event.Event{Type: event.LoadModule}, true, }, { newMemAccessor(), - &event.Event{Type: event.VirtualAlloc, Category: event.Mem}, + &event.Event{Type: event.VirtualAlloc}, true, }, } diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index 1c3fb95ae..b79aa493d 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -175,8 +175,7 @@ func TestProcFilter(t *testing.T) { } evt := &event.Event{ - Type: event.CreateProcess, - Category: event.Process, + Type: event.CreateProcess, Params: event.Params{ params.Cmdline: {Name: params.Cmdline, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\svchost.exe -k DcomLaunch -p -s LSM"}, params.ProcessName: {Name: params.ProcessName, Type: params.AnsiString, Value: "svchost.exe"}, @@ -190,8 +189,7 @@ func TestProcFilter(t *testing.T) { params.ProcessTokenIsElevated: {Name: params.ProcessTokenIsElevated, Type: params.Bool, Value: true}, params.ProcessTokenElevationType: {Name: params.ProcessTokenElevationType, Type: params.AnsiString, Value: "DEFAULT"}, }, - Name: "CreateProcess", - PID: 1234, + PID: 1234, PS: &pstypes.PS{ Name: "svchost.exe", Cmdline: "C:\\Windows\\System32\\svchost.exe -k DcomLaunch -p -s LSM", @@ -226,13 +224,11 @@ func TestProcFilter(t *testing.T) { evt.Timestamp, _ = time.Parse(time.RFC3339, "2011-05-03T15:04:05.323Z") evt1 := &event.Event{ - Type: event.OpenProcess, - Category: event.Process, + Type: event.OpenProcess, Params: event.Params{ params.DesiredAccess: {Name: params.DesiredAccess, Type: params.Flags, Value: uint32(0x1400), Flags: event.PsAccessRightFlags}, }, - Name: "OpenProcess", - PID: 1023, + PID: 1023, PS: &pstypes.PS{ Name: "svchost.exe", Parent: parent, @@ -246,13 +242,11 @@ func TestProcFilter(t *testing.T) { } evt2 := &event.Event{ - Type: event.OpenProcess, - Category: event.Process, + Type: event.OpenProcess, Params: event.Params{ params.DesiredAccess: {Name: params.DesiredAccess, Type: params.Flags, Value: uint32(0x1400), Flags: event.PsAccessRightFlags}, }, - Name: "OpenProcess", - PID: 1023, + PID: 1023, } var tests = []struct { @@ -424,11 +418,9 @@ func TestThreadFilter(t *testing.T) { params.StartAddressModule: {Name: params.StartAddressModule, Type: params.UnicodeString, Value: "C:\\Windows\\System32\\kernel32.dll"}, } evt := &event.Event{ - Type: event.CreateThread, - Params: pars, - Name: "CreateThread", - PID: windows.GetCurrentProcessId(), - Category: event.Thread, + Type: event.CreateThread, + Params: pars, + PID: windows.GetCurrentProcessId(), PS: &pstypes.PS{ Name: "svchost.exe", Envs: map[string]string{"ALLUSERSPROFILE": "C:\\ProgramData", "OS": "Windows_NT", "ProgramFiles(x86)": "C:\\Program Files (x86)"}, @@ -615,15 +607,12 @@ func TestThreadFilter(t *testing.T) { func TestFileFilter(t *testing.T) { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\user32.dll"}, @@ -705,9 +694,7 @@ func TestFileInfoFilter(t *testing.T) { { `file.info_class = 'Allocation'`, &event.Event{ - Category: event.File, - Type: event.SetFileInformation, - Name: "SetFileInformation", + Type: event.SetFileInformation, Params: event.Params{ params.FileInfoClass: {Name: params.FileInfoClass, Type: params.Enum, Value: fs.AllocationClass, Enum: fs.FileInfoClasses}, }, @@ -717,9 +704,7 @@ func TestFileInfoFilter(t *testing.T) { { `file.info.allocation_size = 64500`, &event.Event{ - Category: event.File, - Type: event.SetFileInformation, - Name: "SetFileInformation", + Type: event.SetFileInformation, Params: event.Params{ params.FileInfoClass: {Name: params.FileInfoClass, Type: params.Enum, Value: fs.AllocationClass, Enum: fs.FileInfoClasses}, params.FileExtraInfo: {Name: params.FileExtraInfo, Type: params.Uint64, Value: uint64(64500)}, @@ -730,9 +715,7 @@ func TestFileInfoFilter(t *testing.T) { { `file.info.eof_size = 64500`, &event.Event{ - Category: event.File, - Type: event.SetFileInformation, - Name: "SetFileInformation", + Type: event.SetFileInformation, Params: event.Params{ params.FileInfoClass: {Name: params.FileInfoClass, Type: params.Enum, Value: fs.EOFClass, Enum: fs.FileInfoClasses}, params.FileExtraInfo: {Name: params.FileExtraInfo, Type: params.Uint64, Value: uint64(64500)}, @@ -743,9 +726,7 @@ func TestFileInfoFilter(t *testing.T) { { `file.info.eof_size = 64500`, &event.Event{ - Category: event.File, - Type: event.SetFileInformation, - Name: "SetFileInformation", + Type: event.SetFileInformation, Params: event.Params{ params.FileInfoClass: {Name: params.FileInfoClass, Type: params.Enum, Value: fs.DispositionClass, Enum: fs.FileInfoClasses}, params.FileExtraInfo: {Name: params.FileExtraInfo, Type: params.Uint64, Value: uint64(1)}, @@ -756,9 +737,7 @@ func TestFileInfoFilter(t *testing.T) { { `file.info.is_disposition_delete_file = true`, &event.Event{ - Category: event.File, - Type: event.DeleteFile, - Name: "DeleteFile", + Type: event.DeleteFile, Params: event.Params{ params.FileInfoClass: {Name: params.FileInfoClass, Type: params.Enum, Value: fs.DispositionClass, Enum: fs.FileInfoClasses}, params.FileExtraInfo: {Name: params.FileExtraInfo, Type: params.Uint64, Value: uint64(1)}, @@ -782,16 +761,13 @@ func TestFileInfoFilter(t *testing.T) { func TestEventFilter(t *testing.T) { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", - Evasions: uint32(evasion.IndirectSyscall), + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Host: "archrabbit", + Evasions: uint32(evasion.IndirectSyscall), Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(3434)}, params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, @@ -860,13 +836,12 @@ func TestEventFilter(t *testing.T) { func TestNetFilter(t *testing.T) { evt := &event.Event{ - Type: event.SendTCPv4, + Type: event.Accept, Tid: 2484, PID: 859, PS: &pstypes.PS{ Name: "cmd.exe", }, - Category: event.Net, Params: event.Params{ params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(443)}, params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, @@ -911,13 +886,12 @@ func TestNetFilter(t *testing.T) { } evt1 := &event.Event{ - Type: event.SendTCPv4, + Type: event.Send, Tid: 2484, PID: 859, PS: &pstypes.PS{ Name: "cmd.exe", }, - Category: event.Net, Params: event.Params{ params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(53)}, params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, @@ -950,10 +924,9 @@ func TestNetFilter(t *testing.T) { func TestRegistryFilter(t *testing.T) { evt := &event.Event{ - Type: event.RegSetValue, - Tid: 2484, - PID: 859, - Category: event.Registry, + Type: event.RegSetValue, + Tid: 2484, + PID: 859, Params: event.Params{ params.RegPath: {Name: params.RegPath, Type: params.UnicodeString, Value: `HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid`}, params.RegData: {Name: params.RegData, Type: params.Uint32, Value: uint32(10234)}, @@ -994,8 +967,7 @@ func TestModuleFilter(t *testing.T) { fs.GetMetadataStore().AddFile(filepath.Join(os.Getenv("windir"), "System32", "kernel32.dll"), &fs.FileInfo{IsDLL: true}) e1 := &event.Event{ - Type: event.LoadModule, - Category: event.Module, + Type: event.LoadModule, Params: event.Params{ params.ModulePath: {Name: params.ModulePath, Type: params.UnicodeString, Value: filepath.Join(os.Getenv("windir"), "System32", "kernel32.dll")}, params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1023)}, @@ -1065,8 +1037,7 @@ func TestModuleFilter(t *testing.T) { // now exercise unsigned/unchecked signature e2 := &event.Event{ - Type: event.LoadModule, - Category: event.Module, + Type: event.LoadModule, Params: event.Params{ params.ModulePath: {Name: params.ModulePath, Type: params.UnicodeString, Value: filepath.Join(os.Getenv("windir"), "System32", "kernel32.dll")}, params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1023)}, @@ -1119,8 +1090,7 @@ func TestModuleFilter(t *testing.T) { assert.NotNil(t, signature.GetSignatures().GetSignature(key)) e3 := &event.Event{ - Type: event.LoadModule, - Category: event.Module, + Type: event.LoadModule, Params: event.Params{ params.ModulePath: {Name: params.ModulePath, Type: params.UnicodeString, Value: "..\\pe\\_fixtures\\mscorlib.dll"}, params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(1023)}, @@ -1273,10 +1243,8 @@ func TestMemFilter(t *testing.T) { } evt := &event.Event{ - Type: event.VirtualAlloc, - Params: pars, - Name: "VirtualAlloc", - Category: event.Mem, + Type: event.VirtualAlloc, + Params: pars, PS: &pstypes.PS{ Name: "svchost.exe", Envs: map[string]string{"ALLUSERSPROFILE": "C:\\ProgramData", "OS": "Windows_NT", "ProgramFiles(x86)": "C:\\Program Files (x86)"}, @@ -1318,7 +1286,6 @@ func TestDNSFilter(t *testing.T) { PS: &pstypes.PS{ Name: "cmd.exe", }, - Category: event.Net, Params: event.Params{ params.DNSName: {Name: params.DNSName, Type: params.UnicodeString, Value: "r3.o.lencr.org"}, params.DNSRR: {Name: params.DNSRR, Type: params.Enum, Value: uint32(0x0001), Enum: event.DNSRecordTypes}, @@ -1364,10 +1331,8 @@ func TestInterpolateFields(t *testing.T) { interpolated: "Credential discovery via VaultCmd.exe (VaultCmd.exe /listcreds:Windows Credentials /all) and user LOCAL\\tor", evts: []*event.Event{ { - Type: event.CreateProcess, - Category: event.Process, - Name: "CreateProcess", - PID: 1023, + Type: event.CreateProcess, + PID: 1023, PS: &pstypes.PS{ Name: "VaultCmd.exe", Ppid: 345, @@ -1384,10 +1349,8 @@ func TestInterpolateFields(t *testing.T) { interpolated: "Credential discovery via N/A and pid 1023", evts: []*event.Event{ { - Type: event.CreateProcess, - Category: event.Process, - Name: "CreateProcess", - PID: 1023, + Type: event.CreateProcess, + PID: 1023, }, }, }, @@ -1396,10 +1359,8 @@ func TestInterpolateFields(t *testing.T) { interpolated: "Suspicious thread start module C:\\Windows\\System32\\vault.dll", evts: []*event.Event{ { - Type: event.CreateThread, - Category: event.Thread, - Name: "CreateThread", - PID: 1023, + Type: event.CreateThread, + PID: 1023, Params: event.Params{ params.StartAddressModule: {Name: params.StartAddressModule, Type: params.UnicodeString, Value: "C:\\Windows\\System32\\vault.dll"}, }, @@ -1416,10 +1377,8 @@ and subsequently write the C:\Users eo\Temp\lsass.dump dump file to the disk device`, evts: []*event.Event{ { - Type: event.OpenProcess, - Category: event.Process, - Name: "OpenProcess", - PID: 1023, + Type: event.OpenProcess, + PID: 1023, PS: &pstypes.PS{ Name: "taskmgr.exe", Ppid: 345, @@ -1427,10 +1386,8 @@ eo\Temp\lsass.dump dump file to the disk device`, }, }, { - Type: event.WriteFile, - Category: event.File, - Name: "WriteFile", - PID: 1023, + Type: event.WriteFile, + PID: 1023, Params: event.Params{ params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Users\neo\\Temp\\lsass.dump"}, }, @@ -1452,10 +1409,8 @@ and subsequently write the C:\Users eo\Temp\lsass.dump dump file to the disk device`, evts: []*event.Event{ { - Type: event.OpenProcess, - Category: event.Process, - Name: "OpenProcess", - PID: 1023, + Type: event.OpenProcess, + PID: 1023, PS: &pstypes.PS{ Name: "taskmgr.exe", Ppid: 345, @@ -1463,10 +1418,8 @@ eo\Temp\lsass.dump dump file to the disk device`, }, }, { - Type: event.WriteFile, - Category: event.File, - Name: "WriteFile", - PID: 1023, + Type: event.WriteFile, + PID: 1023, Params: event.Params{ params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Users\neo\\Temp\\lsass.dump"}, }, @@ -1503,7 +1456,6 @@ func BenchmarkFilterRun(b *testing.B) { evt := &event.Event{ Type: event.CreateProcess, Params: pars, - Name: "CreateProcess", } for i := 0; i < b.N; i++ { diff --git a/pkg/filter/ql/literal.go b/pkg/filter/ql/literal.go index 65140dea1..9e632b289 100644 --- a/pkg/filter/ql/literal.go +++ b/pkg/filter/ql/literal.go @@ -27,6 +27,7 @@ import ( "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/filter/fields" + "github.com/rabbitstack/fibratus/pkg/util/bitmap" "github.com/rabbitstack/fibratus/pkg/filter/ql/functions" ) @@ -288,8 +289,10 @@ type SequenceExpr struct { // Alias represents the sequence expression alias when bound fields are used. Alias string - bitsets event.BitSets - types []event.Type + eventBitmap bitmap.Bitmap[event.Type] + categoryBitmap bitmap.Bitmap[event.Category] + + types []event.Type } func (e *SequenceExpr) init() { @@ -343,39 +346,26 @@ func (e *SequenceExpr) walk() { WalkFunc(e.Expr, walk) - uniqCats := make(map[event.Category]bool) - // initialize event type/category buckets for every such field for name, values := range stringFields { for _, v := range values { switch name { case fields.EvtName: - for _, typ := range event.NameToTypes(v) { - if typ == event.UnknownType { - continue - } - e.types = append(e.types, typ) - uniqCats[event.TypeToEventInfo(typ).Category] = true + typ, ok := event.ParseType(v) + if !ok { + continue } + e.types = append(e.types, typ) + e.eventBitmap.Set(typ) case fields.EvtCategory: - e.bitsets.SetCategoryBit(event.Category(v)) + category, ok := event.ParseCategory(v) + if !ok { + continue + } + e.categoryBitmap.Set(category) } } } - - for _, t := range e.types { - switch len(uniqCats) { - case 0: - continue - case 1: - // happy path can use a single bitmask for all - // event types pertaining to the same category - e.bitsets.SetBit(event.TypeBitSet, t) - default: - // use map-backed bitmask for event identifiers - e.bitsets.SetBit(event.BitmaskBitSet, t) - } - } } // IsEvaluable determines if the expression should be evaluated by inspecting @@ -383,7 +373,7 @@ func (e *SequenceExpr) walk() { // to be evaluated when the incoming event type, ID, or category pertains to the one // defined in the field literal. func (e *SequenceExpr) IsEvaluable(evt *event.Event) bool { - return e.bitsets.IsBitSet(evt) + return e.eventBitmap.Has(evt.Type) || e.categoryBitmap.Has(evt.Category()) } // HasBoundFields determines if this sequence expression references any bound field. @@ -434,7 +424,7 @@ func (s *Sequence) init() { for _, expr := range s.Expressions { for _, etype := range expr.types { - sources[etype.Source()] = true + sources[event.GetTypeInfo(etype).Source] = true } } diff --git a/pkg/filter/ql/literal_test.go b/pkg/filter/ql/literal_test.go index 3c978cbf5..19067b5c7 100644 --- a/pkg/filter/ql/literal_test.go +++ b/pkg/filter/ql/literal_test.go @@ -19,64 +19,28 @@ package ql import ( + "testing" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func TestSequenceExprIsEvaluable(t *testing.T) { var tests = []struct { - expr string - evt *event.Event - isEval bool - assertions func(t *testing.T, sexpr *SequenceExpr) + expr string + evt *event.Event + isEval bool }{ - {"evt.name = 'CreateProcess'", &event.Event{Type: event.CreateProcess, Category: event.Process}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.True(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, - {"evt.name = 'CreateProcess'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil}, - {"evt.name = 'CreateProcess' or evt.name = 'TerminateThread'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil}, - {"evt.name = 'CreateProcess' or evt.category = 'object'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil}, - {"evt.name = 'CreateProcess' or evt.name = 'OpenProcess'", &event.Event{Type: event.OpenProcess, Category: event.Process}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.True(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, - {"evt.name = 'CreateProcess' or evt.name = 'CreateThread'", &event.Event{Type: event.CreateThread, Category: event.Thread}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.False(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.True(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, - {"evt.name = 'CreateProcess' or evt.category = 'registry'", &event.Event{Type: event.RegSetValue, Category: event.Registry}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.True(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.True(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, - {"evt.name = 'CreateProcess' or evt.name = 'OpenProcess' or evt.category = 'registry'", &event.Event{Type: event.OpenProcess, Category: event.Process}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.True(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.False(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.True(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, - {"evt.name = 'CreateProcess' or evt.name = 'SetThreadContext' or evt.category = 'registry'", &event.Event{Type: event.CreateProcess, Category: event.Process}, true, - func(t *testing.T, sexpr *SequenceExpr) { - assert.False(t, sexpr.bitsets.IsInitialized(event.TypeBitSet)) - assert.True(t, sexpr.bitsets.IsInitialized(event.BitmaskBitSet)) - assert.True(t, sexpr.bitsets.IsInitialized(event.CategoryBitSet)) - }, - }, + {"evt.name = 'CreateProcess'", &event.Event{Type: event.CreateProcess}, true}, + {"evt.name = 'CreateProcess'", &event.Event{Type: event.TerminateProcess}, false}, + {"evt.name = 'CreateProcess' or evt.name = 'TerminateThread'", &event.Event{Type: event.TerminateProcess}, false}, + {"evt.name = 'CreateProcess' or evt.category = 'object'", &event.Event{Type: event.TerminateProcess}, false}, + {"evt.name = 'CreateProcess' or evt.name = 'OpenProcess'", &event.Event{Type: event.OpenProcess}, true}, + {"evt.name = 'CreateProcess' or evt.name = 'CreateThread'", &event.Event{Type: event.CreateThread}, true}, + {"evt.name = 'CreateProcess' or evt.category = 'registry'", &event.Event{Type: event.RegSetValue}, true}, + {"evt.name = 'CreateProcess' or evt.name = 'OpenProcess' or evt.category = 'registry'", &event.Event{Type: event.OpenProcess}, true}, + {"evt.name = 'CreateProcess' or evt.name = 'SetThreadContext' or evt.category = 'registry'", &event.Event{Type: event.CreateProcess}, true}, } for _, tt := range tests { @@ -90,9 +54,6 @@ func TestSequenceExprIsEvaluable(t *testing.T) { sexpr.walk() assert.Equal(t, tt.isEval, sexpr.IsEvaluable(tt.evt)) - if tt.assertions != nil { - tt.assertions(t, sexpr) - } }) } } diff --git a/pkg/outputs/amqp/amqp_test.go b/pkg/outputs/amqp/amqp_test.go index 9a1539635..f7413f32c 100644 --- a/pkg/outputs/amqp/amqp_test.go +++ b/pkg/outputs/amqp/amqp_test.go @@ -21,11 +21,12 @@ package amqp import ( "encoding/json" "fmt" - "github.com/rabbitstack/fibratus/pkg/util/va" - "golang.org/x/sys/windows" "testing" "time" + "github.com/rabbitstack/fibratus/pkg/util/va" + "golang.org/x/sys/windows" + "github.com/phayes/freeport" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -194,16 +195,13 @@ func amqpURL(port int) string { //nolint:unused func getBatch() *event.Batch { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -262,16 +260,13 @@ func getBatch() *event.Batch { } evt1 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 459, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 459, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -330,16 +325,13 @@ func getBatch() *event.Batch { } evt2 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 829, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 829, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/outputs/elasticsearch/elasticsearch_test.go b/pkg/outputs/elasticsearch/elasticsearch_test.go index b956e8aba..d25c93dbb 100644 --- a/pkg/outputs/elasticsearch/elasticsearch_test.go +++ b/pkg/outputs/elasticsearch/elasticsearch_test.go @@ -21,8 +21,6 @@ package elasticsearch import ( "bytes" "encoding/json" - "github.com/rabbitstack/fibratus/pkg/util/va" - "golang.org/x/sys/windows" "io" "net/http" "net/http/httptest" @@ -30,6 +28,9 @@ import ( "testing" "time" + "github.com/rabbitstack/fibratus/pkg/util/va" + "golang.org/x/sys/windows" + "github.com/olivere/elastic/v7" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" @@ -161,16 +162,13 @@ func getBatch() *event.Batch { ts, _ := time.Parse(time.RFC3339, "2018-05-03T15:04:05.323Z") evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: ts, - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: ts, + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -229,16 +227,13 @@ func getBatch() *event.Batch { } evt1 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 459, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: ts, - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 459, + CPU: 1, + Seq: 2, + Timestamp: ts, + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -297,16 +292,13 @@ func getBatch() *event.Batch { } evt2 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 829, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: ts, - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 829, + CPU: 1, + Seq: 2, + Timestamp: ts, + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/outputs/http/http_test.go b/pkg/outputs/http/http_test.go index c3995b560..e46e18a30 100644 --- a/pkg/outputs/http/http_test.go +++ b/pkg/outputs/http/http_test.go @@ -21,9 +21,6 @@ package http import ( "compress/gzip" "encoding/json" - "github.com/rabbitstack/fibratus/pkg/outputs" - "github.com/rabbitstack/fibratus/pkg/util/va" - "golang.org/x/sys/windows" "io" "log" "net" @@ -32,6 +29,10 @@ import ( "testing" "time" + "github.com/rabbitstack/fibratus/pkg/outputs" + "github.com/rabbitstack/fibratus/pkg/util/va" + "golang.org/x/sys/windows" + "github.com/stretchr/testify/assert" "github.com/rabbitstack/fibratus/pkg/event" @@ -148,16 +149,13 @@ func TestHttpGzipPublish(t *testing.T) { func getBatch() *event.Batch { evt := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 859, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 859, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -216,16 +214,13 @@ func getBatch() *event.Batch { } evt1 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 459, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 459, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, @@ -284,16 +279,13 @@ func getBatch() *event.Batch { } evt2 := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: 829, - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: 829, + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "\\Device\\HarddiskVolume2\\Windows\\system32\\user32.dll"}, diff --git a/pkg/rules/_fixtures/field_values/correct_category_name_field.yml b/pkg/rules/_fixtures/field_values/correct_category_name_field.yml index 377b123cb..0970eb35a 100644 --- a/pkg/rules/_fixtures/field_values/correct_category_name_field.yml +++ b/pkg/rules/_fixtures/field_values/correct_category_name_field.yml @@ -1,5 +1,5 @@ name: match https connections id: 8f36f8e0-a5c2-498f-9563-eea306daa586 version: 1.0.0 -condition: evt.category = 'net' and net.dport = 443 +condition: evt.category = 'network' and net.dport = 443 min-engine-version: 2.0.0 diff --git a/pkg/rules/_fixtures/field_values/incorrect_category_name_field.yml b/pkg/rules/_fixtures/field_values/incorrect_category_name_field.yml index 0970eb35a..a3fa11c3c 100644 --- a/pkg/rules/_fixtures/field_values/incorrect_category_name_field.yml +++ b/pkg/rules/_fixtures/field_values/incorrect_category_name_field.yml @@ -1,5 +1,5 @@ name: match https connections id: 8f36f8e0-a5c2-498f-9563-eea306daa586 version: 1.0.0 -condition: evt.category = 'network' and net.dport = 443 +condition: evt.category = 'pipe' and net.dport = 443 min-engine-version: 2.0.0 diff --git a/pkg/rules/_fixtures/merged_filters/filter3.yml b/pkg/rules/_fixtures/merged_filters/filter3.yml index 66ac3f5e1..66fe167e8 100644 --- a/pkg/rules/_fixtures/merged_filters/filter3.yml +++ b/pkg/rules/_fixtures/merged_filters/filter3.yml @@ -1,5 +1,5 @@ name: match http connections id: 6f36f8e0-a5c2-498f-9563-eea306daa586 version: 1.0.0 -condition: evt.category = 'net' and net.dport = 80 +condition: evt.category = 'network' and net.dport = 80 min-engine-version: 2.0.0 diff --git a/pkg/rules/compiler.go b/pkg/rules/compiler.go index 65e76d7ed..a45a87bc4 100644 --- a/pkg/rules/compiler.go +++ b/pkg/rules/compiler.go @@ -125,7 +125,7 @@ func (c *compiler) compile() (map[*config.FilterConfig]filter.Filter, *config.Ru for _, v := range values { switch field { case fields.EvtName, fields.KevtName: - if !event.IsKnown(v) { + if !event.IsTypeKnown(v) { return nil, nil, ErrUnknownEventName(f.Name, v) } case fields.EvtCategory, fields.KevtCategory: @@ -266,7 +266,11 @@ func (c *compiler) containsEventTypes(root ql.Node, types ...event.Type) bool { evts := make([]event.Type, 0, len(vals)) for _, v := range vals { - evts = append(evts, event.NameToType(v)) + typ, ok := event.ParseType(v) + if !ok { + continue + } + evts = append(evts, typ) } for _, typ := range types { @@ -337,42 +341,46 @@ func (c *compiler) buildCompileResult(filters map[*config.FilterConfig]filter.Fi for name, values := range f.GetStringFields() { for _, v := range values { if name == fields.EvtName || name == fields.EvtCategory { - types := event.NameToTypes(v) - for _, typ := range types { - switch typ.Category() { - case event.Process: - rs.HasProcEvents = true - case event.Thread: - rs.HasThreadEvents = true - case event.Module: - rs.HasModuleEvents = true - case event.File: - rs.HasFileEvents = true - case event.Net: - rs.HasNetworkEvents = true - case event.Registry: - rs.HasRegistryEvents = true - case event.Mem: - rs.HasMemEvents = true - } - if typ.Subcategory() == event.DNS { - rs.HasDNSEvents = true - } - if typ == event.MapViewFile || typ == event.UnmapViewFile { - rs.HasVAMapEvents = true - } - if typ == event.OpenProcess || typ == event.OpenThread || typ == event.SetThreadContext || - typ == event.CreateSymbolicLinkObject { - rs.HasAuditAPIEvents = true - } - - if m[typ] { - continue - } - - events = append(events, typ) - m[typ] = true + typ, ok := event.ParseType(v) + if !ok { + continue + } + + info := event.GetTypeInfo(typ) + + switch info.Category { + case event.Process: + rs.HasProcEvents = true + case event.Thread: + rs.HasThreadEvents = true + case event.Module: + rs.HasModuleEvents = true + case event.File: + rs.HasFileEvents = true + case event.Network: + rs.HasNetworkEvents = true + case event.Registry: + rs.HasRegistryEvents = true + case event.Memory: + rs.HasMemEvents = true + } + if info.Subcategory == event.DNS { + rs.HasDNSEvents = true + } + if typ == event.MapViewOfSection || typ == event.UnmapViewOfSection { + rs.HasVAMapEvents = true } + if typ == event.OpenProcess || typ == event.OpenThread || typ == event.SetThreadContext || + typ == event.CreateSymbolicLinkObject { + rs.HasAuditAPIEvents = true + } + + if m[typ] { + continue + } + + events = append(events, typ) + m[typ] = true } } } diff --git a/pkg/rules/compiler_test.go b/pkg/rules/compiler_test.go index 404bf0c64..4bb2ab9bc 100644 --- a/pkg/rules/compiler_test.go +++ b/pkg/rules/compiler_test.go @@ -44,8 +44,7 @@ func TestCompile(t *testing.T) { assert.Contains(t, rs.UsedEvents, event.CreateProcess) assert.Contains(t, rs.UsedEvents, event.LoadModule) assert.Contains(t, rs.UsedEvents, event.QueryDNS) - assert.Contains(t, rs.UsedEvents, event.ConnectTCPv4) - assert.Contains(t, rs.UsedEvents, event.ConnectTCPv6) + assert.Contains(t, rs.UsedEvents, event.Connect) } func TestCompileMinEngineVersion(t *testing.T) { @@ -82,7 +81,7 @@ func TestCompileEventCategoryFieldNames(t *testing.T) { {"_fixtures/field_values/incorrect_event_name_field.yml", ErrUnknownEventName("match https connections", "RecvTcp4")}, {"_fixtures/field_values/incorrect_event_name_in_operator.yml", ErrUnknownEventName("match https connections", "CreateProc")}, {"_fixtures/field_values/correct_category_name_field.yml", nil}, - {"_fixtures/field_values/incorrect_category_name_field.yml", ErrUnknownCategoryName("match https connections", "network")}, + {"_fixtures/field_values/incorrect_category_name_field.yml", ErrUnknownCategoryName("match https connections", "pipe")}, } for _, tt := range tests { diff --git a/pkg/rules/engine.go b/pkg/rules/engine.go index cd0f4974a..618880af8 100644 --- a/pkg/rules/engine.go +++ b/pkg/rules/engine.go @@ -81,13 +81,13 @@ type compiledFilter struct { // filterset contains compiled filters indexed by event type and category. type filterset struct { types map[event.Type][]*compiledFilter - categories map[uint8][]*compiledFilter + categories map[event.Category][]*compiledFilter } func newFilterset() *filterset { fs := &filterset{ types: make(map[event.Type][]*compiledFilter), - categories: make(map[uint8][]*compiledFilter), + categories: make(map[event.Category][]*compiledFilter), } return fs } @@ -100,7 +100,7 @@ func (f *filterset) collect(e *event.Event) []*compiledFilter { if len(f.categories) == 0 { return f.types[e.Type] } - return append(f.types[e.Type], f.categories[e.Category.Index()]...) + return append(f.types[e.Type], f.categories[e.Category()]...) } func newCompiledFilter(f filter.Filter, c *config.FilterConfig, ss *sequenceState) *compiledFilter { @@ -196,12 +196,17 @@ func (e *Engine) Compile() (*config.RulesCompileResult, error) { for _, v := range values { switch name { case fields.EvtName: - for _, typ := range event.NameToTypes(v) { - e.filters.types[typ] = append(e.filters.types[typ], fltr) + typ, ok := event.ParseType(v) + if !ok { + continue } + e.filters.types[typ] = append(e.filters.types[typ], fltr) case fields.EvtCategory: - category := event.Category(v) - e.filters.categories[category.Index()] = append(e.filters.categories[category.Index()], fltr) + category, ok := event.ParseCategory(v) + if !ok { + continue + } + e.filters.categories[category] = append(e.filters.categories[category], fltr) } } } diff --git a/pkg/rules/engine_test.go b/pkg/rules/engine_test.go index f3d649052..96974545c 100644 --- a/pkg/rules/engine_test.go +++ b/pkg/rules/engine_test.go @@ -116,11 +116,9 @@ func wrapProcessEvent(e *event.Event, fn func(*event.Event) (bool, error)) bool func fireRules(t *testing.T, c *config.Config) bool { e := NewEngine(new(ps.SnapshotterMock), c) evt := &event.Event{ - Type: event.RecvTCPv4, - Name: "Recv", - Tid: 2484, - PID: 859, - Category: event.Net, + Type: event.Recv, + Tid: 2484, + PID: 859, Params: event.Params{ params.NetDport: {Name: params.NetDport, Type: params.Uint16, Value: uint16(443)}, params.NetSport: {Name: params.NetSport, Type: params.Uint16, Value: uint16(43123)}, @@ -141,7 +139,7 @@ func TestCompileIndexableFilters(t *testing.T) { compileRules(t, e) - assert.Len(t, e.filters.types, 5) + assert.Len(t, e.filters.types, 2) assert.Len(t, e.filters.categories, 1) var tests = []struct { @@ -149,10 +147,7 @@ func TestCompileIndexableFilters(t *testing.T) { wants int }{ {&event.Event{Type: event.CreateProcess}, 2}, - {&event.Event{Type: event.RecvUDPv6}, 3}, - {&event.Event{Type: event.RecvTCPv4}, 3}, - {&event.Event{Type: event.RecvTCPv4, Category: event.Net}, 4}, - {&event.Event{Category: event.Net}, 1}, + {&event.Event{Type: event.Recv}, 4}, } for _, tt := range tests { @@ -189,8 +184,6 @@ func TestRunSequenceRule(t *testing.T) { Seq: 1, Type: event.CreateProcess, Timestamp: time.Now(), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 2243, PS: &types.PS{ @@ -208,10 +201,8 @@ func TestRunSequenceRule(t *testing.T) { Seq: 2, Type: event.CreateFile, Timestamp: time.Now().Add(time.Millisecond * 250), - Name: "CreateFile", Tid: 2484, PID: 2243, - Category: event.File, PS: &types.PS{ Name: "firefox.exe", Exe: "C:\\Program Files\\Mozilla Firefox\\firefox.exe", @@ -226,10 +217,8 @@ func TestRunSequenceRule(t *testing.T) { e3 := &event.Event{ Seq: 4, - Type: event.ConnectTCPv4, + Type: event.Connect, Timestamp: time.Now().Add(time.Second), - Category: event.Net, - Name: "Connect", Tid: 244, PID: 2243, PS: &types.PS{ @@ -279,8 +268,6 @@ func TestRunSequenceRuleWithPsUUIDLink(t *testing.T) { Seq: 1, Type: event.CreateProcess, Timestamp: time.Now(), - Category: event.Process, - Name: "CreateProcess", Tid: 2243, PID: uint32(os.Getpid()), PS: &types.PS{ @@ -299,10 +286,8 @@ func TestRunSequenceRuleWithPsUUIDLink(t *testing.T) { Seq: 2, Type: event.CreateFile, Timestamp: time.Now().Add(time.Second), - Name: "CreateFile", Tid: 2484, PID: uint32(os.Getpid()), - Category: event.File, PS: &types.PS{ PID: uint32(os.Getpid()), Name: "firefox.exe", @@ -342,8 +327,6 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { Seq: 1, Type: event.CreateProcess, Timestamp: time.Now(), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 2243, PS: &types.PS{ @@ -360,10 +343,8 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { Seq: 2, Type: event.CreateFile, Timestamp: time.Now().Add(time.Millisecond * 544), - Name: "CreateFile", Tid: 2484, PID: 2243, - Category: event.File, PS: &types.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\cmd.exe", @@ -378,8 +359,6 @@ func TestRunSimpleAndSequenceRules(t *testing.T) { Seq: 10, Type: event.CreateProcess, Timestamp: time.Now().Add(time.Second * 2), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 2243, PS: &types.PS{ @@ -427,11 +406,9 @@ func TestAlertAction(t *testing.T) { compileRules(t, e) evt := &event.Event{ - Type: event.RecvTCPv4, - Name: "Recv", - Tid: 2484, - PID: 859, - Category: event.Net, + Type: event.Recv, + Tid: 2484, + PID: 859, PS: &types.PS{ Name: "cmd.exe", }, @@ -489,10 +466,8 @@ func TestKillAction(t *testing.T) { evt := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now(), - Name: "CreateProcess", Tid: 2484, PID: pi.ProcessId, - Category: event.Process, PS: &types.PS{ Name: "calc.exe", Exe: "C:\\Windows\\system32\\calc.exe", @@ -520,11 +495,9 @@ func BenchmarkRunRules(b *testing.B) { evts := []*event.Event{ { - Type: event.ConnectTCPv4, - Name: "Recv", - Tid: 2484, - PID: 859, - Category: event.Net, + Type: event.Connect, + Tid: 2484, + PID: 859, PS: &types.PS{ Name: "cmd.exe", }, @@ -537,11 +510,9 @@ func BenchmarkRunRules(b *testing.B) { Metadata: make(map[event.MetadataKey]any), }, { - Type: event.CreateProcess, - Name: "CreateProcess", - Category: event.Process, - Tid: 2484, - PID: 859, + Type: event.CreateProcess, + Tid: 2484, + PID: 859, PS: &types.PS{ Name: "powershell.exe", }, @@ -556,11 +527,9 @@ func BenchmarkRunRules(b *testing.B) { Metadata: make(map[event.MetadataKey]any), }, { - Type: event.CreateFile, - Name: "CreateFile", - Category: event.File, - Tid: 2484, - PID: 859, + Type: event.CreateFile, + Tid: 2484, + PID: 859, PS: &types.PS{ Name: "powershell.exe", }, diff --git a/pkg/rules/sequence_test.go b/pkg/rules/sequence_test.go index eb6397f55..dcf475307 100644 --- a/pkg/rules/sequence_test.go +++ b/pkg/rules/sequence_test.go @@ -66,7 +66,6 @@ func TestSequenceState(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, - Name: "CreateProcess", Tid: 2484, PID: 859, Timestamp: time.Now(), @@ -82,7 +81,6 @@ func TestSequenceState(t *testing.T) { e2 := &event.Event{ Type: event.CreateFile, - Name: "CreateFile", Tid: 2484, PID: 4143, Timestamp: time.Now().Add(time.Second * 5), @@ -108,7 +106,6 @@ func TestSequenceState(t *testing.T) { e3 := &event.Event{ Type: event.CreateProcess, - Name: "CreateProcess", Timestamp: time.Now().Add(time.Second * 10), Tid: 2484, PID: 4143, @@ -171,7 +168,6 @@ func TestSequenceState(t *testing.T) { // expire entire sequence e4 := &event.Event{ Type: event.TerminateProcess, - Name: "TerminateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -212,7 +208,6 @@ func TestSimpleSequence(t *testing.T) { }{ {[]*event.Event{{ Type: event.CreateProcess, - Name: "CreateProcess", Timestamp: time.Now(), Tid: 2484, PID: 859, @@ -226,11 +221,9 @@ func TestSimpleSequence(t *testing.T) { Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, }, { Type: event.CreateFile, - Name: "CreateFile", Timestamp: time.Now().Add(time.Second), Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", }, @@ -240,7 +233,6 @@ func TestSimpleSequence(t *testing.T) { Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}}}, []bool{false, true}}, {[]*event.Event{{ Type: event.CreateProcess, - Name: "CreateProcess", Timestamp: time.Now(), Tid: 2484, PID: 859, @@ -254,11 +246,9 @@ func TestSimpleSequence(t *testing.T) { Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"}, }, { Type: event.CreateFile, - Name: "CreateFile", Timestamp: time.Now().Add(time.Second), Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", }, @@ -297,7 +287,6 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now().Add(time.Duration(i) * time.Millisecond), - Name: "CreateProcess", Tid: 2484, PID: pid % 2, PS: &pstypes.PS{ @@ -312,10 +301,8 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { e2 := &event.Event{ Type: event.CreateFile, Timestamp: time.Now().Add(time.Duration(i) * time.Millisecond * 2), - Name: "CreateFile", Tid: 2484, PID: pid * 2, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\cmd.exe", @@ -337,7 +324,6 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { Seq: 20, Type: event.CreateProcess, Timestamp: time.Now().Add(time.Second), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -357,10 +343,8 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) { Type: event.CreateFile, Seq: 22, Timestamp: time.Now().Add(time.Second * time.Duration(2)), - Name: "CreateFile", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\cmd.exe", @@ -404,7 +388,6 @@ func TestUnconstrainedSequenceMatches(t *testing.T) { Seq: 20, Type: event.CreateProcess, Timestamp: time.Now().Add(time.Second), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -424,7 +407,6 @@ func TestUnconstrainedSequenceMatches(t *testing.T) { Seq: 21, Type: event.CreateProcess, Timestamp: time.Now().Add(time.Second * 2), - Name: "CreateProcess", Tid: 2484, PID: 1859, PS: &pstypes.PS{ @@ -444,10 +426,8 @@ func TestUnconstrainedSequenceMatches(t *testing.T) { Type: event.CreateFile, Seq: 25, Timestamp: time.Now().Add(time.Second * 3), - Name: "CreateFile", Tid: 2484, PID: 3859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\cmd.exe", @@ -490,7 +470,6 @@ func TestSimpleSequenceDeadline(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now(), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -508,10 +487,8 @@ func TestSimpleSequenceDeadline(t *testing.T) { e2 := &event.Event{ Type: event.CreateFile, Timestamp: time.Now().Add(time.Millisecond * 200), - Name: "CreateFile", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\svchost.exe", @@ -562,7 +539,6 @@ func TestSequenceMultiLinks(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now(), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -580,10 +556,8 @@ func TestSequenceMultiLinks(t *testing.T) { e2 := &event.Event{ Type: event.CreateFile, Timestamp: time.Now().Add(time.Second), - Name: "CreateFile", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\svchost.exe", @@ -616,8 +590,6 @@ func TestComplexSequence(t *testing.T) { Seq: 1, Type: event.CreateProcess, Timestamp: time.Now(), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 2243, PS: &pstypes.PS{ @@ -637,10 +609,8 @@ func TestComplexSequence(t *testing.T) { Seq: 2, Type: event.CreateFile, Timestamp: time.Now().Add(time.Millisecond * 250), - Name: "CreateFile", Tid: 2484, PID: 2243, - Category: event.File, PS: &pstypes.PS{ Name: "firefox.exe", Exe: "C:\\Program Files\\Mozilla Firefox\\firefox.exe", @@ -660,10 +630,8 @@ func TestComplexSequence(t *testing.T) { e3 := &event.Event{ Seq: 4, - Type: event.ConnectTCPv4, + Type: event.Connect, Timestamp: time.Now().Add(time.Second), - Category: event.Net, - Name: "Connect", Tid: 244, PID: 2243, PS: &pstypes.PS{ @@ -710,10 +678,8 @@ func TestSequenceOOO(t *testing.T) { e1 := &event.Event{ Type: event.CreateFile, Timestamp: time.Now(), - Name: "CreateFile", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\rundll32.exe", @@ -732,7 +698,6 @@ func TestSequenceOOO(t *testing.T) { e2 := &event.Event{ Type: event.OpenProcess, Timestamp: time.Now(), - Name: "OpenProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -771,7 +736,6 @@ func TestSequenceGC(t *testing.T) { e := &event.Event{ Type: event.OpenProcess, Timestamp: time.Now(), - Name: "OpenProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -816,7 +780,6 @@ func TestSequenceExpire(t *testing.T) { { Type: event.OpenProcess, Timestamp: time.Now(), - Name: "OpenProcess", Tid: 2484, PID: 4143, PS: &pstypes.PS{ @@ -832,7 +795,6 @@ func TestSequenceExpire(t *testing.T) { }, { Type: event.TerminateProcess, - Name: "TerminateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -859,8 +821,6 @@ func TestSequenceExpire(t *testing.T) { Seq: 1, Type: event.CreateProcess, Timestamp: time.Now(), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 2243, PS: &pstypes.PS{ @@ -877,8 +837,6 @@ func TestSequenceExpire(t *testing.T) { Seq: 2, Type: event.CreateProcess, Timestamp: time.Now().Add(time.Second), - Category: event.Process, - Name: "CreateProcess", Tid: 2484, PID: 12243, PS: &pstypes.PS{ @@ -897,7 +855,6 @@ func TestSequenceExpire(t *testing.T) { }, { Type: event.TerminateProcess, - Name: "TerminateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -956,7 +913,6 @@ func TestSequenceBoundFields(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now(), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -973,7 +929,6 @@ func TestSequenceBoundFields(t *testing.T) { e2 := &event.Event{ Type: event.CreateProcess, Timestamp: time.Now().Add(time.Millisecond * 20), - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -990,10 +945,8 @@ func TestSequenceBoundFields(t *testing.T) { e3 := &event.Event{ Type: event.CreateFile, Timestamp: time.Now().Add(time.Second), - Name: "CreateFile", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\svchost.exe", @@ -1006,12 +959,10 @@ func TestSequenceBoundFields(t *testing.T) { } e4 := &event.Event{ - Type: event.ConnectTCPv4, + Type: event.Connect, Timestamp: time.Now().Add(time.Second * 3), - Name: "Connect", Tid: 2484, PID: 859, - Category: event.File, PS: &pstypes.PS{ Name: "cmd.exe", Exe: "C:\\Windows\\system32\\svchost.exe", @@ -1050,8 +1001,6 @@ func TestSequenceBoundFieldsWithFunctions(t *testing.T) { e1 := &event.Event{ Type: event.CreateFile, - Name: "CreateFile", - Category: event.File, Timestamp: time.Now(), Tid: 2484, PID: 859, @@ -1067,8 +1016,6 @@ func TestSequenceBoundFieldsWithFunctions(t *testing.T) { e2 := &event.Event{ Type: event.RegSetValue, - Name: "RegSetValue", - Category: event.Registry, Timestamp: time.Now().Add(time.Millisecond * 5), Tid: 2484, PID: 859, @@ -1112,7 +1059,6 @@ func TestIsExpressionEvaluable(t *testing.T) { e1 := &event.Event{ Type: event.CreateProcess, - Name: "CreateProcess", Tid: 2484, PID: 859, PS: &pstypes.PS{ @@ -1127,7 +1073,6 @@ func TestIsExpressionEvaluable(t *testing.T) { e2 := &event.Event{ Type: event.RenameFile, - Name: "RenameFile", Tid: 2484, PID: 859, PS: &pstypes.PS{ diff --git a/pkg/symbolize/symbolizer_test.go b/pkg/symbolize/symbolizer_test.go index 1d27b1886..506905880 100644 --- a/pkg/symbolize/symbolizer_test.go +++ b/pkg/symbolize/symbolizer_test.go @@ -143,16 +143,13 @@ func TestProcessCallstackPeExports(t *testing.T) { } e := &event.Event{ - Type: event.CreateFile, - Tid: 2484, - PID: uint32(os.Getpid()), - CPU: 1, - Seq: 2, - Name: "CreateFile", - Timestamp: time.Now(), - Category: event.File, - Host: "archrabbit", - Description: "Creates or opens a new file, directory, I/O device, pipe, console", + Type: event.CreateFile, + Tid: 2484, + PID: uint32(os.Getpid()), + CPU: 1, + Seq: 2, + Timestamp: time.Now(), + Host: "archrabbit", Params: event.Params{ params.FileObject: {Name: params.FileObject, Type: params.Uint64, Value: uint64(12456738026482168384)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Windows\\system32\\mimi.dll"}, @@ -205,9 +202,7 @@ func TestProcessCallstackPeExports(t *testing.T) { PID: uint32(os.Getpid()), CPU: 1, Seq: 2, - Name: "UnloadModule", Timestamp: time.Now(), - Category: event.Module, Params: event.Params{ params.ModuleBase: {Name: params.ModuleBase, Type: params.Address, Value: uint64(0x7ffb5d8e11c4)}, params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: `C:\Windows\System32\user32.dll`}, @@ -266,9 +261,7 @@ func TestProcessCallstack(t *testing.T) { PID: 2232, CPU: 1, Seq: 2, - Name: "CreatedProcess", Timestamp: time.Now(), - Category: event.Process, Host: "archrabbit", Params: event.Params{ params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: (uint32(os.Getpid()))}, @@ -289,9 +282,7 @@ func TestProcessCallstack(t *testing.T) { PID: 12345, CPU: 1, Seq: 3, - Name: "TerminateProcess", Timestamp: time.Now(), - Category: event.Process, Host: "archrabbit", Params: event.Params{ params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(os.Getpid())}, @@ -359,9 +350,7 @@ func TestKernelCallstackSymbolizationFromDriverStore(t *testing.T) { PID: 2232, CPU: 1, Seq: 2, - Name: "CreatedProcess", Timestamp: time.Now(), - Category: event.Process, Host: "archrabbit", Params: event.Params{ params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: uint32(os.Getpid())}, @@ -465,9 +454,7 @@ func TestSymbolizeEventParamAddress(t *testing.T) { PID: uint32(os.Getpid()), CPU: 1, Seq: 2, - Name: "CreateThread", Timestamp: time.Now(), - Category: event.Thread, Host: "archrabbit", Params: event.Params{ params.Callstack: {Name: params.Callstack, Type: params.Slice, Value: []va.Address{0x7ffb5c1d0396, 0x7ffb5d8e61f4, 0x7ffb3138592e, 0x7ffb313853b2, 0x2638e59e0a5}}, @@ -514,9 +501,7 @@ func TestProcessCallstackProcsTTL(t *testing.T) { PID: 1232, CPU: 1, Seq: 2, - Name: "CreatedProcess", Timestamp: time.Now().Add(time.Millisecond * time.Duration(n)), - Category: event.Process, Host: "archrabbit", Params: event.Params{ params.ProcessParentID: {Name: params.ProcessParentID, Type: params.PID, Value: (uint32(os.Getpid()))}, diff --git a/pkg/sys/etw/types.go b/pkg/sys/etw/types.go index cea293271..2d9fbaf53 100644 --- a/pkg/sys/etw/types.go +++ b/pkg/sys/etw/types.go @@ -64,6 +64,9 @@ const ImageKeyword = 0x40 // SetValueKeyword enables registry key value set events for Microsoft Windows Kernel Registry provider const SetValueKeyword = 0x100 +// CaptureRegistryValue is the undocumented ETW feature to enable captured data in RegSetValue events +var CaptureRegistryValue = 0x2 + const ( // EventHeaderExtTypeStackTrace64 indicates that the extended data contains the call stack if the event is captured on a 64-bit host EventHeaderExtTypeStackTrace64 uint16 = 0x0006 @@ -559,6 +562,11 @@ type ClassicEventID struct { _ [7]uint8 // reserved } +// IsEmpty indicates if event id has been initialized. +func (e ClassicEventID) IsEmpty() bool { + return (e.GUID.Data1 == 0 && e.GUID.Data2 == 0 && e.GUID.Data3 == 0 && len(e.GUID.Data4[:]) == 0) || e.Type == 0 +} + // EventFilterDescriptor defines the filter data that // a session passes to the provider's enable callback. type EventFilterDescriptor struct { @@ -674,32 +682,6 @@ func (e *EventRecord) Version() uint8 { return e.Header.EventDescriptor.Version } -// HookID returns either the opcode or the event ID. -func (e *EventRecord) HookID() uint16 { - if e.Header.EventDescriptor.Opcode > 0 { - return uint16(e.Header.EventDescriptor.Opcode) - } - return e.Header.EventDescriptor.ID -} - -// ID is an unsigned integer that uniquely -// identifies the event. Handy for bitmask -// operations. -func (e *EventRecord) ID() uint { - d1 := e.Header.ProviderID.Data1 - d2 := e.Header.ProviderID.Data2 - - id := uint(byte(d1>>24))<<56 | - uint(byte(d1>>16))<<48 | - uint(byte(d1>>8))<<40 | - uint(byte(d1))<<32 | - uint(byte(d2>>8))<<24 | - uint(byte(d2))<<16 | - uint(e.HookID()) - - return id -} - // Clone makes a copy of this event record and returns the // copy itself and the event buffer. The buffer must outlive // the event record instance. Both are drawn from pools. diff --git a/pkg/util/bitmap/bitmap.go b/pkg/util/bitmap/bitmap.go new file mode 100644 index 000000000..3ead349a2 --- /dev/null +++ b/pkg/util/bitmap/bitmap.go @@ -0,0 +1,39 @@ +/* + * Copyright 2021-2026 by Nedim Sabic Sabic + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed 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 bitmap + +// Bitmap is a set of bits backed by a uint64. The type parameter +// T represents the type used to identify bits. It may be any unsigned +// integer type or an alias of one. Valid bit values range from 0 to 63. +type Bitmap[T ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64] uint64 + +// Has reports whether the bit identified by b is set. +func (s Bitmap[T]) Has(b T) bool { + return s&(1< sequence maxspan 40s by ps.uuid, file.view.base - |unmap_view_file and + |unmap_view_of_section and file.view.type = 'PAGEFILE' and file.view.protection = 'READONLY' and (file.view.size = 12288 or (file.view.size = 4096 and not (ps.exe imatches '?:\\Windows\\explorer.exe') and @@ -38,8 +38,8 @@ condition: > not (ps.name iin ('procexp.exe', 'procexp64.exe') and ps.signature.trusted = true and ps.signature.subject imatches '*Microsoft Corporation*') ) | - |map_view_file and file.view.size = 12288 and file.view.type = 'PAGEFILE' and file.view.protection = 'READWRITE'| + |map_view_of_section and file.view.size = 12288 and file.view.type = 'PAGEFILE' and file.view.protection = 'READWRITE'| severity: high -min-engine-version: 3.0.0 +min-engine-version: 3.2.0 diff --git a/rules/defense_evasion_potential_ntdll_unhooking_via_file_mapping.yml b/rules/defense_evasion_potential_ntdll_unhooking_via_file_mapping.yml index d57bf6df9..99652e033 100644 --- a/rules/defense_evasion_potential_ntdll_unhooking_via_file_mapping.yml +++ b/rules/defense_evasion_potential_ntdll_unhooking_via_file_mapping.yml @@ -1,6 +1,6 @@ name: Potential NTDLL unhooking via file mapping id: b000955d-90df-44eb-8e32-8269d395f0ef -version: 1.0.1 +version: 1.0.2 description: | Identifies processes that map a fresh image view of NTDLL.dll from disk, a behavior commonly associated with user-mode API @@ -18,7 +18,7 @@ references: - https://github.com/hwbp/NTDLL-Unhook condition: > - map_view_file and + map_view_of_section and file.view.type = 'IMAGE' and evt.pid not in (0, 4) and file.path imatches ( @@ -36,4 +36,4 @@ condition: > severity: high -min-engine-version: 3.0.0 +min-engine-version: 3.2.0 diff --git a/rules/defense_evasion_potential_process_injection_via_tainted_memory_section.yml b/rules/defense_evasion_potential_process_injection_via_tainted_memory_section.yml index 89d877e76..2484ce030 100644 --- a/rules/defense_evasion_potential_process_injection_via_tainted_memory_section.yml +++ b/rules/defense_evasion_potential_process_injection_via_tainted_memory_section.yml @@ -1,6 +1,6 @@ name: Potential process injection via tainted memory section id: 8e4182f3-02e7-4e95-afc3-93d18c9a9c09 -version: 1.0.8 +version: 1.0.9 description: | Identifies potential process injection when the adversary creates and maps a memory section with RW protection rights followed by mapping of the same memory section in @@ -25,6 +25,7 @@ condition: > maxspan 1m |map_view_of_section and file.view.protection = 'READWRITE' and evt.pid != 4 and file.view.size >= 4096 and + file.view.type in ('IMAGE', 'IMAGE_NO_EXECUTE', 'PAGEFILE') and ps.exe not imatches ( '?:\\Program Files\\*.exe', @@ -52,4 +53,4 @@ condition: > action: - name: kill -min-engine-version: 3.0.0 +min-engine-version: 3.2.0 diff --git a/rules/defense_evasion_process_execution_from_hollowed_memory_section.yml b/rules/defense_evasion_process_execution_from_hollowed_memory_section.yml index b9e3f2374..b764c9b33 100644 --- a/rules/defense_evasion_process_execution_from_hollowed_memory_section.yml +++ b/rules/defense_evasion_process_execution_from_hollowed_memory_section.yml @@ -1,6 +1,6 @@ name: Process execution from hollowed memory section id: 2a3fbae8-5e8c-4b71-b9da-56c3958c0d53 -version: 2.1.4 +version: 2.1.5 description: | Adversaries may inject malicious code into suspended and hollowed processes in order to evade process-based defenses. Process hollowing is a method of executing arbitrary code @@ -23,7 +23,7 @@ condition: > maxspan 40s |unmap_view_of_section and evt.pid != 4 and ps.sid not in ('S-1-5-18', 'S-1-5-19', 'S-1-5-20') and - file.view.size > 20000 and file.view.protection != 'READONLY' and + file.view.type in ('IMAGE', 'IMAGE_NO_EXECUTE') and file.view.size > 20000 and file.view.protection != 'READONLY' and (file.name = '' or file.extension != '.dll') and ps.parent.exe not imatches ( @@ -47,4 +47,4 @@ output: > Process %2.ps.exe executed from hollowed memory section severity: high -min-engine-version: 3.0.0 +min-engine-version: 3.2.0 diff --git a/rules/defense_evasion_process_execution_from_remote_memory_section.yml b/rules/defense_evasion_process_execution_from_remote_memory_section.yml index 675f14035..d23446acf 100644 --- a/rules/defense_evasion_process_execution_from_remote_memory_section.yml +++ b/rules/defense_evasion_process_execution_from_remote_memory_section.yml @@ -1,6 +1,6 @@ name: Process execution from remote memory section id: 6e4cc918-a30e-4167-ba26-6356d6384f30 -version: 1.0.0 +version: 1.0.1 description: | Detects execution of a process image originating from a memory section mapped without a backing file, a strong indicator of advanced process @@ -21,9 +21,9 @@ references: condition: > sequence maxspan 1m - |map_view_file and + |map_view_of_section and evt.pid != 4 and ps.sid not in ('S-1-5-18', 'S-1-5-19', 'S-1-5-20') and - file.view.size > 50000 and file.path = '' and file.view.type = 'IMAGE' and + file.view.type in ('IMAGE', 'IMAGE_NO_EXECUTE') and file.view.size > 50000 and file.path = '' and file.view.type = 'IMAGE' and ps.exe not imatches ( '?:\\Windows\\System32\\dwm.exe', @@ -51,4 +51,4 @@ output: > Process %3.ps.exe executed from a remotely mapped memory section with no backing file severity: high -min-engine-version: 3.0.0 +min-engine-version: 3.2.0 diff --git a/rules/macros/macros.yml b/rules/macros/macros.yml index f4677eb4c..5205a48fd 100644 --- a/rules/macros/macros.yml +++ b/rules/macros/macros.yml @@ -91,17 +91,11 @@ - macro: virtual_free expr: evt.name = 'VirtualFree' -- macro: map_view_file - expr: evt.name = 'MapViewFile' - -- macro: unmap_view_file - expr: evt.name = 'UnmapViewFile' - - macro: map_view_of_section - expr: map_view_file and file.view.type in ('IMAGE', 'IMAGE_NO_EXECUTE', 'PAGEFILE') + expr: evt.name = 'MapViewOfSection' - macro: unmap_view_of_section - expr: unmap_view_file and file.view.type in ('IMAGE', 'IMAGE_NO_EXECUTE') + expr: evt.name = 'UnmapViewOfSection' - macro: query_dns expr: evt.name = 'QueryDns'