Skip to content

Commit

Permalink
feat(symbolizer): Symbolize thread start address
Browse files Browse the repository at this point in the history
Resolve the thread start address to its respective
symbol name and the module from which the function
call is originated.
  • Loading branch information
rabbitstack committed Dec 15, 2024
1 parent 4d6027d commit efc84b3
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/kevent/kparams/fields_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const (
UstackLimit = "ustack_limit"
// StartAddress field is the thread start address.
StartAddress = "start_address"
// StartAddressSymbol field is the symbol associated with the thread start address.
StartAddressSymbol = "start_address_symbol"
// StartAddressModule field is the module where the thread start address is mapped.
StartAddressModule = "start_address_module"
// TEB field is the address of the Thread Environment Block (TEB)
TEB = "teb"

Expand Down
70 changes: 70 additions & 0 deletions pkg/symbolize/symbolizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,25 @@ func (s *Symbolizer) processCallstack(e *kevent.Kevent) error {
defer s.mu.Unlock()

if e.PS != nil {
// symbolize thread start address
if e.IsCreateThread() && !e.IsSystemPid() {
addr := e.Kparams.TryGetAddress(kparams.StartAddress)

mod := e.PS.FindModuleByVa(addr)
symbol := s.symbolizeAddress(e.Kparams.MustGetPid(), addr, mod)

if symbol != "" {
e.Kparams.Append(kparams.StartAddressSymbol, kparams.UnicodeString, symbol)
}
if mod != nil {
e.Kparams.Append(kparams.StartAddressModule, kparams.UnicodeString, mod.Name)
}
}

// try to resolve addresses from process
// state and PE export directory data
s.pushFrames(addrs, e, false, true)

return nil
}

Expand Down Expand Up @@ -504,6 +520,60 @@ func (s *Symbolizer) resolveSymbolFromExportDirectory(addr va.Address, mod *psty
return symbolFromRVA(rva, px.Exports)
}

// symbolizeAddress resolves the given address to a symbol. If the symbol
// for this address was resolved previously, we fetch it from the cache.
// On the contrary, the symbol is first consulted in the export directory.
// If not found, the Debug Help API is used to symbolize the address.
func (s *Symbolizer) symbolizeAddress(pid uint32, addr va.Address, mod *pstypes.Module) string {
if addr.InSystemRange() {
return ""
}

symbol, ok := s.symbols[pid][addr]
if !ok && mod != nil {
// resolve symbol from the export directory
symbol = s.resolveSymbolFromExportDirectory(addr, mod)
}

// try to get the symbol via Debug Help API
if symbol == "" {
proc, ok := s.procs[pid]
if !ok {
handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_QUERY_INFORMATION, false, pid)
if err != nil {
return ""
}

// initialize symbol handler
opts := uint32(sys.SymUndname | sys.SymCaseInsensitive | sys.SymAutoPublics | sys.SymOmapFindNearest | sys.SymDeferredLoads)
err = s.r.Initialize(handle, opts)
if err != nil {
return ""
}

proc = &process{pid, handle, time.Now(), 1}
s.procs[pid] = proc

// resolve address to symbol
symbol, _ = s.r.GetSymbolNameAndOffset(handle, addr)
} else {
symbol, _ = s.r.GetSymbolNameAndOffset(proc.handle, addr)
proc.keepalive()
}
}

// cache the resolved symbol
if addrs, ok := s.symbols[pid]; ok {
if _, ok := addrs[addr]; !ok {
s.symbols[pid][addr] = symbol
}
} else {
s.symbols[pid] = map[va.Address]string{addr: symbol}
}

return symbol
}

// symbolFromRVA finds the closest export address before RVA.
func symbolFromRVA(rva va.Address, exports map[uint32]string) string {
var exp uint32
Expand Down

0 comments on commit efc84b3

Please sign in to comment.