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
21 changes: 21 additions & 0 deletions pkg/kevent/kparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ func (kpars Kparams) Set(name string, value kparams.Value, typ kparams.Type) err
return nil
}

// SetValue replaces the value for the given parameter name. It will return an error
// if the supplied parameter is not present in the parameter map.
func (kpars Kparams) SetValue(name string, value kparams.Value) error {
_, err := kpars.findParam(name)
if err != nil {
return fmt.Errorf("setting the value on a missing %q parameter is not allowed", name)
}
kpars[name].Value = value
return nil
}

// Get returns the raw value for given parameter name. It is the responsibility of the caller to probe type assertion
// on the value before yielding its underlying type.
func (kpars Kparams) Get(name string) (kparams.Value, error) {
Expand Down Expand Up @@ -265,6 +276,16 @@ func (kpars Kparams) GetUint32(name string) (uint32, error) {
return v, nil
}

// MustGetUint32 returns the underlying uint32 value parameter. It panics if
// an error occurs while trying to get the parameter.
func (kpars Kparams) MustGetUint32(name string) uint32 {
v, err := kpars.GetUint32(name)
if err != nil {
panic(err)
}
return v
}

// GetInt32 returns the underlying int32 value from the parameter.
func (kpars Kparams) GetInt32(name string) (int32, error) {
kpar, err := kpars.findParam(name)
Expand Down
5 changes: 5 additions & 0 deletions pkg/kevent/kparam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@ func TestKparams(t *testing.T) {
mode := filemode.(fs.FileShareMode)

assert.Equal(t, "r-d", mode.String())

require.NoError(t, kpars.SetValue(kparams.FileName, "\\Device\\HarddiskVolume2\\Windows\\system32\\KERNEL32.dll"))
filename1, err := kpars.GetString(kparams.FileName)
require.NoError(t, err)
assert.Equal(t, "\\Device\\HarddiskVolume2\\Windows\\system32\\KERNEL32.dll", filename1)
}
78 changes: 41 additions & 37 deletions pkg/kstream/interceptors/ps_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (
)

// systemRootRegexp is the regular expression for detecting path with unexpanded SystemRoot environment variable
var systemRootRegexp = regexp.MustCompile(`%SystemRoot%|\\SystemRoot`)
var systemRootRegexp = regexp.MustCompile(`%SystemRoot%|^\\SystemRoot|%systemroot%`)

// procYaraScans stores the total count of yara process scans
var procYaraScans = expvar.NewInt("yara.proc.scans")
Expand Down Expand Up @@ -72,29 +72,37 @@ func newPsInterceptor(snap ps.Snapshotter, yara yara.Scanner) KstreamInterceptor

func (ps psInterceptor) Intercept(kevt *kevent.Kevent) (*kevent.Kevent, bool, error) {
switch kevt.Type {
case ktypes.CreateProcess, ktypes.TerminateProcess, ktypes.EnumProcess:
comm, err := kevt.Kparams.GetString(kparams.Comm)
case ktypes.CreateProcess,
ktypes.TerminateProcess,
ktypes.EnumProcess:
cmdline, err := kevt.Kparams.GetString(kparams.Comm)
if err != nil {
return kevt, true, err
}
// if leading/trailing quotes are found, get rid of them
if cmdline[0] == '"' && cmdline[len(cmdline)-1] == '"' {
cmdline = cmdline[1 : len(cmdline)-1]
}
// expand all variations of the SystemRoot env variable
if systemRootRegexp.MatchString(cmdline) {
cmdline = systemRootRegexp.ReplaceAllString(cmdline, os.Getenv("SystemRoot"))
}
// some system processes are reported without the path in command line
if !strings.Contains(comm, `\\:`) {
_, ok := sysProcs[comm]
if strings.Index(cmdline, `:\\`) != 1 {
proc, _ := kevt.Kparams.GetString(kparams.ProcessName)
_, ok := sysProcs[proc]
if ok {
_ = kevt.Kparams.Set(kparams.Comm, filepath.Join(os.Getenv("SystemRoot"), comm), kparams.UnicodeString)
cmdline = filepath.Join(os.Getenv("SystemRoot"), "System32", cmdline)
}
}
// to compose the full executable string we extract the path
// from the process's command line by expanding the `SystemRoot`
// env variable accordingly and also removing rubbish characters
i := strings.Index(comm, ".exe")
// append executable path parameter
i := strings.Index(strings.ToLower(cmdline), ".exe")
if i > 0 {
exe := strings.Replace(comm[0:i+4], "\"", "", -1)
if strings.Contains(exe, "SystemRoot") {
exe = systemRootRegexp.ReplaceAllString(exe, os.Getenv("SystemRoot"))
}
exe := cmdline[0 : i+4]
kevt.Kparams.Append(kparams.Exe, kparams.UnicodeString, exe)
}
_ = kevt.Kparams.SetValue(kparams.Comm, cmdline)

// convert hexadecimal PID values to integers
pid, err := kevt.Kparams.GetHexAsUint32(kparams.ProcessID)
if err != nil {
Expand All @@ -116,10 +124,9 @@ func (ps psInterceptor) Intercept(kevt *kevent.Kevent) (*kevent.Kevent, bool, er
// get the process's start time and append it to the parameters
started, err := getStartTime(pid)
if err != nil {
log.Warnf("couldn't get process (%d) start time: %v", pid, err)
} else {
_ = kevt.Kparams.Append(kparams.StartTime, kparams.Time, started)
started = kevt.Timestamp
}
_ = kevt.Kparams.Append(kparams.StartTime, kparams.Time, started)
}
if ps.yara != nil && kevt.Type == ktypes.CreateProcess {
// run yara scanner on the target process
Expand All @@ -133,10 +140,10 @@ func (ps psInterceptor) Intercept(kevt *kevent.Kevent) (*kevent.Kevent, bool, er
}
return kevt, false, ps.snap.Write(kevt)
}

return kevt, false, ps.snap.Remove(kevt)

case ktypes.CreateThread, ktypes.TerminateThread, ktypes.EnumThread:
case ktypes.CreateThread,
ktypes.TerminateThread,
ktypes.EnumThread:
pid, err := kevt.Kparams.GetHexAsUint32(kparams.ProcessID)
if err != nil {
return kevt, true, err
Expand All @@ -151,43 +158,40 @@ func (ps psInterceptor) Intercept(kevt *kevent.Kevent) (*kevent.Kevent, bool, er
if err := kevt.Kparams.Set(kparams.ThreadID, tid, kparams.TID); err != nil {
return kevt, true, err
}

if kevt.Type != ktypes.TerminateThread {
return kevt, false, ps.snap.Write(kevt)
}

return kevt, false, ps.snap.Remove(kevt)

case ktypes.OpenProcess, ktypes.OpenThread:
case ktypes.OpenProcess,
ktypes.OpenThread:
pid, err := kevt.Kparams.GetUint32(kparams.ProcessID)
if err != nil {
return kevt, true, err
}

proc := ps.snap.Find(pid)
if proc != nil {
kevt.Kparams.Append(kparams.Exe, kparams.UnicodeString, proc.Exe)
kevt.Kparams.Append(kparams.ProcessName, kparams.UnicodeString, proc.Name)
kevt.Kparams.Append(kparams.Exe, kparams.UnicodeString, proc.Exe).
Append(kparams.ProcessName, kparams.UnicodeString, proc.Name)
}
_ = kevt.Kparams.Set(kparams.ProcessID, pid, kparams.PID)

// format the status code
status, err := kevt.Kparams.GetUint32(kparams.NTStatus)
if err == nil {
_ = kevt.Kparams.Set(kparams.NTStatus, formatStatus(status, kevt), kparams.UnicodeString)
}
status := kevt.Kparams.MustGetUint32(kparams.NTStatus)
_ = kevt.Kparams.Set(kparams.NTStatus, formatStatus(status, kevt), kparams.UnicodeString)

// convert desired access mask to hex value and transform
// the access mask to a list of symbolical names
desiredAccess, err := kevt.Kparams.GetUint32(kparams.DesiredAccess)
if err == nil {
_ = kevt.Kparams.Set(kparams.DesiredAccess, toHex(desiredAccess), kparams.AnsiString)
}
access := kevt.Kparams.MustGetUint32(kparams.DesiredAccess)
_ = kevt.Kparams.Set(kparams.DesiredAccess, toHex(access), kparams.AnsiString)

if kevt.Type == ktypes.OpenProcess {
kevt.Kparams.Append(kparams.DesiredAccessNames, kparams.Slice, process.DesiredAccess(desiredAccess).Flags())
kevt.Kparams.Append(kparams.DesiredAccessNames, kparams.Slice, process.DesiredAccess(access).Flags())
} else {
kevt.Kparams.Append(kparams.DesiredAccessNames, kparams.Slice, thread.DesiredAccess(desiredAccess).Flags())
kevt.Kparams.Append(kparams.DesiredAccessNames, kparams.Slice, thread.DesiredAccess(access).Flags())
}
return kevt, false, nil
}

return kevt, true, nil
}

Expand Down
21 changes: 20 additions & 1 deletion pkg/kstream/interceptors/ps_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ func TestPsInterceptorIntercept(t *testing.T) {
assert.Equal(t, "C:\\Windows\\System32\\smss.exe", exe)

tpid := fmt.Sprintf("%x", os.Getpid())

kpars2 := kevent.Kparams{
kparams.Comm: {Name: kparams.Comm, Type: kparams.UnicodeString, Value: "C:\\Windows\\System32\\smss.exe"},
kparams.Comm: {Name: kparams.Comm, Type: kparams.UnicodeString, Value: "\"C:\\Windows\\System32\\smss.exe\""},
kparams.ProcessID: {Name: kparams.ProcessID, Type: kparams.HexInt32, Value: kparams.Hex(tpid)},
kparams.ProcessParentID: {Name: kparams.ProcessParentID, Type: kparams.HexInt32, Value: kparams.Hex("26c")},
}
Expand All @@ -89,4 +90,22 @@ func TestPsInterceptorIntercept(t *testing.T) {
require.NoError(t, err)

require.True(t, kevt2.Kparams.Contains(kparams.StartTime))

cmdline, _ := kevt2.Kparams.GetString(kparams.Comm)
require.Equal(t, "C:\\Windows\\System32\\smss.exe", cmdline)

kpars3 := kevent.Kparams{
kparams.Comm: {Name: kparams.Comm, Type: kparams.UnicodeString, Value: "csrss.exe"},
kparams.ProcessName: {Name: kparams.ProcessName, Type: kparams.UnicodeString, Value: "csrss.exe"},
}

kevt3 := &kevent.Kevent{
Type: ktypes.CreateProcess,
Kparams: kpars3,
}

_, _, err = psi.Intercept(kevt3)
require.NoError(t, err)
cmdline1, _ := kevt3.Kparams.GetString(kparams.Comm)
require.Equal(t, "C:\\Windows\\System32\\csrss.exe", cmdline1)
}
12 changes: 1 addition & 11 deletions pkg/ps/types/types_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,17 +218,7 @@ func NewFromKcap(buf []byte) (*PS, error) {
return &ps, nil
}

func splitArgs(comm string) []string {
ext := strings.Index(comm, ".exe")
if ext == -1 {
return []string{}
}
ext += 5
if ext > len(comm) {
return []string{}
}
return strings.Fields(comm[ext:])
}
func splitArgs(cmdline string) []string { return strings.Fields(cmdline) }

// AddThread adds a thread to process's state descriptor.
func (ps *PS) AddThread(thread Thread) {
Expand Down
13 changes: 13 additions & 0 deletions pkg/ps/types/types_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package types

import (
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/require"
"testing"
)

Expand Down Expand Up @@ -59,3 +60,15 @@ func TestVisit(t *testing.T) {

assert.Equal(t, expected1, parents1)
}

func TestPSArgs(t *testing.T) {
ps := NewPS(
233,
4532,
"spotify.exe",
"",
"C:\\Users\\admin\\AppData\\Roaming\\Spotify\\Spotify.exe --type=crashpad-handler /prefetch:7 --max-uploads=5 --max-db-size=20 --max-db-age=5 --monitor-self-annotation=ptype=crashpad-handler \"--metrics-dir=C:\\Users\\admin\\AppData\\Local\\Spotify\\User Data\" --url=https://crashdump.spotify.com:443/ --annotation=platform=win32 --annotation=product=spotify",
Thread{}, nil)
require.Len(t, ps.Args, 12)
require.Equal(t, "/prefetch:7", ps.Args[2])
}