diff --git a/pkg/filter/accessor_windows.go b/pkg/filter/accessor_windows.go index e6c868f4c..fcc6c4dec 100644 --- a/pkg/filter/accessor_windows.go +++ b/pkg/filter/accessor_windows.go @@ -21,6 +21,7 @@ package filter import ( "errors" "expvar" + "github.com/rabbitstack/fibratus/pkg/fs" "github.com/rabbitstack/fibratus/pkg/kevent/ktypes" "github.com/rabbitstack/fibratus/pkg/network" psnap "github.com/rabbitstack/fibratus/pkg/ps" @@ -799,6 +800,19 @@ func (l *fileAccessor) Get(f fields.Field, kevt *kevent.Kevent) (kparams.Value, return kevt.Kparams.GetPid() case fields.FileKey: return kevt.Kparams.GetUint64(kparams.FileKey) + case fields.FileInfoClass: + return kevt.GetParamAsString(kparams.FileInfoClass), nil + case fields.FileInfoAllocationSize: + if kevt.Kparams.TryGetUint32(kparams.FileInfoClass) == fs.AllocationClass { + return kevt.Kparams.GetUint64(kparams.FileExtraInfo) + } + case fields.FileInfoEOFSize: + if kevt.Kparams.TryGetUint32(kparams.FileInfoClass) == fs.EOFClass { + return kevt.Kparams.GetUint64(kparams.FileExtraInfo) + } + case fields.FileInfoIsDispositionDeleteFile: + return kevt.Kparams.TryGetUint32(kparams.FileInfoClass) == fs.DispositionClass && + kevt.Kparams.TryGetUint64(kparams.FileExtraInfo) > 0, nil } return nil, nil } diff --git a/pkg/filter/fields/fields_windows.go b/pkg/filter/fields/fields_windows.go index fd2183978..e136493c2 100644 --- a/pkg/filter/fields/fields_windows.go +++ b/pkg/filter/fields/fields_windows.go @@ -402,6 +402,14 @@ const ( FilePID Field = "file.pid" // FileKey represents the field that uniquely identifies the file object. FileKey Field = "file.key" + // FileInfoClass represents the field that identifies the file information class + FileInfoClass Field = "file.info_class" + // FileInfoAllocationSize represents the field that contains the file allocation size + FileInfoAllocationSize Field = "file.info.allocation_size" + // FileInfoEOFSize represents the field that contains the EOF size + FileInfoEOFSize Field = "file.info.eof_size" + // FileInfoIsDispositionDeleteFile represents the field that indicates if the file is deleted when its handle is closed + FileInfoIsDispositionDeleteFile Field = "file.info.is_disposition_delete_file" // RegistryKeyName represents the registry key name RegistryKeyName Field = "registry.key.name" @@ -734,27 +742,31 @@ var fields = map[Field]FieldInfo{ ImageIsExecutable: {ImageIsExecutable, "indicates if the loaded image is an executable", kparams.Bool, []string{"image.is_exec'"}, nil}, ImageIsDotnet: {ImageIsDotnet, "indicates if the loaded image is a .NET assembly", kparams.Bool, []string{"image.is_dotnet'"}, nil}, - FileObject: {FileObject, "file object address", kparams.Uint64, []string{"file.object = 18446738026482168384"}, nil}, - FileName: {FileName, "full file name", kparams.UnicodeString, []string{"file.name contains 'mimikatz'"}, nil}, - FileOperation: {FileOperation, "file operation", kparams.AnsiString, []string{"file.operation = 'open'"}, nil}, - FileShareMask: {FileShareMask, "file share mask", kparams.AnsiString, []string{"file.share.mask = 'rw-'"}, nil}, - FileIOSize: {FileIOSize, "file I/O size", kparams.Uint32, []string{"file.io.size > 512"}, nil}, - FileOffset: {FileOffset, "file offset", kparams.Uint64, []string{"file.offset = 1024"}, nil}, - FileType: {FileType, "file type", kparams.AnsiString, []string{"file.type = 'directory'"}, nil}, - FileExtension: {FileExtension, "file extension", kparams.AnsiString, []string{"file.extension = '.dll'"}, nil}, - FileAttributes: {FileAttributes, "file attributes", kparams.Slice, []string{"file.attributes in ('archive', 'hidden')"}, nil}, - FileStatus: {FileStatus, "file operation status message", kparams.UnicodeString, []string{"file.status != 'success'"}, nil}, - FileViewBase: {FileViewBase, "view base address", kparams.Address, []string{"file.view.base = '25d42170000'"}, nil}, - FileViewSize: {FileViewSize, "size of the mapped view", kparams.Uint64, []string{"file.view.size > 1024"}, nil}, - FileViewType: {FileViewType, "type of the mapped view section", kparams.Enum, []string{"file.view.type = 'IMAGE'"}, nil}, - FileViewProtection: {FileViewProtection, "protection rights of the section view", kparams.AnsiString, []string{"file.view.protection = 'READONLY'"}, nil}, - FileIsDriverMalicious: {FileIsDriverMalicious, "indicates if the dropped driver is malicious", kparams.Bool, []string{"file.is_driver_malicious"}, nil}, - FileIsDriverVulnerable: {FileIsDriverVulnerable, "indicates if the dropped driver is vulnerable", kparams.Bool, []string{"file.is_driver_vulnerable"}, nil}, - FileIsDLL: {FileIsDLL, "indicates if the created file is a DLL", kparams.Bool, []string{"file.is_dll'"}, nil}, - FileIsDriver: {FileIsDriver, "indicates if the created file is a driver", kparams.Bool, []string{"file.is_driver'"}, nil}, - FileIsExecutable: {FileIsExecutable, "indicates if the created file is an executable", kparams.Bool, []string{"file.is_exec'"}, nil}, - FilePID: {FilePID, "denotes the process id performing file operation", kparams.PID, []string{"file.pid = 4"}, nil}, - FileKey: {FileKey, "uniquely identifies the file object", kparams.Uint64, []string{"file.key = 12446738026482168384"}, nil}, + FileObject: {FileObject, "file object address", kparams.Uint64, []string{"file.object = 18446738026482168384"}, nil}, + FileName: {FileName, "full file name", kparams.UnicodeString, []string{"file.name contains 'mimikatz'"}, nil}, + FileOperation: {FileOperation, "file operation", kparams.AnsiString, []string{"file.operation = 'open'"}, nil}, + FileShareMask: {FileShareMask, "file share mask", kparams.AnsiString, []string{"file.share.mask = 'rw-'"}, nil}, + FileIOSize: {FileIOSize, "file I/O size", kparams.Uint32, []string{"file.io.size > 512"}, nil}, + FileOffset: {FileOffset, "file offset", kparams.Uint64, []string{"file.offset = 1024"}, nil}, + FileType: {FileType, "file type", kparams.AnsiString, []string{"file.type = 'directory'"}, nil}, + FileExtension: {FileExtension, "file extension", kparams.AnsiString, []string{"file.extension = '.dll'"}, nil}, + FileAttributes: {FileAttributes, "file attributes", kparams.Slice, []string{"file.attributes in ('archive', 'hidden')"}, nil}, + FileStatus: {FileStatus, "file operation status message", kparams.UnicodeString, []string{"file.status != 'success'"}, nil}, + FileViewBase: {FileViewBase, "view base address", kparams.Address, []string{"file.view.base = '25d42170000'"}, nil}, + FileViewSize: {FileViewSize, "size of the mapped view", kparams.Uint64, []string{"file.view.size > 1024"}, nil}, + FileViewType: {FileViewType, "type of the mapped view section", kparams.Enum, []string{"file.view.type = 'IMAGE'"}, nil}, + FileViewProtection: {FileViewProtection, "protection rights of the section view", kparams.AnsiString, []string{"file.view.protection = 'READONLY'"}, nil}, + FileIsDriverMalicious: {FileIsDriverMalicious, "indicates if the dropped driver is malicious", kparams.Bool, []string{"file.is_driver_malicious"}, nil}, + FileIsDriverVulnerable: {FileIsDriverVulnerable, "indicates if the dropped driver is vulnerable", kparams.Bool, []string{"file.is_driver_vulnerable"}, nil}, + FileIsDLL: {FileIsDLL, "indicates if the created file is a DLL", kparams.Bool, []string{"file.is_dll'"}, nil}, + FileIsDriver: {FileIsDriver, "indicates if the created file is a driver", kparams.Bool, []string{"file.is_driver'"}, nil}, + FileIsExecutable: {FileIsExecutable, "indicates if the created file is an executable", kparams.Bool, []string{"file.is_exec'"}, nil}, + FilePID: {FilePID, "denotes the process id performing file operation", kparams.PID, []string{"file.pid = 4"}, nil}, + FileKey: {FileKey, "uniquely identifies the file object", kparams.Uint64, []string{"file.key = 12446738026482168384"}, nil}, + FileInfoClass: {FileInfoClass, "identifies the file information class", kparams.Enum, []string{"file.info_class = 'Allocation'"}, nil}, + FileInfoAllocationSize: {FileInfoAllocationSize, "file allocation size", kparams.Uint64, []string{"file.info.allocation_size > 645400"}, nil}, + FileInfoEOFSize: {FileInfoEOFSize, "file EOF size", kparams.Uint64, []string{"file.info.eof_size > 1000"}, nil}, + FileInfoIsDispositionDeleteFile: {FileInfoIsDispositionDeleteFile, "indicates if the file is deleted when its handle is closed", kparams.Bool, []string{"file.is_disposition_file_delete = true"}, nil}, RegistryKeyName: {RegistryKeyName, "fully qualified key name", kparams.UnicodeString, []string{"registry.key.name contains 'HKEY_LOCAL_MACHINE'"}, nil}, RegistryKeyHandle: {RegistryKeyHandle, "registry key object address", kparams.Address, []string{"registry.key.handle = 'FFFFB905D60C2268'"}, nil}, diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index 094f1c21e..a2f430eb7 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -22,6 +22,7 @@ import ( "github.com/rabbitstack/fibratus/internal/etw/processors" "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/filter/fields" + "github.com/rabbitstack/fibratus/pkg/fs" "github.com/rabbitstack/fibratus/pkg/kevent" "github.com/rabbitstack/fibratus/pkg/kevent/kparams" "github.com/rabbitstack/fibratus/pkg/kevent/ktypes" @@ -456,6 +457,90 @@ func TestFileFilter(t *testing.T) { } } +func TestFileInfoFilter(t *testing.T) { + var tests = []struct { + f string + e *kevent.Kevent + matches bool + }{ + { + `file.info_class = 'Allocation'`, + &kevent.Kevent{ + Category: ktypes.File, + Type: ktypes.SetFileInformation, + Name: "SetFileInformation", + Kparams: kevent.Kparams{ + kparams.FileInfoClass: {Name: kparams.FileInfoClass, Type: kparams.Enum, Value: fs.AllocationClass, Enum: fs.FileInfoClasses}, + }, + }, + true, + }, + { + `file.info.allocation_size = 64500`, + &kevent.Kevent{ + Category: ktypes.File, + Type: ktypes.SetFileInformation, + Name: "SetFileInformation", + Kparams: kevent.Kparams{ + kparams.FileInfoClass: {Name: kparams.FileInfoClass, Type: kparams.Enum, Value: fs.AllocationClass, Enum: fs.FileInfoClasses}, + kparams.FileExtraInfo: {Name: kparams.FileExtraInfo, Type: kparams.Uint64, Value: uint64(64500)}, + }, + }, + true, + }, + { + `file.info.eof_size = 64500`, + &kevent.Kevent{ + Category: ktypes.File, + Type: ktypes.SetFileInformation, + Name: "SetFileInformation", + Kparams: kevent.Kparams{ + kparams.FileInfoClass: {Name: kparams.FileInfoClass, Type: kparams.Enum, Value: fs.EOFClass, Enum: fs.FileInfoClasses}, + kparams.FileExtraInfo: {Name: kparams.FileExtraInfo, Type: kparams.Uint64, Value: uint64(64500)}, + }, + }, + true, + }, + { + `file.info.eof_size = 64500`, + &kevent.Kevent{ + Category: ktypes.File, + Type: ktypes.SetFileInformation, + Name: "SetFileInformation", + Kparams: kevent.Kparams{ + kparams.FileInfoClass: {Name: kparams.FileInfoClass, Type: kparams.Enum, Value: fs.DispositionClass, Enum: fs.FileInfoClasses}, + kparams.FileExtraInfo: {Name: kparams.FileExtraInfo, Type: kparams.Uint64, Value: uint64(1)}, + }, + }, + false, + }, + { + `file.info.is_disposition_delete_file = true`, + &kevent.Kevent{ + Category: ktypes.File, + Type: ktypes.DeleteFile, + Name: "DeleteFile", + Kparams: kevent.Kparams{ + kparams.FileInfoClass: {Name: kparams.FileInfoClass, Type: kparams.Enum, Value: fs.DispositionClass, Enum: fs.FileInfoClasses}, + kparams.FileExtraInfo: {Name: kparams.FileExtraInfo, Type: kparams.Uint64, Value: uint64(1)}, + }, + }, + true, + }, + } + + for _, tt := range tests { + t.Run(tt.f, func(t *testing.T) { + f := New(tt.f, cfg) + err := f.Compile() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, tt.matches, f.Run(tt.e)) + }) + } +} + func TestKeventFilter(t *testing.T) { kevt := &kevent.Kevent{ Type: ktypes.CreateFile, diff --git a/pkg/fs/file_class.go b/pkg/fs/file_class.go index dfe379fa0..bef4b028b 100644 --- a/pkg/fs/file_class.go +++ b/pkg/fs/file_class.go @@ -18,6 +18,12 @@ package fs +const ( + DispositionClass uint32 = 13 + AllocationClass uint32 = 19 + EOFClass uint32 = 20 +) + // FileInfoClasses contains the values that specify which structure to use to query or set information for a file object. // For more information see https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class var FileInfoClasses = map[uint32]string{ diff --git a/pkg/kevent/kparam.go b/pkg/kevent/kparam.go index fa135890c..0178af6ca 100644 --- a/pkg/kevent/kparam.go +++ b/pkg/kevent/kparam.go @@ -444,6 +444,16 @@ func (kpars Kparams) MustGetUint32(name string) uint32 { return v } +// TryGetUint32 tries to retrieve the uint32 value from the parameter. +// Returns the underlying value on success, or zero otherwise. +func (kpars Kparams) TryGetUint32(name string) uint32 { + val, err := kpars.GetUint32(name) + if err != nil { + return 0 + } + return val +} + // GetInt32 returns the underlying int32 value from the parameter. func (kpars Kparams) GetInt32(name string) (int32, error) { kpar, err := kpars.findParam(name) @@ -480,6 +490,16 @@ func (kpars Kparams) MustGetUint64(name string) uint64 { return v } +// TryGetUint64 tries to retrieve the uint64 value from the parameter. +// Returns the underlying value on success, or zero otherwise. +func (kpars Kparams) TryGetUint64(name string) uint64 { + val, err := kpars.GetUint64(name) + if err != nil { + return 0 + } + return val +} + // GetInt64 returns the underlying int64 value from the parameter. func (kpars Kparams) GetInt64(name string) (int64, error) { kpar, err := kpars.findParam(name)