Skip to content

Commit a367399

Browse files
committed
fix(symbolizer): Lookup live modules
When the module in process state can't be derived from the VA, we enumerate the modules via API call
1 parent 3d1006a commit a367399

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

pkg/symbolize/symbolizer.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ var (
6565
// symModulesCount counts the number of loaded module exports
6666
symModulesCount = expvar.NewInt("symbolizer.modules.count")
6767

68+
// symEnumModulesHits counts the number of hits from enumerated modules
69+
symEnumModulesHits = expvar.NewInt("symbolizer.enum.modules.hits")
70+
6871
// debugHelpFallbacks counts how many times we Debug Help API was called
6972
// to resolve symbol information since we fail to do this from process
7073
// modules and PE export directory data
@@ -462,6 +465,23 @@ func (s *Symbolizer) produceFrame(addr va.Address, e *kevent.Kevent) callstack.F
462465
if mod == nil && e.PS.Parent != nil {
463466
mod = e.PS.Parent.FindModuleByVa(addr)
464467
}
468+
if mod == nil {
469+
// our last resort is to enumerate process modules
470+
modules := sys.EnumProcessModules(e.PID)
471+
for _, m := range modules {
472+
b := va.Address(m.BaseOfDll)
473+
size := uint64(m.SizeOfImage)
474+
if addr >= b && addr <= b.Inc(size) {
475+
mod = &pstypes.Module{
476+
Name: m.Name,
477+
BaseAddress: b,
478+
Size: size,
479+
}
480+
symEnumModulesHits.Add(1)
481+
break
482+
}
483+
}
484+
}
465485
if mod != nil {
466486
frame.Module = mod.Name
467487
frame.ModuleAddress = mod.BaseAddress

pkg/sys/process.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,59 @@ func IsWindowsService() bool {
117117
isSvc, err := svc.IsWindowsService()
118118
return isSvc && err == nil
119119
}
120+
121+
// ProcessModule describes the process loaded module.
122+
type ProcessModule struct {
123+
windows.ModuleInfo
124+
Name string
125+
}
126+
127+
// EnumProcessModules returns all loaded modules in the process address space.
128+
func EnumProcessModules(pid uint32) []ProcessModule {
129+
n := uint32(1024)
130+
mods := make([]windows.Handle, n)
131+
proc, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, pid)
132+
if err != nil {
133+
return nil
134+
}
135+
defer windows.Close(proc)
136+
137+
if err := windows.EnumProcessModules(proc, &mods[0], 1024, &n); err != nil {
138+
// needs bigger buffer
139+
if n > 1024 {
140+
mods = make([]windows.Handle, n)
141+
if err := windows.EnumProcessModules(proc, &mods[0], n, &n); err != nil {
142+
return nil
143+
}
144+
}
145+
return nil
146+
}
147+
148+
modules := make([]ProcessModule, 0)
149+
for _, mod := range mods {
150+
if mod == 0 {
151+
continue
152+
}
153+
var moduleInfo windows.ModuleInfo
154+
if err := windows.GetModuleInformation(proc, mod, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo))); err != nil {
155+
continue
156+
}
157+
module := ProcessModule{ModuleInfo: moduleInfo}
158+
if name, err := getModuleFileName(proc, mod); err == nil {
159+
module.Name = name
160+
}
161+
modules = append(modules, module)
162+
}
163+
164+
return modules
165+
}
166+
167+
func getModuleFileName(proc, mod windows.Handle) (string, error) {
168+
n := uint32(1024)
169+
buf := make([]uint16, n)
170+
err := windows.GetModuleFileNameEx(proc, mod, &buf[0], n)
171+
if err != nil {
172+
return "", err
173+
}
174+
return windows.UTF16ToString(buf), nil
175+
}

pkg/sys/process_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2021-present by Nedim Sabic Sabic
3+
* https://www.fibratus.io
4+
* All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package sys
20+
21+
import (
22+
"github.com/stretchr/testify/assert"
23+
"golang.org/x/sys/windows"
24+
"path/filepath"
25+
"strings"
26+
"testing"
27+
)
28+
29+
func TestEnumProcessModules(t *testing.T) {
30+
mods := EnumProcessModules(windows.GetCurrentProcessId())
31+
assert.True(t, len(mods) > 0)
32+
names := make([]string, 0, len(mods))
33+
for _, mod := range mods {
34+
names = append(names, filepath.Base(strings.ToLower(mod.Name)))
35+
}
36+
assert.Contains(t, names, "ntdll.dll")
37+
}

0 commit comments

Comments
 (0)