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
20 changes: 20 additions & 0 deletions pkg/symbolize/symbolizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ var (
// symModulesCount counts the number of loaded module exports
symModulesCount = expvar.NewInt("symbolizer.modules.count")

// symEnumModulesHits counts the number of hits from enumerated modules
symEnumModulesHits = expvar.NewInt("symbolizer.enum.modules.hits")

// debugHelpFallbacks counts how many times we Debug Help API was called
// to resolve symbol information since we fail to do this from process
// modules and PE export directory data
Expand Down Expand Up @@ -462,6 +465,23 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent) callstack.F
if mod == nil && e.PS.Parent != nil {
mod = e.PS.Parent.FindModuleByVa(addr)
}
if mod == nil {
// our last resort is to enumerate process modules
modules := sys.EnumProcessModules(e.PID)
for _, m := range modules {
b := va.Address(m.BaseOfDll)
size := uint64(m.SizeOfImage)
if addr >= b && addr <= b.Inc(size) {
mod = &pstypes.Module{
Name: m.Name,
BaseAddress: b,
Size: size,
}
symEnumModulesHits.Add(1)
break
}
}
}
if mod != nil {
frame.Module = mod.Name
frame.ModuleAddress = mod.BaseAddress
Expand Down
56 changes: 56 additions & 0 deletions pkg/sys/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,59 @@ func IsWindowsService() bool {
isSvc, err := svc.IsWindowsService()
return isSvc && err == nil
}

// ProcessModule describes the process loaded module.
type ProcessModule struct {
windows.ModuleInfo
Name string
}

// EnumProcessModules returns all loaded modules in the process address space.
func EnumProcessModules(pid uint32) []ProcessModule {
n := uint32(1024)
mods := make([]windows.Handle, n)
proc, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, pid)
if err != nil {
return nil
}
defer windows.Close(proc)

if err := windows.EnumProcessModules(proc, &mods[0], 1024, &n); err != nil {
// needs bigger buffer
if n > 1024 {
mods = make([]windows.Handle, n)
if err := windows.EnumProcessModules(proc, &mods[0], n, &n); err != nil {
return nil
}
}
return nil
}

modules := make([]ProcessModule, 0)
for _, mod := range mods {
if mod == 0 {
continue
}
var moduleInfo windows.ModuleInfo
if err := windows.GetModuleInformation(proc, mod, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo))); err != nil {
continue
}
module := ProcessModule{ModuleInfo: moduleInfo}
if name, err := getModuleFileName(proc, mod); err == nil {
module.Name = name
}
modules = append(modules, module)
}

return modules
}

func getModuleFileName(proc, mod windows.Handle) (string, error) {
n := uint32(1024)
buf := make([]uint16, n)
err := windows.GetModuleFileNameEx(proc, mod, &buf[0], n)
if err != nil {
return "", err
}
return windows.UTF16ToString(buf), nil
}
37 changes: 37 additions & 0 deletions pkg/sys/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2021-present 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 sys

import (
"github.com/stretchr/testify/assert"
"golang.org/x/sys/windows"
"path/filepath"
"strings"
"testing"
)

func TestEnumProcessModules(t *testing.T) {
mods := EnumProcessModules(windows.GetCurrentProcessId())
assert.True(t, len(mods) > 0)
names := make([]string, 0, len(mods))
for _, mod := range mods {
names = append(names, filepath.Base(strings.ToLower(mod.Name)))
}
assert.Contains(t, names, "ntdll.dll")
}