Skip to content

Commit

Permalink
feat(kevent): Map and unmap view file events (rabbitstack#172)
Browse files Browse the repository at this point in the history
* MapViewFile/UnmapViewFile event parsing

* Initial support for MapFileView/UnmapFileView events

* Augment UnmapViewFile events with filename, adjust failing tests and print MapViewFile events in tests to diagnose failures

* Fix MapViewFile event test by avoiding unmapping the section
  • Loading branch information
rabbitstack authored Jun 14, 2023
1 parent 2a000a8 commit 7480862
Show file tree
Hide file tree
Showing 26 changed files with 389 additions and 33 deletions.
6 changes: 6 additions & 0 deletions pkg/filter/accessor_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,12 @@ func (l *fileAccessor) get(f fields.Field, kevt *kevent.Kevent) (kparams.Value,
return nil, nil
}
return kevt.GetParamAsString(kparams.NTStatus), nil
case fields.FileViewBase:
return kevt.GetParamAsString(kparams.FileViewBase), nil
case fields.FileViewSize:
return kevt.Kparams.GetUint64(kparams.FileViewSize)
case fields.FileViewType:
return kevt.GetParamAsString(kparams.FileViewSectionType), nil
}
return nil, nil
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/filter/fields/fields_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ const (
FileAttributes Field = "file.attributes"
// FileStatus represents the status message of the file operation
FileStatus Field = "file.status"
// FileViewBase represents the base address of the mapped view
FileViewBase Field = "file.view.base"
// FileViewSize represents the size of the mapped view
FileViewSize Field = "file.view.size"
// FileViewType represents the type of the mapped view section
FileViewType Field = "file.view.type"

// RegistryKeyName represents the registry key name
RegistryKeyName Field = "registry.key.name"
Expand Down Expand Up @@ -528,6 +534,9 @@ var fields = map[Field]FieldInfo{
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},

RegistryKeyName: {RegistryKeyName, "fully qualified key name", kparams.UnicodeString, []string{"registry.key.name contains 'HKEY_LOCAL_MACHINE'"}, nil},
RegistryKeyHandle: {RegistryKeyHandle, "registry key object address", kparams.HexInt64, []string{"registry.key.handle = 'FFFFB905D60C2268'"}, nil},
Expand Down
4 changes: 2 additions & 2 deletions pkg/filter/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,9 +633,9 @@ func TestMemFilter(t *testing.T) {
kpars := kevent.Kparams{
kparams.MemRegionSize: {Name: kparams.MemRegionSize, Type: kparams.Uint64, Value: uint64(8192)},
kparams.MemBaseAddress: {Name: kparams.MemBaseAddress, Type: kparams.Address, Value: uint64(1311246336000)},
kparams.MemAllocType: {Name: kparams.MemAllocType, Type: kparams.Flags, Value: uint32(0x00001000 | 0x00002000), Flags: kevent.MemAllocationTypeFlags},
kparams.MemAllocType: {Name: kparams.MemAllocType, Type: kparams.Flags, Value: uint32(0x00001000 | 0x00002000), Flags: kevent.MemAllocationFlags},
kparams.ProcessID: {Name: kparams.ProcessID, Type: kparams.Uint32, Value: uint32(345)},
kparams.MemProtect: {Name: kparams.MemProtect, Type: kparams.Flags, Value: uint32(0x40), Flags: kevent.MemAllocationProtectFlags},
kparams.MemProtect: {Name: kparams.MemProtect, Type: kparams.Flags, Value: uint32(0x40), Flags: kevent.MemProtectionFlags},
kparams.MemProtectMask: {Name: kparams.MemProtectMask, Type: kparams.AnsiString, Value: "RWX"},
kparams.MemPageType: {Name: kparams.MemPageType, Type: kparams.Enum, Value: uint32(0x1000000), Enum: processors.MemPageTypes},
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/kevent/enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 kevent

import "github.com/rabbitstack/fibratus/pkg/util/va"

// ViewSectionTypes describes possible values for process mapped sections.
var ViewSectionTypes = ParamEnum{
va.SectionData: "DATA",
va.SectionImage: "IMAGE",
va.SectionImageNoExecute: "IMAGE_NO_EXECUTE",
va.SectionPagefile: "PAGEFILE",
va.SectionPhysical: "PHYSICAL",
}
23 changes: 19 additions & 4 deletions pkg/kevent/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ var FileShareModeFlags = []ParamFlag{
{"DELETE", windows.FILE_SHARE_DELETE},
}

// MemAllocationTypeFlags describes virtual allocation/free type flags
var MemAllocationTypeFlags = []ParamFlag{
// MemAllocationFlags describes virtual allocation/free type flags
var MemAllocationFlags = []ParamFlag{
{"COMMIT", windows.MEM_COMMIT},
{"RESERVE", windows.MEM_RESERVE},
{"RESET", windows.MEM_RESET},
Expand All @@ -172,8 +172,8 @@ var MemAllocationTypeFlags = []ParamFlag{
{"WRITE_WATCH", windows.MEM_WRITE_WATCH},
}

// MemAllocationProtectFlags represents memory protection option flags.
var MemAllocationProtectFlags = []ParamFlag{
// MemProtectionFlags represents memory protection option flags.
var MemProtectionFlags = []ParamFlag{
{"NONE", 0},
{"EXECUTE", windows.PAGE_EXECUTE},
{"EXECUTE_READ", windows.PAGE_EXECUTE_READ},
Expand All @@ -189,3 +189,18 @@ var MemAllocationProtectFlags = []ParamFlag{
{"NOCACHE", windows.PAGE_NOCACHE},
{"WRITECOMBINE", windows.PAGE_WRITECOMBINE},
}

// ViewProtectionFlags describes section protection flags. These
// have different values than the memory protection flags as they
// are reported by the kernel.
var ViewProtectionFlags = []ParamFlag{
{"EXECUTE_READWRITE", 0x60000},
{"EXECUTE_WRITECOPY", 0x70000},
{"NOCACHE", 0x80000},
{"WRITECOMBINE", 0x90000},
{"READONLY", 0x10000},
{"EXECUTE", 0x20000},
{"EXECUTE_READ", 0x30000},
{"READWRITE", 0x40000},
{"WRITECOPY", 0x50000},
}
22 changes: 22 additions & 0 deletions pkg/kevent/kevent_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ func (e Kevent) IsFileOpEnd() bool { return e.Type == ktypes.FileOpEnd }
func (e Kevent) IsRegSetValue() bool { return e.Type == ktypes.RegSetValue }
func (e Kevent) IsProcessRundown() bool { return e.Type == ktypes.ProcessRundown }
func (e Kevent) IsVirtualAlloc() bool { return e.Type == ktypes.VirtualAlloc }
func (e Kevent) IsMapViewFile() bool { return e.Type == ktypes.MapViewFile }
func (e Kevent) IsUnmapViewFile() bool { return e.Type == ktypes.UnmapViewFile }

// InvalidPid indicates if the process generating the event is invalid.
func (e Kevent) InvalidPid() bool { return e.PID == sys.InvalidProcessID }
Expand Down Expand Up @@ -235,6 +237,13 @@ func (e Kevent) RundownKey() uint64 {
fileObject, _ := e.Kparams.GetUint64(kparams.FileObject)
binary.LittleEndian.PutUint64(b, fileObject)

return hashers.FnvUint64(b)
case ktypes.MapFileRundown:
b := make([]byte, 12)
fileKey, _ := e.Kparams.GetUint64(kparams.FileKey)
binary.LittleEndian.PutUint32(b, e.PID)
binary.LittleEndian.PutUint64(b, fileKey)

return hashers.FnvUint64(b)
case ktypes.RegKCBRundown:
key, _ := e.Kparams.GetString(kparams.RegKeyName)
Expand All @@ -260,6 +269,13 @@ func (e Kevent) PartialKey() uint64 {
binary.LittleEndian.PutUint32(b, e.PID)
binary.LittleEndian.PutUint64(b, object)

return hashers.FnvUint64(b)
case ktypes.MapFileRundown, ktypes.UnmapViewFile:
b := make([]byte, 12)
fileKey, _ := e.Kparams.GetUint64(kparams.FileKey)
binary.LittleEndian.PutUint32(b, e.PID)
binary.LittleEndian.PutUint64(b, fileKey)

return hashers.FnvUint64(b)
case ktypes.CreateFile:
file, _ := e.Kparams.GetString(kparams.FileName)
Expand Down Expand Up @@ -501,6 +517,12 @@ func (e *Kevent) Summary() string {
case ktypes.VirtualFree:
addr := e.GetParamAsString(kparams.MemBaseAddress)
return printSummary(e, fmt.Sprintf("released memory at <code>%s</code> address", addr))
case ktypes.MapViewFile:
sec := e.GetParamAsString(kparams.FileViewSectionType)
return printSummary(e, fmt.Sprintf("mapped view of <code>%s</code> section", sec))
case ktypes.UnmapViewFile:
sec := e.GetParamAsString(kparams.FileViewSectionType)
return printSummary(e, fmt.Sprintf("unmapped view of <code>%s</code> section", sec))
case ktypes.DuplicateHandle:
handleType := e.GetParamAsString(kparams.HandleObjectTypeID)
return printSummary(e, fmt.Sprintf("duplicated <code>%s</code> handle", handleType))
Expand Down
25 changes: 24 additions & 1 deletion pkg/kevent/kparam_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,29 @@ func (e *Kevent) produceParams(evt *etw.EventRecord) {
e.AppendParam(kparams.FileKey, kparams.Address, fileKey)
e.AppendParam(kparams.FileName, kparams.UnicodeString, filename)
e.AppendParam(kparams.FileInfoClass, kparams.Enum, infoClass, WithEnum(fs.FileInfoClasses))
case ktypes.MapViewFile, ktypes.UnmapViewFile, ktypes.MapFileRundown:
var (
viewBase uint64
fileKey uint64
extraInfo uint64
viewSize uint64
offset uint64
)
viewBase = evt.ReadUint64(0)
fileKey = evt.ReadUint64(8)
extraInfo = evt.ReadUint64(16)
viewSize = evt.ReadUint64(24)
if evt.Version() >= 3 {
offset = evt.ReadUint64(32)
}
protect := uint32(extraInfo >> 32)
section := uint32(extraInfo >> 52)
e.AppendParam(kparams.FileViewBase, kparams.Address, viewBase)
e.AppendParam(kparams.FileKey, kparams.Address, fileKey)
e.AppendParam(kparams.FileViewSize, kparams.Uint64, viewSize)
e.AppendParam(kparams.FileOffset, kparams.Uint64, offset)
e.AppendParam(kparams.MemProtect, kparams.Flags, protect, WithFlags(ViewProtectionFlags))
e.AppendParam(kparams.FileViewSectionType, kparams.Enum, section, WithEnum(ViewSectionTypes))
case ktypes.SendTCPv4,
ktypes.SendUDPv4,
ktypes.RecvTCPv4,
Expand Down Expand Up @@ -659,6 +682,6 @@ func (e *Kevent) produceParams(evt *etw.EventRecord) {
e.AppendParam(kparams.MemBaseAddress, kparams.Address, baseAddress)
e.AppendParam(kparams.MemRegionSize, kparams.Uint64, regionSize)
e.AppendParam(kparams.ProcessID, kparams.PID, pid)
e.AppendParam(kparams.MemAllocType, kparams.Flags, flags, WithFlags(MemAllocationTypeFlags))
e.AppendParam(kparams.MemAllocType, kparams.Flags, flags, WithFlags(MemAllocationFlags))
}
}
7 changes: 7 additions & 0 deletions pkg/kevent/kparams/fields_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ const (
// FileExtraInfo is the parameter that represents extra information returned by the file system for the operation. For example for a read request, the actual number of bytes that were read.
FileExtraInfo = "extra_info"

// FileViewBase is the parameter that represents the base address of the mapped section.
FileViewBase = "view_base"
// FileViewSize is the parameter that represents the size of the mapped section.
FileViewSize = "view_size"
// FileViewSectionType is the parameter that represents the mapped section type.
FileViewSectionType = "section_type"

// RegKeyHandle identifies the parameter name for the registry key handle.
RegKeyHandle = "key_handle"
// RegKeyName represents the parameter name for the fully qualified key name.
Expand Down
20 changes: 19 additions & 1 deletion pkg/kevent/ktypes/ktypes_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ var (
// OpenThread identifies the kernel events that are triggered when the process acquires a thread handle
OpenThread = pack(windows.GUID{Data1: 0xe02a841c, Data2: 0x75a3, Data3: 0x4fa7, Data4: [8]byte{0xaf, 0xc8, 0xae, 0x09, 0xcf, 0x9b, 0x7f, 0x23}}, 6)

// MapViewFile represents events that map a view of a file mapping into the address space of a calling process
MapViewFile = pack(windows.GUID{Data1: 0x90cbdc39, Data2: 0x4a3e, Data3: 0x11d1, Data4: [8]byte{0x84, 0xf4, 0x0, 0x0, 0xf8, 0x04, 0x64, 0xe3}}, 37)
// UnmapViewFile represents events that unmap a view of a file mapping from the address space of a calling process
UnmapViewFile = pack(windows.GUID{Data1: 0x90cbdc39, Data2: 0x4a3e, Data3: 0x11d1, Data4: [8]byte{0x84, 0xf4, 0x0, 0x0, 0xf8, 0x04, 0x64, 0xe3}}, 38)
// MapFileRundown represents the event that is emitted at the start of the tracing session to enumerate I/O mapped files
MapFileRundown = pack(windows.GUID{Data1: 0x90cbdc39, Data2: 0x4a3e, Data3: 0x11d1, Data4: [8]byte{0x84, 0xf4, 0x0, 0x0, 0xf8, 0x04, 0x64, 0xe3}}, 39)

// FileRundown events are generated by kernel rundown logger to enumerate all open files on the start of the kernel session
FileRundown = pack(windows.GUID{Data1: 0x90cbdc39, Data2: 0x4a3e, Data3: 0x11d1, Data4: [8]byte{0x84, 0xf4, 0x0, 0x0, 0xf8, 0x04, 0x64, 0xe3}}, 36)
// CreateFile represents events that create/open a file or I/O device
Expand Down Expand Up @@ -207,6 +214,12 @@ func (k Ktype) String() string {
return "FileOpEnd"
case FileRundown:
return "FileRundown"
case MapViewFile:
return "MapViewFile"
case UnmapViewFile:
return "UnmapViewFile"
case MapFileRundown:
return "MapFileRundown"
case CreateHandle:
return "CreateHandle"
case CloseHandle:
Expand Down Expand Up @@ -274,7 +287,7 @@ func (k Ktype) Category() Category {
case LoadImage, UnloadImage, ImageRundown:
return Image
case CreateFile, ReadFile, WriteFile, EnumDirectory, DeleteFile, RenameFile, CloseFile, SetFileInformation,
FileRundown, FileOpEnd, ReleaseFile:
FileRundown, FileOpEnd, ReleaseFile, MapViewFile, UnmapViewFile, MapFileRundown:
return File
case RegCreateKey, RegDeleteKey, RegOpenKey, RegCloseKey, RegQueryKey, RegQueryValue, RegSetValue, RegDeleteValue,
RegKCBRundown, RegDeleteKCB, RegCreateKCB:
Expand Down Expand Up @@ -329,6 +342,10 @@ func (k Ktype) Description() string {
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"
case RegCreateKey:
return "Creates a registry key or opens it if the key already exists"
case RegOpenKey:
Expand Down Expand Up @@ -403,6 +420,7 @@ func (k Ktype) OnlyState() bool {
RegKCBRundown,
FileOpEnd,
ReleaseFile,
MapFileRundown,
RegCreateKCB,
RegDeleteKCB:
return true
Expand Down
6 changes: 5 additions & 1 deletion pkg/kevent/ktypes/metainfo_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ var kevents = map[Ktype]KeventInfo{
LoadDriver: {"LoadDriver", Driver, "Loads the kernel driver"},
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"},
}

var ktypes = map[string]Ktype{
Expand Down Expand Up @@ -128,10 +130,12 @@ var ktypes = map[string]Ktype{
"RetransmitTCP6": RetransmitTCPv6,
"CreateHandle": CreateHandle,
"CloseHandle": CloseHandle,
"DuplicateHandle": DuplicateHandle,
"LoadDriver": LoadDriver,
"VirtualAlloc": VirtualAlloc,
"VirtualFree": VirtualFree,
"DuplicateHandle": DuplicateHandle,
"MapViewFile": MapViewFile,
"UnmapViewFile": UnmapViewFile,
}

// KtypeToKeventInfo maps the event type to the structure storing detailed information about the event.
Expand Down
2 changes: 1 addition & 1 deletion pkg/kstream/controller_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (c *Controller) Start() error {
flags |= etw.Registry
}
if c.kstreamConfig.EnableFileIOKevents {
flags |= etw.DiskFileIO | etw.FileIO | etw.FileIOInit
flags |= etw.DiskFileIO | etw.FileIO | etw.FileIOInit | etw.VaMap
}
if c.kstreamConfig.EnableMemKevents {
flags |= etw.VirtualAlloc
Expand Down
4 changes: 2 additions & 2 deletions pkg/kstream/controller_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestStartTraces(t *testing.T) {
FlushTimer: time.Millisecond * 2300,
},
1,
[]etw.EventTraceFlags{0x6010203, 0},
[]etw.EventTraceFlags{0x6018203, 0},
},
{"start kernel logger and audit api sessions",
config.KstreamConfig{
Expand All @@ -57,7 +57,7 @@ func TestStartTraces(t *testing.T) {
EnableAuditAPIEvents: true,
},
2,
[]etw.EventTraceFlags{0x6030203, 0x80000040},
[]etw.EventTraceFlags{0x6038203, 0x80000040},
},
}

Expand Down
Loading

0 comments on commit 7480862

Please sign in to comment.