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
1 change: 1 addition & 0 deletions pkg/filter/ql/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ var funcs = map[string]FunctionDef{
functions.IsAbsFn.String(): &functions.IsAbs{},
functions.VolumeFn.String(): &functions.Volume{},
functions.GetRegValueFn.String(): &functions.GetRegValue{},
functions.YaraFn.String(): &functions.Yara{},
}

// FunctionDef is the interface that all function definitions have to satisfy.
Expand Down
Binary file added pkg/filter/ql/functions/_fixtures/yara-test.dll
Binary file not shown.
5 changes: 1 addition & 4 deletions pkg/filter/ql/functions/glob.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ func (f Glob) Call(args []interface{}) (interface{}, bool) {
if len(args) < 1 {
return false, false
}
pattern, ok := args[0].(string)
if !ok {
return false, false
}
pattern := parseString(0, args)
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, true
Expand Down
36 changes: 18 additions & 18 deletions pkg/filter/ql/functions/indexof.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@ import (
"strings"
)

// index is the type alias for the string position search order
type index uint8
// IndexPosition is the type alias for the string position search order
type IndexPosition uint8

const (
unknown index = iota
first // Index
any // IndexAny
last // LastIndex
lastany // LastIndexAny
UnknownIndex IndexPosition = iota
FirstIndex // Index
AnyIndex // IndexAny
LastIndex // LastIndex
LastAnyIndex // LastIndexAny
)

var indexMappings = map[string]index{
"first": first,
"any": any,
"last": last,
"lastany": lastany,
var indexMappings = map[string]IndexPosition{
"first": FirstIndex,
"any": AnyIndex,
"last": LastIndex,
"lastany": LastAnyIndex,
}

func indexFromString(s string) index { return indexMappings[s] }
func indexFromString(s string) IndexPosition { return indexMappings[s] }

// IndexOf returns the index of the instance of substring in a given string
// depending on the provided search order.
Expand All @@ -58,13 +58,13 @@ func (f IndexOf) Call(args []interface{}) (interface{}, bool) {
}
// index search order
switch indexFromString(parseString(2, args)) {
case first:
case FirstIndex:
return strings.Index(str, substr), true
case any:
case AnyIndex:
return strings.IndexAny(str, substr), true
case last:
case LastIndex:
return strings.LastIndex(str, substr), true
case lastany:
case LastAnyIndex:
return strings.LastIndexAny(str, substr), true
default:
return false, false
Expand All @@ -83,7 +83,7 @@ func (f IndexOf) Desc() FunctionDesc {
if len(args) == 2 {
return nil
}
if len(args) == 3 && indexFromString(args[2]) == unknown {
if len(args) == 3 && indexFromString(args[2]) == UnknownIndex {
return fmt.Errorf("%s is not a valid index search order. Available options are: first,any,last,lastany", args[2])
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/filter/ql/functions/length.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (f Length) Desc() FunctionDesc {
desc := FunctionDesc{
Name: LengthFn,
Args: []FunctionArgDesc{
{Keyword: "string/slice", Types: []ArgType{Field, Slice, Func}, Required: true},
{Keyword: "string|slice", Types: []ArgType{Field, Slice, Func}, Required: true},
},
}
return desc
Expand Down
2 changes: 1 addition & 1 deletion pkg/filter/ql/functions/minidump.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (f IsMinidump) Call(args []interface{}) (interface{}, bool) {
if len(args) < 1 {
return false, false
}
path := args[0].(string)
path := parseString(0, args)

file, err := os.Open(path)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/filter/ql/functions/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const (
VolumeFn
// GetRegValueFn represents the GET_REG_VALUE function
GetRegValueFn
// YaraFn represents the YARA function
YaraFn
)

// ArgType is the type alias for the argument value type.
Expand Down Expand Up @@ -204,14 +206,16 @@ func (f Fn) String() string {
return "VOLUME"
case GetRegValueFn:
return "GET_REG_VALUE"
case YaraFn:
return "YARA"
default:
return "UNDEFINED"
}
}

// parseString yields a string value from the specific position in the args slice.
func parseString(index int, args []interface{}) string {
if index > len(args) {
if index > len(args)-1 {
return ""
}
s, ok := args[index].(string)
Expand Down
141 changes: 141 additions & 0 deletions pkg/filter/ql/functions/yara.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//go:build yara
// +build yara

/*
* Copyright 2021-2022 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 functions

import (
"fmt"
"github.com/hillu/go-yara/v4"
"github.com/rabbitstack/fibratus/pkg/util/multierror"
log "github.com/sirupsen/logrus"
"strings"
"time"
)

// scanTimeout specifies the timeout interval for the scan operation
const scanTimeout = time.Second * 10

// Yara provides signature-based detection in filters and rules.
// YARA is a tool aimed at (but not limited to) helping malware
// researchers to identify and classify malware samples. With YARA
// you can create descriptions of malware families based on textual
// or binary patterns. Depending on the parameter type supplied to this
// function, the scan can be performed on the process, filename or a
// memory block.
type Yara struct{}

func (f Yara) Call(args []interface{}) (interface{}, bool) {
if len(args) < 2 {
return false, false
}
var rules string
var vars map[string]interface{}
switch r := args[1].(type) {
case string:
rules = r
case []string:
rules = strings.Join(r, " ")
}
if len(args) > 3 {
vars, _ = args[2].(map[string]interface{})
}
scanner, err := f.newScanner(rules, vars)
if err != nil {
log.Warnf("erroneous scanner in YARA function: %v: %s", err, rules)
return false, true
}
defer scanner.Destroy()

var cb yara.MatchRules
switch n := args[0].(type) {
case uint32: // pid
err = scanner.SetCallback(&cb).ScanProc(int(n))
case string: // file
err = scanner.SetCallback(&cb).ScanFile(n)
case []byte: // mem block
err = scanner.SetCallback(&cb).ScanMem(n)
default: // invalid type
return false, false
}
if err != nil {
log.Warnf("YARA function scan failed: %v", err)
return false, true
}
if len(cb) > 0 {
log.Debugf("YARA function produced %d match(es)", len(cb))
for _, match := range cb {
log.Debugf("Matched YARA rule: %s", match.Rule)
}
}
return len(cb) > 0, true
}

func (f Yara) Desc() FunctionDesc {
desc := FunctionDesc{
Name: YaraFn,
Args: []FunctionArgDesc{
{Keyword: "pid|file|bytes", Types: []ArgType{Field, Func, String, Number}, Required: true},
{Keyword: "rules", Types: []ArgType{Field, Func, String}, Required: true},
{Keyword: "vars", Types: []ArgType{Field, Func, String}},
},
}
return desc
}

func (f Yara) Name() Fn { return YaraFn }

func (f Yara) newScanner(rules string, vars map[string]interface{}) (*yara.Scanner, error) {
c, err := yara.NewCompiler()
if err != nil {
return nil, err
}
defer c.Destroy()
if err := c.AddString(rules, ""); err != nil {
return nil, err
}
for k, v := range vars {
if err := c.DefineVariable(k, v); err != nil {
return nil, err
}
}
if len(c.Errors) > 0 {
return nil, parseCompilerErrors(c.Errors)
}
r, err := c.GetRules()
if err != nil {
return nil, err
}
scanner, err := yara.NewScanner(r)
if err != nil {
return nil, err
}
scanner.SetFlags(yara.ScanFlagsFastMode)
scanner.SetTimeout(scanTimeout)
return scanner, nil
}

func parseCompilerErrors(errors []yara.CompilerMessage) error {
errs := make([]error, len(errors))
for i, err := range errors {
errs[i] = fmt.Errorf("%s, line: %d", err.Text, err.Line)
}
return multierror.Wrap(errs...)
}
Loading