Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 37 additions & 69 deletions internal/etw/processors/fs_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ var (
type fsProcessor struct {
// files stores the file metadata indexed by file object
files map[uint64]*FileInfo
// mmaps stores memory-mapped files by pid and file object
mmaps map[uint32]map[uint64]*MmapInfo

hsnap handle.Snapshotter
psnap ps.Snapshotter
Expand All @@ -75,13 +73,6 @@ type FileInfo struct {
Type fs.FileType
}

// MmapInfo stores information of the memory-mapped file.
type MmapInfo struct {
File string
BaseAddr uint64
Size uint64
}

func newFsProcessor(
hsnap handle.Snapshotter,
psnap ps.Snapshotter,
Expand All @@ -91,7 +82,6 @@ func newFsProcessor(
) Processor {
f := &fsProcessor{
files: make(map[uint64]*FileInfo),
mmaps: make(map[uint32]map[uint64]*MmapInfo),
irps: make(map[uint64]*kevent.Kevent),
hsnap: hsnap,
psnap: psnap,
Expand Down Expand Up @@ -139,35 +129,25 @@ func (f *fsProcessor) processEvent(e *kevent.Kevent) (*kevent.Kevent, error) {
f.files[fileObject] = &FileInfo{Name: filepath, Type: fs.GetFileType(filepath, 0)}
}
case ktypes.MapFileRundown:
// if the memory-mapped view refers to the image/data file
// we store it in internal state for each process. The state
// is consulted later when we process unmap events
sec := e.Kparams.MustGetUint32(kparams.FileViewSectionType)
isMapped := sec != va.SectionPagefile && sec != va.SectionPhysical
if !isMapped {
return e, nil
}
fileKey := e.Kparams.MustGetUint64(kparams.FileKey)
viewBase := e.Kparams.MustGetUint64(kparams.FileViewBase)
viewSize := e.Kparams.MustGetUint64(kparams.FileViewSize)
f.initMmap(e.PID)
fileinfo := f.files[fileKey]

if fileinfo != nil {
totalMapRundownFiles.Add(1)
f.mmaps[e.PID][fileKey] = &MmapInfo{File: fileinfo.Name, BaseAddr: viewBase, Size: viewSize}
e.AppendParam(kparams.FilePath, kparams.Path, fileinfo.Name)
} else {
process, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, e.PID)
if err != nil {
return e, nil
// if the view of section is backed by the data/image file
// try to get the mapped file name and append it to params
sec := e.Kparams.MustGetUint32(kparams.FileViewSectionType)
isMapped := sec != va.SectionPagefile && sec != va.SectionPhysical
if isMapped {
totalMapRundownFiles.Add(1)
addr := e.Kparams.MustGetUint64(kparams.FileViewBase) + (e.Kparams.MustGetUint64(kparams.FileOffset))
e.AppendParam(kparams.FilePath, kparams.Path, f.getMappedFile(e.PID, addr))
}
defer windows.Close(process)
totalMapRundownFiles.Add(1)
addr := e.Kparams.MustGetUint64(kparams.FileViewBase) + (e.Kparams.MustGetUint64(kparams.FileOffset))
name := f.devMapper.Convert(sys.GetMappedFile(process, uintptr(addr)))
f.mmaps[e.PID][fileKey] = &MmapInfo{File: name, BaseAddr: viewBase, Size: viewSize}
}
e.AppendParam(kparams.FilePath, kparams.Path, f.mmaps[e.PID][fileKey].File)
return e, f.psnap.AddFileMapping(e)

return e, f.psnap.AddMmap(e)
case ktypes.CreateFile:
// we defer the processing of the CreateFile event until we get
// the matching FileOpEnd event. This event contains the operation
Expand Down Expand Up @@ -255,24 +235,22 @@ func (f *fsProcessor) processEvent(e *kevent.Kevent) (*kevent.Kevent, error) {
fileObject := e.Kparams.MustGetUint64(kparams.FileObject)
delete(f.files, fileObject)
case ktypes.UnmapViewFile:
_ = f.psnap.RemoveFileMapping(e.PID, e.Kparams.TryGetAddress(kparams.FileViewBase))
fileKey := e.Kparams.MustGetUint64(kparams.FileKey)
if _, ok := f.mmaps[e.PID]; !ok {
return e, nil
}
mmapinfo := f.mmaps[e.PID][fileKey]
if mmapinfo != nil {
e.AppendParam(kparams.FilePath, kparams.Path, mmapinfo.File)
ok, proc := f.psnap.Find(e.PID)
addr := e.Kparams.TryGetAddress(kparams.FileViewBase)
if ok {
mmap := proc.FindMmap(addr)
if mmap != nil {
e.AppendParam(kparams.FilePath, kparams.Path, mmap.File)
}
}

totalMapRundownFiles.Add(-1)
delete(f.mmaps[e.PID], fileKey)
if len(f.mmaps[e.PID]) == 0 {
// process terminated, all files unmapped
f.removeMmap(e.PID)
}

return e, f.psnap.RemoveMmap(e.PID, addr)
default:
var fileObject uint64
fileKey := e.Kparams.MustGetUint64(kparams.FileKey)

if !e.IsMapViewFile() {
fileObject = e.Kparams.MustGetUint64(kparams.FileObject)
}
Expand All @@ -286,28 +264,18 @@ func (f *fsProcessor) processEvent(e *kevent.Kevent) (*kevent.Kevent, error) {
if fileinfo == nil && e.IsMapViewFile() {
sec := e.Kparams.MustGetUint32(kparams.FileViewSectionType)
isMapped := sec != va.SectionPagefile && sec != va.SectionPhysical
if !isMapped {
return e, nil
if isMapped {
totalMapRundownFiles.Add(1)
addr := e.Kparams.MustGetUint64(kparams.FileViewBase) + (e.Kparams.MustGetUint64(kparams.FileOffset))
e.AppendParam(kparams.FilePath, kparams.Path, f.getMappedFile(e.PID, addr))
}
process, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, e.PID)
if err != nil {
return e, nil
}
defer windows.Close(process)
viewBase := e.Kparams.MustGetUint64(kparams.FileViewBase)
viewSize := e.Kparams.MustGetUint64(kparams.FileViewSize)
addr := e.Kparams.MustGetUint64(kparams.FileViewBase) + (e.Kparams.MustGetUint64(kparams.FileOffset))
name := f.devMapper.Convert(sys.GetMappedFile(process, uintptr(addr)))
f.initMmap(e.PID)
f.mmaps[e.PID][fileKey] = &MmapInfo{File: name, BaseAddr: viewBase, Size: viewSize}
e.AppendParam(kparams.FilePath, kparams.Path, name)
return e, f.psnap.AddFileMapping(e)
}

// ignore object misses that are produced by CloseFile
if fileinfo == nil && !e.IsCloseFile() {
fileObjectMisses.Add(1)
}

if e.IsDeleteFile() {
delete(f.files, fileObject)
}
Expand All @@ -317,14 +285,16 @@ func (f *fsProcessor) processEvent(e *kevent.Kevent) (*kevent.Kevent, error) {
}
return e, nil
}

if fileinfo != nil {
if fileinfo.Type != fs.Unknown {
e.AppendEnum(kparams.FileType, uint32(fileinfo.Type), fs.FileTypes)
}
e.AppendParam(kparams.FilePath, kparams.Path, fileinfo.Name)
}

if e.IsMapViewFile() {
return e, f.psnap.AddFileMapping(e)
return e, f.psnap.AddMmap(e)
}
}
return e, nil
Expand Down Expand Up @@ -352,15 +322,13 @@ func (f *fsProcessor) findFile(fileKey, fileObject uint64) *FileInfo {
return nil
}

func (f *fsProcessor) initMmap(pid uint32) {
m := f.mmaps[pid]
if m == nil {
f.mmaps[pid] = make(map[uint64]*MmapInfo)
func (f *fsProcessor) getMappedFile(pid uint32, addr uint64) string {
process, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, pid)
if err != nil {
return ""
}
}

func (f *fsProcessor) removeMmap(pid uint32) {
delete(f.mmaps, pid)
defer windows.Close(process)
return f.devMapper.Convert(sys.GetMappedFile(process, uintptr(addr)))
}

func (f *fsProcessor) purge() {
Expand Down
32 changes: 17 additions & 15 deletions internal/etw/processors/fs_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/rabbitstack/fibratus/pkg/kevent/kparams"
"github.com/rabbitstack/fibratus/pkg/kevent/ktypes"
"github.com/rabbitstack/fibratus/pkg/ps"
pstypes "github.com/rabbitstack/fibratus/pkg/ps/types"
"github.com/rabbitstack/fibratus/pkg/util/va"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -89,12 +90,11 @@ func TestFsProcessor(t *testing.T) {
},
func(e *kevent.Kevent, t *testing.T, hsnap *handle.SnapshotterMock, p Processor) {
fsProcessor := p.(*fsProcessor)
assert.Contains(t, fsProcessor.mmaps, uint32(10233))
mapinfo := fsProcessor.mmaps[10233][124567380264]
require.NotNil(t, mapinfo)
assert.Equal(t, "C:\\Windows\\System32\\kernel32.dll", mapinfo.File)
assert.Equal(t, uint64(3098), mapinfo.Size)
assert.Equal(t, uint64(0xffff23433), mapinfo.BaseAddr)

assert.Equal(t, "C:\\Windows\\System32\\kernel32.dll", e.GetParamAsString(kparams.FilePath))

psnap := fsProcessor.psnap.(*ps.SnapshotterMock)
psnap.AssertNumberOfCalls(t, "AddMmap", 1)
},
},
{
Expand Down Expand Up @@ -201,19 +201,16 @@ func TestFsProcessor(t *testing.T) {
kparams.FileViewSectionType: {Name: kparams.FileViewSectionType, Type: kparams.Enum, Value: uint32(va.SectionImage), Enum: kevent.ViewSectionTypes},
},
},
func(p Processor) {
fsProcessor := p.(*fsProcessor)
fsProcessor.mmaps[10233] = make(map[uint64]*MmapInfo)
fsProcessor.mmaps[10233][124567380264] = &MmapInfo{File: "C:\\Windows\\System32\\kernel32.dll"}
},
nil,
func() *handle.SnapshotterMock {
hsnap := new(handle.SnapshotterMock)
return hsnap
},
func(e *kevent.Kevent, t *testing.T, hsnap *handle.SnapshotterMock, p Processor) {
fsProcessor := p.(*fsProcessor)
assert.True(t, e.Kparams.Contains(kparams.FilePath))
assert.Nil(t, fsProcessor.mmaps[3098][124567380264])

psnap := fsProcessor.psnap.(*ps.SnapshotterMock)
psnap.AssertNumberOfCalls(t, "RemoveMmap", 1)
},
},
{
Expand Down Expand Up @@ -298,8 +295,13 @@ func TestFsProcessor(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
hsnap := tt.hsnap()
psnap := new(ps.SnapshotterMock)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("Find", mock.Anything).Return(true, &pstypes.PS{
Mmaps: []pstypes.Mmap{
{File: "C:\\Windows\\System32\\kernel32.dll", BaseAddress: va.Address(0xffff23433), Size: 3098},
},
})
p := newFsProcessor(hsnap, psnap, fs.NewDevMapper(), fs.NewDevPathResolver(), &config.Config{})
if tt.setupProcessor != nil {
tt.setupProcessor(p)
Expand Down
52 changes: 26 additions & 26 deletions internal/etw/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ func TestEventSourceStartTraces(t *testing.T) {
psnap.On("Write", mock.Anything).Return(nil)
psnap.On("AddThread", mock.Anything).Return(nil)
psnap.On("AddModule", mock.Anything).Return(nil)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveThread", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveModule", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindModule", mock.Anything).Return(false, nil)
Expand Down Expand Up @@ -153,8 +153,8 @@ func TestEventSourceEnableFlagsDynamically(t *testing.T) {
psnap.On("Write", mock.Anything).Return(nil)
psnap.On("AddThread", mock.Anything).Return(nil)
psnap.On("AddModule", mock.Anything).Return(nil)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveThread", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveModule", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindModule", mock.Anything).Return(false, nil)
Expand Down Expand Up @@ -236,8 +236,8 @@ func TestEventSourceEnableFlagsDynamicallyWithYaraEnabled(t *testing.T) {
psnap.On("Write", mock.Anything).Return(nil)
psnap.On("AddThread", mock.Anything).Return(nil)
psnap.On("AddModule", mock.Anything).Return(nil)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveThread", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveModule", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindModule", mock.Anything).Return(false, nil)
Expand Down Expand Up @@ -311,8 +311,8 @@ func TestEventSourceRundownEvents(t *testing.T) {
psnap.On("Write", mock.Anything).Return(nil)
psnap.On("AddThread", mock.Anything).Return(nil)
psnap.On("AddModule", mock.Anything).Return(nil)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveThread", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveModule", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindModule", mock.Anything).Return(false, nil)
Expand Down Expand Up @@ -721,11 +721,11 @@ func TestEventSourceAllEvents(t *testing.T) {
psnap.On("Write", mock.Anything).Return(nil)
psnap.On("AddThread", mock.Anything).Return(nil)
psnap.On("AddModule", mock.Anything).Return(nil)
psnap.On("AddFileMapping", mock.Anything).Return(nil)
psnap.On("AddMmap", mock.Anything).Return(nil)
psnap.On("RemoveThread", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveModule", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindModule", mock.Anything).Return(false, nil)
psnap.On("RemoveFileMapping", mock.Anything, mock.Anything).Return(nil)
psnap.On("RemoveMmap", mock.Anything, mock.Anything).Return(nil)
psnap.On("FindAndPut", mock.Anything).Return(&pstypes.PS{})
psnap.On("Find", mock.Anything).Return(true, &pstypes.PS{})
psnap.On("Remove", mock.Anything).Return(nil)
Expand Down Expand Up @@ -814,22 +814,22 @@ type NoopPsSnapshotter struct{}

var fakeProc = &pstypes.PS{PID: 111111, Name: "fake.exe"}

func (s *NoopPsSnapshotter) Write(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) Remove(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) Find(pid uint32) (bool, *pstypes.PS) { return true, fakeProc }
func (s *NoopPsSnapshotter) FindAndPut(pid uint32) *pstypes.PS { return fakeProc }
func (s *NoopPsSnapshotter) Put(ps *pstypes.PS) {}
func (s *NoopPsSnapshotter) Size() uint32 { return 1 }
func (s *NoopPsSnapshotter) Close() error { return nil }
func (s *NoopPsSnapshotter) GetSnapshot() []*pstypes.PS { return nil }
func (s *NoopPsSnapshotter) AddThread(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) AddModule(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) FindModule(addr va.Address) (bool, *pstypes.Module) { return false, nil }
func (s *NoopPsSnapshotter) RemoveThread(pid uint32, tid uint32) error { return nil }
func (s *NoopPsSnapshotter) RemoveModule(pid uint32, mod string) error { return nil }
func (s *NoopPsSnapshotter) WriteFromKcap(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) AddFileMapping(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) RemoveFileMapping(pid uint32, address va.Address) error { return nil }
func (s *NoopPsSnapshotter) Write(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) Remove(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) Find(pid uint32) (bool, *pstypes.PS) { return true, fakeProc }
func (s *NoopPsSnapshotter) FindAndPut(pid uint32) *pstypes.PS { return fakeProc }
func (s *NoopPsSnapshotter) Put(ps *pstypes.PS) {}
func (s *NoopPsSnapshotter) Size() uint32 { return 1 }
func (s *NoopPsSnapshotter) Close() error { return nil }
func (s *NoopPsSnapshotter) GetSnapshot() []*pstypes.PS { return nil }
func (s *NoopPsSnapshotter) AddThread(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) AddModule(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) FindModule(addr va.Address) (bool, *pstypes.Module) { return false, nil }
func (s *NoopPsSnapshotter) RemoveThread(pid uint32, tid uint32) error { return nil }
func (s *NoopPsSnapshotter) RemoveModule(pid uint32, mod string) error { return nil }
func (s *NoopPsSnapshotter) WriteFromKcap(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) AddMmap(kevt *kevent.Kevent) error { return nil }
func (s *NoopPsSnapshotter) RemoveMmap(pid uint32, address va.Address) error { return nil }

func TestCallstackEnrichment(t *testing.T) {
hsnap := new(handle.SnapshotterMock)
Expand Down
8 changes: 4 additions & 4 deletions pkg/ps/snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ type Snapshotter interface {
RemoveThread(pid uint32, tid uint32) error
// RemoveModule removes the module the given process.
RemoveModule(pid uint32, mod string) error
// AddFileMapping adds a new data memory-mapped file to this process state.
AddFileMapping(*kevent.Kevent) error
// RemoveFileMapping removes memory-mapped file at the given base address.
RemoveFileMapping(pid uint32, address va.Address) error
// AddMmap adds a new memory mapping (data memory-mapped file, image, or pagefile) to this process state.
AddMmap(*kevent.Kevent) error
// RemoveMmap removes memory mapping at the given base address.
RemoveMmap(pid uint32, address va.Address) error
// WriteFromKcap appends a new process state to the snapshotter from the captured kernel event.
WriteFromKcap(kevt *kevent.Kevent) error
// Remove deletes process's state from the snapshotter.
Expand Down
4 changes: 2 additions & 2 deletions pkg/ps/snapshotter_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ func (s *SnapshotterMock) RemoveModule(pid uint32, mod string) error {
func (s *SnapshotterMock) WriteFromKcap(kevt *kevent.Kevent) error { return nil }

// AddFileMapping method
func (s *SnapshotterMock) AddFileMapping(kevt *kevent.Kevent) error {
func (s *SnapshotterMock) AddMmap(kevt *kevent.Kevent) error {
args := s.Called(kevt)
return args.Error(0)
}

// RemoveFileMapping method
func (s *SnapshotterMock) RemoveFileMapping(pid uint32, address va.Address) error {
func (s *SnapshotterMock) RemoveMmap(pid uint32, address va.Address) error {
args := s.Called(pid, address)
return args.Error(0)
}
Loading
Loading