Skip to content

Commit

Permalink
feat(filter): Add additional file filter fields
Browse files Browse the repository at this point in the history
Introduce a set of filter fields that permit extracting
the file information class and the scalar values pertaining
to the Allocation, EOF, and Disposition info classes.
  • Loading branch information
rabbitstack committed Oct 24, 2024
1 parent 9219478 commit 28093e7
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 21 deletions.
14 changes: 14 additions & 0 deletions pkg/filter/accessor_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
54 changes: 33 additions & 21 deletions pkg/filter/fields/fields_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.info.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},
Expand Down
85 changes: 85 additions & 0 deletions pkg/filter/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions pkg/fs/file_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
20 changes: 20 additions & 0 deletions pkg/kevent/kparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 28093e7

Please sign in to comment.