From 9f2f130ba7ce603e06f796fa4446d60a49728d49 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Fri, 20 Dec 2024 16:17:48 +0100 Subject: [PATCH] refactor(ps): Store memory mappings per process The process state is refactored to store all the memory mappings created by the process. This effectively eliminates the need to keep the memory mappings in the fs processor hence improving code quality, readability, and performance. --- internal/etw/processors/fs_windows.go | 106 +++++++-------------- internal/etw/processors/fs_windows_test.go | 32 ++++--- internal/etw/source_test.go | 52 +++++----- pkg/ps/snapshotter.go | 8 +- pkg/ps/snapshotter_mock.go | 4 +- pkg/ps/snapshotter_windows.go | 20 ++-- pkg/ps/types/types_windows.go | 81 +++++++++------- pkg/symbolize/symbolizer.go | 6 -- 8 files changed, 138 insertions(+), 171 deletions(-) diff --git a/internal/etw/processors/fs_windows.go b/internal/etw/processors/fs_windows.go index 40e0ad6f4..c8e0c798d 100644 --- a/internal/etw/processors/fs_windows.go +++ b/internal/etw/processors/fs_windows.go @@ -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 @@ -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, @@ -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, @@ -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 @@ -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) } @@ -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) } @@ -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 @@ -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() { diff --git a/internal/etw/processors/fs_windows_test.go b/internal/etw/processors/fs_windows_test.go index 260012601..d08756ca0 100644 --- a/internal/etw/processors/fs_windows_test.go +++ b/internal/etw/processors/fs_windows_test.go @@ -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" @@ -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) }, }, { @@ -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) }, }, { @@ -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) diff --git a/internal/etw/source_test.go b/internal/etw/source_test.go index d2143a88f..db58db85a 100644 --- a/internal/etw/source_test.go +++ b/internal/etw/source_test.go @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) diff --git a/pkg/ps/snapshotter.go b/pkg/ps/snapshotter.go index 8ebbe1af3..8135489a3 100644 --- a/pkg/ps/snapshotter.go +++ b/pkg/ps/snapshotter.go @@ -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. diff --git a/pkg/ps/snapshotter_mock.go b/pkg/ps/snapshotter_mock.go index b54fcc24c..4e36d44c6 100644 --- a/pkg/ps/snapshotter_mock.go +++ b/pkg/ps/snapshotter_mock.go @@ -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) } diff --git a/pkg/ps/snapshotter_windows.go b/pkg/ps/snapshotter_windows.go index b6edf62f9..a6d6f9fdf 100644 --- a/pkg/ps/snapshotter_windows.go +++ b/pkg/ps/snapshotter_windows.go @@ -25,7 +25,6 @@ import ( "golang.org/x/sys/windows" "path/filepath" "strconv" - "strings" "sync" "time" @@ -266,7 +265,7 @@ func (s *snapshotter) FindModule(addr va.Address) (bool, *pstypes.Module) { return false, nil } -func (s *snapshotter) AddFileMapping(e *kevent.Kevent) error { +func (s *snapshotter) AddMmap(e *kevent.Kevent) error { s.mu.Lock() defer s.mu.Unlock() proc, ok := s.procs[e.PID] @@ -274,24 +273,21 @@ func (s *snapshotter) AddFileMapping(e *kevent.Kevent) error { return nil } - filename := e.GetParamAsString(kparams.FilePath) - ext := strings.ToLower(filepath.Ext(filename)) - // skip redundant or unneeded memory-mapped files - if ext == ".dll" || ext == ".exe" || ext == ".mui" { - return nil - } mmapCount.Add(1) + mmap := pstypes.Mmap{} - mmap.File = filename + mmap.File = e.GetParamAsString(kparams.FilePath) mmap.BaseAddress = e.Kparams.TryGetAddress(kparams.FileViewBase) mmap.Size, _ = e.Kparams.GetUint64(kparams.FileViewSize) + mmap.Protection, _ = e.Kparams.GetUint32(kparams.MemProtect) + mmap.Type = e.GetParamAsString(kparams.FileViewSectionType) - proc.MapFile(mmap) + proc.AddMmap(mmap) return nil } -func (s *snapshotter) RemoveFileMapping(pid uint32, addr va.Address) error { +func (s *snapshotter) RemoveMmap(pid uint32, addr va.Address) error { s.mu.Lock() defer s.mu.Unlock() proc, ok := s.procs[pid] @@ -299,7 +295,7 @@ func (s *snapshotter) RemoveFileMapping(pid uint32, addr va.Address) error { return nil } mmapCount.Add(-1) - proc.UnmapFile(addr) + proc.RemoveMmap(addr) return nil } diff --git a/pkg/ps/types/types_windows.go b/pkg/ps/types/types_windows.go index 85c35f3fb..619e58ba5 100644 --- a/pkg/ps/types/types_windows.go +++ b/pkg/ps/types/types_windows.go @@ -67,8 +67,8 @@ type PS struct { Threads map[uint32]Thread `json:"-"` // Modules contains all the modules loaded by the process. Modules []Module `json:"modules"` - // FileMappings contains all memory-mapped data files. - FileMappings []Mmap + // Mmaps contains all memory mappings. + Mmaps []Mmap // Handles represents the collection of handles allocated by the process. Handles htypes.Handles `json:"handles"` // PE stores the PE (Portable Executable) metadata. @@ -333,28 +333,36 @@ func (m Module) String() string { // IsExecutable determines if the loaded module is an executable. func (m Module) IsExecutable() bool { return strings.ToLower(filepath.Ext(m.Name)) == ".exe" } -// Mmap stores information of the memory-mapped file. +// Mmap stores information related to the memory mapping. type Mmap struct { - File string + // BaseAddress represents the address where the view of section is mapped. BaseAddress va.Address - Size uint64 + // Size indicates the size of the mapped view of section. + Size uint64 + // Protection is the bitmask with view protection rights. + Protection uint32 + // Type represents the type of the view of section (e.g. IMAGE, DATA, PAGEFILE) + Type string + // File if the view is backed by the file object, this field indicates + // the file path of the mapped image/data file. + File string } // New produces a new process state. func New(pid, ppid uint32, name, cmndline, exe string, sid *windows.SID, sessionID uint32) *PS { ps := &PS{ - PID: pid, - Ppid: ppid, - Name: name, - Cmdline: cmndline, - Exe: exe, - Args: cmdline.Split(cmndline), - SID: sid.String(), - SessionID: sessionID, - Threads: make(map[uint32]Thread), - Modules: make([]Module, 0), - Handles: make([]htypes.Handle, 0), - FileMappings: make([]Mmap, 0), + PID: pid, + Ppid: ppid, + Name: name, + Cmdline: cmndline, + Exe: exe, + Args: cmdline.Split(cmndline), + SID: sid.String(), + SessionID: sessionID, + Threads: make(map[uint32]Thread), + Modules: make([]Module, 0), + Handles: make([]htypes.Handle, 0), + Mmaps: make([]Mmap, 0), } ps.Username, ps.Domain, _, _ = sid.LookupAccount("") return ps @@ -363,12 +371,12 @@ func New(pid, ppid uint32, name, cmndline, exe string, sid *windows.SID, session // NewFromKcap reconstructs the state of the process from the capture file. func NewFromKcap(buf []byte, sec section.Section) (*PS, error) { ps := PS{ - Args: make([]string, 0), - Envs: make(map[string]string), - Handles: make([]htypes.Handle, 0), - Modules: make([]Module, 0), - Threads: make(map[uint32]Thread), - FileMappings: make([]Mmap, 0), + Args: make([]string, 0), + Envs: make(map[string]string), + Handles: make([]htypes.Handle, 0), + Modules: make([]Module, 0), + Threads: make(map[uint32]Thread), + Mmaps: make([]Mmap, 0), } if err := ps.Unmarshal(buf, sec); err != nil { return nil, err @@ -445,28 +453,27 @@ func (ps *PS) FindModuleByVa(addr va.Address) *Module { return nil } -// MapFile adds a new data-mapped file this process state. -func (ps *PS) MapFile(mmap Mmap) { - ps.FileMappings = append(ps.FileMappings, mmap) +// AddMmap adds a new memory mapping for this process state. +func (ps *PS) AddMmap(mmap Mmap) { + ps.Mmaps = append(ps.Mmaps, mmap) } -// UnmapFile removes a specified data-mapped file from this process state. -func (ps *PS) UnmapFile(addr va.Address) { - for i, mmap := range ps.FileMappings { +// RemoveMmap removes the memory mapping at the specified address. +func (ps *PS) RemoveMmap(addr va.Address) { + for i, mmap := range ps.Mmaps { if mmap.BaseAddress == addr { - ps.FileMappings = append(ps.FileMappings[:i], ps.FileMappings[i+1:]...) + ps.Mmaps = append(ps.Mmaps[:i], ps.Mmaps[i+1:]...) break } } } -// FindMappingByVa finds the memory-mapped file -// by probing the range of the given virtual address. -func (ps *PS) FindMappingByVa(addr va.Address) string { - for _, mmap := range ps.FileMappings { - if addr >= mmap.BaseAddress && addr <= mmap.BaseAddress.Inc(mmap.Size) { - return mmap.File +// FindMmap returns the memory mapping by the given address. +func (ps *PS) FindMmap(addr va.Address) *Mmap { + for _, mmap := range ps.Mmaps { + if mmap.BaseAddress == addr { + return &mmap } } - return "unbacked" + return nil } diff --git a/pkg/symbolize/symbolizer.go b/pkg/symbolize/symbolizer.go index c0bdf0c95..c0f830ecf 100644 --- a/pkg/symbolize/symbolizer.go +++ b/pkg/symbolize/symbolizer.go @@ -396,9 +396,6 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent, fast, looku if mod != nil { frame.Module = mod.Name } - if frame.Module == "unbacked" || frame.Module == "" { - frame.Module = e.PS.FindMappingByVa(addr) - } if lookupExport { frame.Symbol = s.resolveSymbolFromExportDirectory(addr, mod) } @@ -493,9 +490,6 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent, fast, looku if frame.Module == "" { mod := s.r.GetModuleName(proc.handle, addr) - if mod == "?" && e.PS != nil { - mod = e.PS.FindMappingByVa(addr) - } frame.Module = mod if frame.Module == "?" { frame.Module = "unbacked"