Skip to content

Commit 9e35266

Browse files
committed
feat(filter): Add additional callstack fields
The new filter fields allow accessing the attributes of the last user/kernel space frame. Additionally, symbols/module filter fields are modified to accept the frame index and only retrieve the symbol/module name of the accessed frame. The callstack segments are added to access signature info of the iterated frame.
1 parent a83dd8b commit 9e35266

File tree

10 files changed

+494
-158
lines changed

10 files changed

+494
-158
lines changed

pkg/filter/accessor_windows.go

Lines changed: 125 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"github.com/rabbitstack/fibratus/pkg/network"
2727
psnap "github.com/rabbitstack/fibratus/pkg/ps"
2828
"github.com/rabbitstack/fibratus/pkg/util/cmdline"
29-
"github.com/rabbitstack/fibratus/pkg/util/loldrivers"
3029
"github.com/rabbitstack/fibratus/pkg/util/signature"
3130
"net"
3231
"path/filepath"
@@ -539,73 +538,167 @@ func newThreadAccessor() Accessor {
539538
return &threadAccessor{}
540539
}
541540

542-
func (t *threadAccessor) Get(f Field, kevt *kevent.Kevent) (kparams.Value, error) {
541+
func (t *threadAccessor) Get(f Field, e *kevent.Kevent) (kparams.Value, error) {
543542
switch f.Name {
544543
case fields.ThreadBasePrio:
545-
return kevt.Kparams.GetUint8(kparams.BasePrio)
544+
return e.Kparams.GetUint8(kparams.BasePrio)
546545
case fields.ThreadIOPrio:
547-
return kevt.Kparams.GetUint8(kparams.IOPrio)
546+
return e.Kparams.GetUint8(kparams.IOPrio)
548547
case fields.ThreadPagePrio:
549-
return kevt.Kparams.GetUint8(kparams.PagePrio)
548+
return e.Kparams.GetUint8(kparams.PagePrio)
550549
case fields.ThreadKstackBase:
551-
return kevt.GetParamAsString(kparams.KstackBase), nil
550+
return e.GetParamAsString(kparams.KstackBase), nil
552551
case fields.ThreadKstackLimit:
553-
return kevt.GetParamAsString(kparams.KstackLimit), nil
552+
return e.GetParamAsString(kparams.KstackLimit), nil
554553
case fields.ThreadUstackBase:
555-
return kevt.GetParamAsString(kparams.UstackBase), nil
554+
return e.GetParamAsString(kparams.UstackBase), nil
556555
case fields.ThreadUstackLimit:
557-
return kevt.GetParamAsString(kparams.UstackLimit), nil
556+
return e.GetParamAsString(kparams.UstackLimit), nil
558557
case fields.ThreadEntrypoint, fields.ThreadStartAddress:
559-
return kevt.GetParamAsString(kparams.StartAddress), nil
558+
return e.GetParamAsString(kparams.StartAddress), nil
560559
case fields.ThreadPID:
561-
return kevt.Kparams.GetUint32(kparams.ProcessID)
560+
return e.Kparams.GetUint32(kparams.ProcessID)
562561
case fields.ThreadTEB:
563-
return kevt.GetParamAsString(kparams.TEB), nil
562+
return e.GetParamAsString(kparams.TEB), nil
564563
case fields.ThreadAccessMask:
565-
if kevt.Type != ktypes.OpenThread {
564+
if e.Type != ktypes.OpenThread {
566565
return nil, nil
567566
}
568-
return kevt.Kparams.GetString(kparams.DesiredAccess)
567+
return e.Kparams.GetString(kparams.DesiredAccess)
569568
case fields.ThreadAccessMaskNames:
570-
if kevt.Type != ktypes.OpenThread {
569+
if e.Type != ktypes.OpenThread {
571570
return nil, nil
572571
}
573-
return kevt.GetFlagsAsSlice(kparams.DesiredAccess), nil
572+
return e.GetFlagsAsSlice(kparams.DesiredAccess), nil
574573
case fields.ThreadAccessStatus:
575-
if kevt.Type != ktypes.OpenThread {
574+
if e.Type != ktypes.OpenThread {
576575
return nil, nil
577576
}
578-
return kevt.GetParamAsString(kparams.NTStatus), nil
577+
return e.GetParamAsString(kparams.NTStatus), nil
579578
case fields.ThreadCallstackSummary:
580-
return kevt.Callstack.Summary(), nil
579+
return e.Callstack.Summary(), nil
581580
case fields.ThreadCallstackDetail:
582-
return kevt.Callstack.String(), nil
581+
return e.Callstack.String(), nil
583582
case fields.ThreadCallstackModules:
584-
return kevt.Callstack.Modules(), nil
583+
// return the module at the given frame level
584+
if f.Arg != "" {
585+
n, err := strconv.Atoi(f.Arg)
586+
if err != nil {
587+
return nil, err
588+
}
589+
590+
if n > e.Callstack.Depth() {
591+
return "", nil
592+
}
593+
594+
return e.Callstack.FrameAt(n).Module, nil
595+
}
596+
597+
return e.Callstack.Modules(), nil
585598
case fields.ThreadCallstackSymbols:
586-
return kevt.Callstack.Symbols(), nil
599+
// return the symbol at the given frame level
600+
if f.Arg != "" {
601+
n, err := strconv.Atoi(f.Arg)
602+
if err != nil {
603+
return nil, err
604+
}
605+
606+
if n > e.Callstack.Depth() {
607+
return "", nil
608+
}
609+
610+
return e.Callstack.FrameAt(n).Symbol, nil
611+
}
612+
613+
return e.Callstack.Symbols(), nil
587614
case fields.ThreadCallstackAllocationSizes:
588-
return kevt.Callstack.AllocationSizes(kevt.PID), nil
615+
return e.Callstack.AllocationSizes(e.PID), nil
589616
case fields.ThreadCallstackProtections:
590-
return kevt.Callstack.Protections(kevt.PID), nil
617+
return e.Callstack.Protections(e.PID), nil
591618
case fields.ThreadCallstackCallsiteLeadingAssembly:
592-
return kevt.Callstack.CallsiteInsns(kevt.PID, true), nil
619+
return e.Callstack.CallsiteInsns(e.PID, true), nil
593620
case fields.ThreadCallstackCallsiteTrailingAssembly:
594-
return kevt.Callstack.CallsiteInsns(kevt.PID, false), nil
621+
return e.Callstack.CallsiteInsns(e.PID, false), nil
595622
case fields.ThreadCallstackIsUnbacked:
596-
return kevt.Callstack.ContainsUnbacked(), nil
623+
return e.Callstack.ContainsUnbacked(), nil
597624
case fields.ThreadCallstack:
598-
return kevt.Callstack, nil
625+
return e.Callstack, nil
599626
case fields.ThreadStartAddressSymbol:
600-
if kevt.Type != ktypes.CreateThread {
627+
if e.Type != ktypes.CreateThread {
601628
return nil, nil
602629
}
603-
return kevt.GetParamAsString(kparams.StartAddressSymbol), nil
630+
return e.GetParamAsString(kparams.StartAddressSymbol), nil
604631
case fields.ThreadStartAddressModule:
605-
if kevt.Type != ktypes.CreateThread {
632+
if e.Type != ktypes.CreateThread {
606633
return nil, nil
607634
}
608-
return kevt.GetParamAsString(kparams.StartAddressModule), nil
635+
return e.GetParamAsString(kparams.StartAddressModule), nil
636+
case fields.ThreadCallstackAddresses:
637+
return e.Callstack.Addresses(), nil
638+
case fields.ThreadCallstackFinalUserModuleName, fields.ThreadCallstackFinalUserModulePath:
639+
frame := e.Callstack.FinalUserFrame()
640+
if frame != nil {
641+
if f.Name == fields.ThreadCallstackFinalUserModuleName {
642+
return filepath.Base(frame.Module), nil
643+
}
644+
return frame.Module, nil
645+
}
646+
return nil, nil
647+
case fields.ThreadCallstackFinalUserSymbolName:
648+
frame := e.Callstack.FinalUserFrame()
649+
if frame != nil {
650+
return frame.Symbol, nil
651+
}
652+
return nil, nil
653+
case fields.ThreadCallstackFinalKernelModuleName, fields.ThreadCallstackFinalKernelModulePath:
654+
frame := e.Callstack.FinalKernelFrame()
655+
if frame != nil {
656+
if f.Name == fields.ThreadCallstackFinalKernelModuleName {
657+
return filepath.Base(frame.Module), nil
658+
}
659+
return frame.Module, nil
660+
}
661+
return nil, nil
662+
case fields.ThreadCallstackFinalKernelSymbolName:
663+
frame := e.Callstack.FinalKernelFrame()
664+
if frame != nil {
665+
return frame.Symbol, nil
666+
}
667+
return nil, nil
668+
case fields.ThreadCallstackFinalUserModuleSignatureIsSigned, fields.ThreadCallstackFinalUserModuleSignatureIsTrusted:
669+
frame := e.Callstack.FinalUserFrame()
670+
if frame == nil || (frame != nil && frame.ModuleAddress.IsZero()) {
671+
return nil, nil
672+
}
673+
674+
sign := getSignature(frame.ModuleAddress, frame.Module, false)
675+
if sign == nil {
676+
return nil, nil
677+
}
678+
679+
if f.Name == fields.ThreadCallstackFinalUserModuleSignatureIsSigned {
680+
return sign.IsSigned(), nil
681+
}
682+
683+
return sign.IsTrusted(), nil
684+
case fields.ThreadCallstackFinalUserModuleSignatureCertIssuer, fields.ThreadCallstackFinalUserModuleSignatureCertSubject:
685+
frame := e.Callstack.FinalUserFrame()
686+
if frame == nil || (frame != nil && frame.ModuleAddress.IsZero()) {
687+
return nil, nil
688+
}
689+
690+
sign := getSignature(frame.ModuleAddress, frame.Module, true)
691+
if sign == nil {
692+
return nil, nil
693+
}
694+
695+
if sign.HasCertificate() && f.Name == fields.ThreadCallstackFinalUserModuleSignatureCertIssuer {
696+
return sign.Cert.Issuer, nil
697+
}
698+
699+
if sign.HasCertificate() {
700+
return sign.Cert.Subject, nil
701+
}
609702
}
610703

611704
return nil, nil
@@ -1244,42 +1337,3 @@ func (*dnsAccessor) Get(f Field, kevt *kevent.Kevent) (kparams.Value, error) {
12441337

12451338
return nil, nil
12461339
}
1247-
1248-
// isLOLDriver interacts with the loldrivers client to determine
1249-
// whether the loaded/dropped driver is malicious or vulnerable.
1250-
func isLOLDriver(f fields.Field, kevt *kevent.Kevent) (kparams.Value, error) {
1251-
var filename string
1252-
1253-
if kevt.Category == ktypes.File {
1254-
filename = kevt.GetParamAsString(kparams.FilePath)
1255-
} else {
1256-
filename = kevt.GetParamAsString(kparams.ImagePath)
1257-
}
1258-
1259-
isDriver := filepath.Ext(filename) == ".sys" || kevt.Kparams.TryGetBool(kparams.FileIsDriver)
1260-
if !isDriver {
1261-
return nil, nil
1262-
}
1263-
ok, driver := loldrivers.GetClient().MatchHash(filename)
1264-
if !ok {
1265-
return nil, nil
1266-
}
1267-
if (f == fields.FileIsDriverVulnerable || f == fields.ImageIsDriverVulnerable) && driver.IsVulnerable {
1268-
return true, nil
1269-
}
1270-
if (f == fields.FileIsDriverMalicious || f == fields.ImageIsDriverMalicious) && driver.IsMalicious {
1271-
return true, nil
1272-
}
1273-
return false, nil
1274-
}
1275-
1276-
// initLOLDriversClient initializes the loldrivers client if the filter expression
1277-
// contains any of the relevant fields.
1278-
func initLOLDriversClient(flds []Field) {
1279-
for _, f := range flds {
1280-
if f.Name == fields.FileIsDriverVulnerable || f.Name == fields.FileIsDriverMalicious ||
1281-
f.Name == fields.ImageIsDriverVulnerable || f.Name == fields.ImageIsDriverMalicious {
1282-
loldrivers.InitClient(loldrivers.WithAsyncDownload())
1283-
}
1284-
}
1285-
}

pkg/filter/fields/fields.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package fields
2121
import (
2222
"github.com/rabbitstack/fibratus/pkg/kevent/kparams"
2323
"sort"
24+
"unicode"
2425
)
2526

2627
// FieldInfo is the field metadata descriptor.
@@ -33,6 +34,17 @@ type FieldInfo struct {
3334
Argument *Argument
3435
}
3536

37+
// isNumber is the field argument validation function that
38+
// returns true if all characters are digits.
39+
var isNumber = func(s string) bool {
40+
for _, c := range s {
41+
if !unicode.IsNumber(c) {
42+
return false
43+
}
44+
}
45+
return true
46+
}
47+
3648
// Argument defines field argument information.
3749
type Argument struct {
3850
// Optional indicates if the argument is optional.

0 commit comments

Comments
 (0)