зеркало из https://github.com/mozilla/mig.git
Коммит
de41ae30a0
12
.travis.yml
12
.travis.yml
|
@ -12,7 +12,7 @@ services:
|
|||
before_install:
|
||||
# this is a fix to get rng-tools to work in travis-ci
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install --yes rng-tools
|
||||
- sudo apt-get install --yes rng-tools autoconf automake gcc
|
||||
- sudo rm -f /dev/random
|
||||
- sudo mknod -m 0666 /dev/random c 1 9
|
||||
- echo HRNGDEVICE=/dev/urandom | sudo tee /etc/default/rng-tools
|
||||
|
@ -20,6 +20,14 @@ before_install:
|
|||
script:
|
||||
- export OLDPATH=$(pwd)
|
||||
- cd
|
||||
- curl -OL https://github.com/VirusTotal/yara/archive/v3.5.0.tar.gz
|
||||
- tar -zxf v3.5.0.tar.gz
|
||||
- cd yara-3.5.0
|
||||
- ./bootstrap.sh
|
||||
- ./configure --disable-shared --disable-magic --disable-cuckoo --without-crypto
|
||||
- make
|
||||
- sudo make install
|
||||
- cd ..
|
||||
- mkdir -p "$GOPATH/src/mig.ninja/"
|
||||
- mv "$OLDPATH" "$GOPATH/src/mig.ninja/"
|
||||
- cd "$GOPATH/src/mig.ninja/mig"
|
||||
|
@ -33,7 +41,7 @@ script:
|
|||
- diff client/mig/available_modules.go conf/available_modules.go
|
||||
# enable all the modules we have for the test
|
||||
- sed -i 's,//_,_,' conf/available_modules.go
|
||||
- make
|
||||
- make WITHYARA=yes
|
||||
- docker build -t mozilla/mig .
|
||||
- |
|
||||
if [ ! -z "$TRAVIS_TAG" ]; then
|
||||
|
|
|
@ -6,7 +6,7 @@ MAINTAINER Mozilla
|
|||
RUN apt-get update && \
|
||||
apt-get install -y sudo golang git make \
|
||||
curl rng-tools tmux postgresql rabbitmq-server \
|
||||
libreadline-dev && \
|
||||
libreadline-dev automake autoconf libtool && \
|
||||
echo '%mig ALL=(ALL:ALL) NOPASSWD:ALL' > /etc/sudoers.d/mig && \
|
||||
groupadd -g 10001 mig && \
|
||||
useradd -g 10001 -u 10001 -d /mig -m mig
|
||||
|
|
46
Makefile
46
Makefile
|
@ -14,6 +14,19 @@ else
|
|||
BINSUFFIX := ""
|
||||
endif
|
||||
|
||||
# Set this to yes if you want yara support and want to use the yara module
|
||||
#
|
||||
# This assumes yara has been compiled with the following options:
|
||||
# --disable-shared --disable-magic --disable-cuckoo --without-crypto
|
||||
#
|
||||
# If you have built yara some other way or have yara shared libraries
|
||||
# installed you will need to adjust the makefile
|
||||
#
|
||||
# You may have to set the CPATH and LIBRARY_PATH environment variables
|
||||
# if you have installed the yara headers and library somewhere the build
|
||||
# tools can't locate
|
||||
WITHYARA=no
|
||||
|
||||
# These variables control signature operations used when building various
|
||||
# targets on OSX.
|
||||
#
|
||||
|
@ -21,7 +34,7 @@ endif
|
|||
# sign the mig-agent and mig-loader binaries when built on OSX. If empty,
|
||||
# the compiled binaries will not be signed.
|
||||
#
|
||||
# OSXPACKSIGID if set will result in the specified identify being used to
|
||||
# OSXPACKSIGID if set will result in the specified identity being used to
|
||||
# sign the mig-loader package (osx-loader-pkg). If empty the .pkg will not
|
||||
# be signed.
|
||||
#
|
||||
|
@ -83,6 +96,7 @@ endif
|
|||
GCC := gcc
|
||||
CFLAGS :=
|
||||
LDFLAGS :=
|
||||
CGOLDFLAGS :=
|
||||
GOOPTS :=
|
||||
GO := GOOS=$(OS) GOARCH=$(ARCH) GO15VENDOREXPERIMENT=1 go
|
||||
GOGETTER := GOPATH=$(shell pwd)/.tmpdeps go get -d
|
||||
|
@ -98,6 +112,18 @@ CLIENTTARGETS := mig-cmd mig-console mig-action-generator mig-action-verifier
|
|||
AGENTTARGETS := mig-agent mig-loader
|
||||
ALLTARGETS := $(AGENTTARGETS) $(SERVERTARGETS) $(CLIENTTARGETS)
|
||||
|
||||
ifeq ($(WITHYARA),yes)
|
||||
ifeq ($(OS),linux)
|
||||
CGOLDFLAGS += -lyara -lm
|
||||
else ifeq ($(OS),darwin)
|
||||
# Nothing special required here for this to work on darwin
|
||||
else
|
||||
$(error WITHYARA not supported for this platform)
|
||||
endif
|
||||
endif
|
||||
|
||||
export CGO_LDFLAGS = $(CGOLDFLAGS)
|
||||
|
||||
all: test $(ALLTARGETS)
|
||||
|
||||
tag:
|
||||
|
@ -195,6 +221,7 @@ go_vendor_dependencies:
|
|||
$(GOGETTER) golang.org/x/net/ipv4
|
||||
$(GOGETTER) golang.org/x/net/ipv6
|
||||
$(GOGETTER) gopkg.in/gcfg.v1
|
||||
$(GOGETTER) github.com/hillu/go-yara
|
||||
$(GOGETTER) github.com/cheggaaa/pb
|
||||
$(GOGETTER) github.com/stretchr/testify
|
||||
$(GOGETTER) github.com/fsnotify/fsnotify
|
||||
|
@ -424,7 +451,22 @@ test: test-modules
|
|||
|
||||
test-modules:
|
||||
# test all modules
|
||||
$(GO) test mig.ninja/mig/modules/...
|
||||
$(GO) test mig.ninja/mig/modules/
|
||||
$(GO) test mig.ninja/mig/modules/agentdestroy
|
||||
$(GO) test mig.ninja/mig/modules/example
|
||||
$(GO) test mig.ninja/mig/modules/examplepersist
|
||||
$(GO) test mig.ninja/mig/modules/file
|
||||
$(GO) test mig.ninja/mig/modules/fswatch
|
||||
$(GO) test mig.ninja/mig/modules/memory
|
||||
$(GO) test mig.ninja/mig/modules/netstat
|
||||
$(GO) test mig.ninja/mig/modules/ping
|
||||
$(GO) test mig.ninja/mig/modules/pkg
|
||||
$(GO) test mig.ninja/mig/modules/scribe
|
||||
$(GO) test mig.ninja/mig/modules/timedrift
|
||||
ifeq ($(WITHYARA),yes)
|
||||
$(GO) test mig.ninja/mig/modules/yara
|
||||
endif
|
||||
|
||||
|
||||
clean-agent:
|
||||
if [ -d bin/ ]; then \
|
||||
|
|
|
@ -16,5 +16,6 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig.ninja/mig/modules/yara"
|
||||
//_ "mig.ninja/mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -16,5 +16,6 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig.ninja/mig/modules/yara"
|
||||
//_ "mig.ninja/mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -16,5 +16,6 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig.ninja/mig/modules/yara"
|
||||
//_ "mig.ninja/mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -16,5 +16,6 @@ import (
|
|||
_ "mig.ninja/mig/modules/pkg"
|
||||
_ "mig.ninja/mig/modules/scribe"
|
||||
_ "mig.ninja/mig/modules/timedrift"
|
||||
//_ "mig.ninja/mig/modules/yara"
|
||||
//_ "mig.ninja/mig/modules/example"
|
||||
)
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
=================================
|
||||
Mozilla InvestiGator: yara module
|
||||
=================================
|
||||
:Author: Aaron Meihm <ameihm@mozilla.com>
|
||||
|
||||
.. sectnum::
|
||||
.. contents:: Table of Contents
|
||||
|
||||
The yara module provides the ability to scan systems the agent is running on
|
||||
for objects which match provided yara rules. An investigator can send a list of
|
||||
yara rules to the MIG agents along with an indication of what objects should be
|
||||
scanned, and the agents will return any objects which matched and the rules that
|
||||
matched against them.
|
||||
|
||||
Scanning is currently limited to files only at the moment.
|
||||
|
||||
Building MIG with Yara support
|
||||
------------------------------
|
||||
Yara support is not enabled by default and requires certain dependencies on the
|
||||
build system to enable. Specifically, you will want the to make sure that the
|
||||
`yara libraries <https://github.com/VirusTotal/yara>`_ are installed on the system
|
||||
you are building MIG on.
|
||||
|
||||
To ensure that any systems with a yara enabled agent do not need to have the yara
|
||||
library installed, MIG can be built with the yara library statically linked into
|
||||
the MIG binary.
|
||||
|
||||
Fetch and install yara with the required options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Download the yara tarball and compile it with the required options. You will need
|
||||
a working c compiler in addition to automake and autoconf.
|
||||
|
||||
.. code::
|
||||
|
||||
$ curl -OL https://github.com/VirusTotal/yara/archive/v3.5.0.tar.gz
|
||||
$ tar -zxvf v3.5.0.tar.gz
|
||||
$ cd yara-3.5.0
|
||||
$ ./bootstrap.sh
|
||||
$ ./configure --disable-shared --disable-magic --disable-cuckoo --without-crypto
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
From here the agent can be compiled with yara support. The yara module should be
|
||||
enabled in `conf/available_modules.go` (or whatever you have the Makefile variable
|
||||
AVAILMOD set to). Then the agent can be compiled with yara support.
|
||||
|
||||
.. code::
|
||||
|
||||
$ make mig-agent WITHYARA=yes
|
||||
|
||||
The previous example applies to Linux. If you are building an OSX agent, you might
|
||||
need a few extra environment variables to help locate things, such as:
|
||||
|
||||
.. code::
|
||||
|
||||
$ env CPATH=/my/path/to/yara/include LIBRARY_PATH=/my/path/to/yara/lib make mig-agent WITHYARA=yes
|
||||
|
||||
This should result in a mig-agent with the yara library builtin, which will work when
|
||||
deployed to hosts without libyara. Note that, as modules are used in other MIG components
|
||||
such as the client tools, you will likely want to set WITHYARA=yes when building the
|
||||
client tools as well.
|
||||
|
||||
`tools/standalone_install.sh` also includes yara support, so can be reviewed for some
|
||||
hints on the build process.
|
||||
|
||||
Usage
|
||||
-----
|
||||
Two options must be provided to the yara module.
|
||||
|
||||
The `rules` should specify the path on your system containing the yara rules
|
||||
you want to send to the agents.
|
||||
|
||||
The `files` option should be set to a string which is essentially the arguments
|
||||
you would provide to the file module. See the help output of the file module for
|
||||
more information.
|
||||
|
||||
The following example shows a set of rules being used to scan everything in /bin
|
||||
and in /sbin on each agent system.
|
||||
|
||||
.. code::
|
||||
|
||||
$ mig yara -t all -rules ./testrules.yara -files '-path /bin -path /sbin -name .'
|
|
@ -0,0 +1,126 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]
|
||||
|
||||
package yara /* import "mig.ninja/mig/modules/yara" */
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func printHelp(isCmd bool) {
|
||||
dash := ""
|
||||
if isCmd {
|
||||
dash = "-"
|
||||
}
|
||||
fmt.Printf(`Query parameters
|
||||
----------------
|
||||
%srules <path> - yara rules path
|
||||
ex: path ./myrules.yar
|
||||
processes yara rules on agent
|
||||
|
||||
%sfiles <spec> - scan files using rules
|
||||
ex: files '-path /bin -path /sbin -name ssh'
|
||||
indicate files that should be scanned, argument is
|
||||
parameters as supplied to the file module for scanning,
|
||||
each matching file will be scanned using rules. see the
|
||||
help output for the file module for available options.
|
||||
`, dash, dash)
|
||||
}
|
||||
|
||||
func (r *run) ParamsCreator() (interface{}, error) {
|
||||
p := newParameters()
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for {
|
||||
fmt.Printf("search> ")
|
||||
scanmore := scanner.Scan()
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Println("Invalid input. Try again")
|
||||
continue
|
||||
}
|
||||
if !scanmore {
|
||||
goto exit
|
||||
}
|
||||
input := scanner.Text()
|
||||
if input == "done" {
|
||||
goto exit
|
||||
} else if input == "help" {
|
||||
printHelp(false)
|
||||
continue
|
||||
}
|
||||
arr := strings.SplitN(input, " ", 2)
|
||||
if len(arr) != 2 {
|
||||
fmt.Printf("Invalid input format!\n")
|
||||
printHelp(false)
|
||||
continue
|
||||
}
|
||||
checkType := arr[0]
|
||||
checkValue := arr[1]
|
||||
switch checkType {
|
||||
case "rules":
|
||||
rulebuf, err := ioutil.ReadFile(checkValue)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
continue
|
||||
}
|
||||
p.YaraRules = string(rulebuf)
|
||||
case "files":
|
||||
p.FileSearch = checkValue
|
||||
default:
|
||||
fmt.Printf("Invalid method!\nTry 'help'\n")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
r.Parameters = *p
|
||||
return r.Parameters, r.ValidateParameters()
|
||||
}
|
||||
|
||||
func (r *run) ParamsParser(args []string) (interface{}, error) {
|
||||
var (
|
||||
fs flag.FlagSet
|
||||
yaraPath string
|
||||
fileSearch string
|
||||
)
|
||||
|
||||
if len(args) < 1 || args[0] == "" || args[0] == "help" {
|
||||
printHelp(true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fs.Init("yara", flag.ContinueOnError)
|
||||
fs.StringVar(&yaraPath, "rules", "", "see help")
|
||||
fs.StringVar(&fileSearch, "files", "", "see help")
|
||||
err := fs.Parse(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := newParameters()
|
||||
|
||||
if yaraPath == "" {
|
||||
return nil, fmt.Errorf("-rules option is required")
|
||||
}
|
||||
rulebuf, err := ioutil.ReadFile(yaraPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.YaraRules = string(rulebuf)
|
||||
p.FileSearch = fileSearch
|
||||
// Right now file searching is the only supported search, so this
|
||||
// option must be specified.
|
||||
if p.FileSearch == "" {
|
||||
return nil, fmt.Errorf("-files option is required")
|
||||
}
|
||||
r.Parameters = *p
|
||||
|
||||
return r.Parameters, r.ValidateParameters()
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]
|
||||
|
||||
// yara module implementation for MIG.
|
||||
|
||||
package yara /* import "mig.ninja/mig/modules/yara" */
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
yara "github.com/hillu/go-yara"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mig.ninja/mig/modules"
|
||||
"mig.ninja/mig/modules/file"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
}
|
||||
|
||||
func (m *module) NewRun() modules.Runner {
|
||||
return new(run)
|
||||
}
|
||||
|
||||
func init() {
|
||||
modules.Register("yara", new(module))
|
||||
}
|
||||
|
||||
type run struct {
|
||||
Parameters parameters
|
||||
Results modules.Result
|
||||
}
|
||||
|
||||
func buildResults(e YaraElements, r *modules.Result) (buf []byte, err error) {
|
||||
r.Success = true
|
||||
r.Elements = e
|
||||
if len(e.Matches) > 0 {
|
||||
r.FoundAnything = true
|
||||
}
|
||||
buf, err = json.Marshal(r)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert sub-module arguments supplied as a string in the arguments of the
|
||||
// yara module query to a slice. For example, parses input like:
|
||||
// -path /etc -name test -content "test file"
|
||||
//
|
||||
// into:
|
||||
// ["-path", "/etc", "-name", "test", "content", "test file"]
|
||||
func moduleArguments(args string) (ret []string) {
|
||||
r := regexp.MustCompile("'.+'|\".+\"|\\S+")
|
||||
m := r.FindAllString(args, -1)
|
||||
for _, x := range m {
|
||||
b := strings.Trim(x, "\"")
|
||||
b = strings.Trim(b, "'")
|
||||
ret = append(ret, b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If a file scan is being conducted, this locates any files we will include in the
|
||||
// scan. This function makes use of the file module for this purpose.
|
||||
func findFiles(args []string) ([]string, error) {
|
||||
ret := make([]string, 0)
|
||||
|
||||
run := modules.Available["file"].NewRun()
|
||||
param, err := run.(modules.HasParamsParser).ParamsParser(args)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
buf, err := modules.MakeMessage(modules.MsgClassParameters, param, false)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
rdr := modules.NewModuleReader(bytes.NewReader(buf))
|
||||
|
||||
res := run.Run(rdr)
|
||||
var modresult modules.Result
|
||||
var sr file.SearchResults
|
||||
err = json.Unmarshal([]byte(res), &modresult)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
err = modresult.GetElements(&sr)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
p0, ok := sr["s1"]
|
||||
if !ok {
|
||||
return ret, fmt.Errorf("result in file module call was missing")
|
||||
}
|
||||
for _, x := range p0 {
|
||||
ret = append(ret, x.File)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *run) Run(in modules.ModuleReader) (resStr string) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
// return error in json
|
||||
r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
|
||||
r.Results.Success = false
|
||||
err, _ := json.Marshal(r.Results)
|
||||
resStr = string(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// Restrict go runtime processor utilization here, this might be moved
|
||||
// into a more generic agent module function at some point.
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
// Read module parameters from stdin
|
||||
err := modules.ReadInputParameters(in, &r.Parameters)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = r.ValidateParameters()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
yaracomp, err := yara.NewCompiler()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = yaracomp.AddString(r.Parameters.YaraRules, "default")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rules, err := yaracomp.GetRules()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e := &YaraElements{}
|
||||
if r.Parameters.FileSearch != "" {
|
||||
flist, err := findFiles(moduleArguments(r.Parameters.FileSearch))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, x := range flist {
|
||||
mr, err := rules.ScanFile(x, 0, time.Second*10)
|
||||
if err != nil {
|
||||
emsg := fmt.Sprintf("%v: %v", x, err)
|
||||
r.Results.Errors = append(r.Results.Errors, emsg)
|
||||
continue
|
||||
}
|
||||
if len(mr) != 0 {
|
||||
nm := YaraMatch{Object: x}
|
||||
for _, y := range mr {
|
||||
nm.MatchedRules = append(nm.MatchedRules, y.Rule)
|
||||
}
|
||||
e.Matches = append(e.Matches, nm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := buildResults(*e, &r.Results)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resStr = string(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *run) ValidateParameters() (err error) {
|
||||
if r.Parameters.YaraRules == "" {
|
||||
return fmt.Errorf("yara rules are a required option")
|
||||
}
|
||||
if r.Parameters.FileSearch != "" {
|
||||
run := modules.Available["file"].NewRun()
|
||||
_, err = run.(modules.HasParamsParser).ParamsParser(moduleArguments(r.Parameters.FileSearch))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
|
||||
var (
|
||||
elem YaraElements
|
||||
)
|
||||
|
||||
err = result.GetElements(&elem)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, x := range elem.Matches {
|
||||
var rn []string
|
||||
for _, y := range x.MatchedRules {
|
||||
rn = append(rn, y)
|
||||
}
|
||||
prints = append(prints, fmt.Sprintf("%v [%v]", x.Object, strings.Join(rn, ",")))
|
||||
}
|
||||
if !foundOnly {
|
||||
for _, we := range result.Errors {
|
||||
prints = append(prints, we)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type YaraMatch struct {
|
||||
Object string // Object matched (e.g., file name)
|
||||
MatchedRules []string // Matched rule
|
||||
}
|
||||
|
||||
type YaraElements struct {
|
||||
Matches []YaraMatch // Module returns a list of matches
|
||||
}
|
||||
|
||||
type parameters struct {
|
||||
YaraRules string `json:"yara"` // Yara rules as a string
|
||||
FileSearch string `json:"filesearch"` // file module parameters for file search
|
||||
}
|
||||
|
||||
func newParameters() *parameters {
|
||||
return ¶meters{}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]
|
||||
|
||||
package yara /* import "mig.ninja/mig/modules/yara" */
|
||||
|
||||
import (
|
||||
"mig.ninja/mig/testutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegistration(t *testing.T) {
|
||||
testutil.CheckModuleRegistration(t, "yara")
|
||||
}
|
|
@ -86,12 +86,15 @@ fi
|
|||
go_version=$(go version)
|
||||
echo $go_version | grep -E -q --regexp="go1\.[0-4]" && echo -e "installed version of go is ${go_version}\nwe need at least version 1.5" && fail
|
||||
|
||||
which git 2>&1 1>/dev/null || pkglist="$pkglist git"
|
||||
which make 2>&1 1>/dev/null || pkglist="$pkglist make"
|
||||
which gcc 2>&1 1>/dev/null || pkglist="$pkglist gcc"
|
||||
which tmux 2>&1 1>/dev/null || pkglist="$pkglist tmux"
|
||||
which curl 2>&1 1>/dev/null || pkglist="$pkglist curl"
|
||||
which rngd 2>&1 1>/dev/null || pkglist="$pkglist rng-tools"
|
||||
which git 2>&1 1>/dev/null || pkglist="$pkglist git"
|
||||
which make 2>&1 1>/dev/null || pkglist="$pkglist make"
|
||||
which gcc 2>&1 1>/dev/null || pkglist="$pkglist gcc"
|
||||
which tmux 2>&1 1>/dev/null || pkglist="$pkglist tmux"
|
||||
which curl 2>&1 1>/dev/null || pkglist="$pkglist curl"
|
||||
which rngd 2>&1 1>/dev/null || pkglist="$pkglist rng-tools"
|
||||
which autoconf 2>&1 1>/dev/null || pkglist="$pkglist autoconf"
|
||||
which automake 2>&1 1>/dev/null || pkglist="$pkglist automake"
|
||||
which libtool 2>&1 1>/dev/null || pkglist="$pkglist libtool"
|
||||
|
||||
if [ "$pkglist" != "" ]; then
|
||||
echo "missing packages: $pkglist"
|
||||
|
@ -117,6 +120,13 @@ if [ "$isRPM" = true ]; then
|
|||
sudo service postgresql restart
|
||||
fi
|
||||
|
||||
# Fetch and install a version of libyara with our desired configuration
|
||||
echo -e "\n---- Building libyara\n"
|
||||
curl -OL https://github.com/VirusTotal/yara/archive/v3.5.0.tar.gz || fail
|
||||
tar -zxf v3.5.0.tar.gz || fail
|
||||
(cd yara-3.5.0 && ./bootstrap.sh && ./configure --disable-shared --disable-magic --disable-cuckoo --without-crypto) || fail
|
||||
(cd yara-3.5.0 && make && sudo make install) || fail
|
||||
|
||||
echo -e "\n---- Building MIG Scheduler\n"
|
||||
make mig-scheduler || fail
|
||||
id mig || sudo useradd -r mig || fail
|
||||
|
@ -137,12 +147,12 @@ sudo chown mig /usr/local/bin/mig-worker-agent-verif || fail
|
|||
sudo chmod 550 /usr/local/bin/mig-worker-agent-verif || fail
|
||||
|
||||
echo -e "\n---- Building MIG Clients\n"
|
||||
make mig-console || fail
|
||||
make mig-console WITHYARA=yes || fail
|
||||
sudo cp bin/linux/amd64/mig-console /usr/local/bin/ || fail
|
||||
sudo chown mig /usr/local/bin/mig-console || fail
|
||||
sudo chmod 555 /usr/local/bin/mig-console || fail
|
||||
|
||||
make mig-cmd || fail
|
||||
make mig-cmd WITHYARA=yes || fail
|
||||
sudo cp bin/linux/amd64/mig /usr/local/bin/ || fail
|
||||
sudo chown mig /usr/local/bin/mig || fail
|
||||
sudo chmod 555 /usr/local/bin/mig || fail
|
||||
|
@ -354,7 +364,7 @@ var AGENTKEY = []byte("")
|
|||
EOF
|
||||
|
||||
echo -e "\n---- Building and running local agent\n"
|
||||
make mig-agent AGTCONF=conf/mig-agent-conf.go BUILDENV=demo || fail
|
||||
make mig-agent AGTCONF=conf/mig-agent-conf.go BUILDENV=demo WITHYARA=yes || fail
|
||||
sudo cp bin/linux/amd64/mig-agent-latest /sbin/mig-agent || fail
|
||||
sudo chown root /sbin/mig-agent || fail
|
||||
sudo chmod 500 /sbin/mig-agent || fail
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- tip
|
||||
dist: xenial
|
||||
sudo: required
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install bison flex automake autoconf libtool make gcc
|
||||
- wget --no-verbose -O- https://github.com/VirusTotal/yara/archive/v3.5.0.tar.gz | tar -C ${TRAVIS_BUILD_DIR} -xzf -
|
||||
- ( cd ${TRAVIS_BUILD_DIR}/yara-3.5.0 && ./bootstrap.sh && ./configure && make )
|
||||
- export CGO_CFLAGS=-I${TRAVIS_BUILD_DIR}/yara-3.5.0/libyara/include
|
||||
- export CGO_LDFLAGS=-L${TRAVIS_BUILD_DIR}/yara-3.5.0/libyara/.libs
|
||||
- export LD_LIBRARY_PATH=${TRAVIS_BUILD_DIR}/yara-3.5.0/libyara/.libs
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Hilko Bengen <bengen@hilluzination.de>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,94 @@
|
|||
# go-yara
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/hillu/go-yara?status.svg)](https://godoc.org/github.com/hillu/go-yara)
|
||||
[![Travis](https://travis-ci.org/hillu/go-yara.svg?branch=master)](https://travis-ci.org/hillu/go-yara)
|
||||
|
||||
Go bindings for [YARA](http://plusvic.github.io/yara/), staying as
|
||||
close as sensible to the library's C-API while taking inspiration from
|
||||
the `yara-python` implementation.
|
||||
|
||||
YARA 3.4.0 or higher is required for full functionality. If you need
|
||||
to build with YARA 3.3.0, please build with the `yara3.3` build tag.
|
||||
(The `compat-yara-3.3` branch has been removed.)
|
||||
|
||||
## Installation
|
||||
|
||||
### Unix
|
||||
|
||||
On a Unix system with libyara properly installed, this should work,
|
||||
provided that `GOPATH` is set:
|
||||
|
||||
```
|
||||
go get github.com/hillu/go-yara
|
||||
go install github.com/hillu/go-yara
|
||||
```
|
||||
|
||||
Depending on what location libyara and its headers have been
|
||||
installed, proper `CFLAGS` and `LDFLAGS` may have to be added to
|
||||
`cgo.go` or be specified via environment variables (`CGO_CFLAGS` and
|
||||
`CGO_LDFLAGS`).
|
||||
|
||||
Linker errors buried in the CGO output such as
|
||||
|
||||
undefined reference to `yr_compiler_add_file'
|
||||
|
||||
probably mean that the linker is looking at an old version of the YARA
|
||||
library.
|
||||
|
||||
### Cross-building for Windows
|
||||
|
||||
YARA and go-yara can be cross-built on a Debian system as long as the
|
||||
Go compiler contains Windows runtime libraries with CGO support
|
||||
([cf.](https://github.com/hillu/golang-go-cross)).
|
||||
|
||||
The YARA library is built from the source tree with the MinGW compiler
|
||||
using the usual `./configure && make && make install`. Then go-yara is
|
||||
built and installed to `GOPATH` using `go install`. Some environment
|
||||
variables need to be passed to the `go` tool:
|
||||
|
||||
- `GOOS`, `GOARCH` indicate the cross compilation
|
||||
target.
|
||||
- `CGO_ENABLED` is set to 1 beacuse it defaults to 0 when
|
||||
cross-compiling.
|
||||
- `CC` has to specified because the `go` tool has no knowledge on what
|
||||
C compiler to use (it defaults to the system C compiler, usually
|
||||
gcc).
|
||||
- The C compiler in turn needs to know where to find headers and
|
||||
libraries, these locations are specified via the `CGO_CFLAGS` and
|
||||
`CGO_LDFLAGS` variables.
|
||||
|
||||
32bit:
|
||||
|
||||
```
|
||||
cd ${YARA_SRC}
|
||||
./configure --host=i686-w64-mingw32 --disable-magic --disable-cuckoo --without-crypto
|
||||
make
|
||||
make install prefix=./i686-w64-mingw32
|
||||
cd ${GO_YARA_SRC}
|
||||
GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc \
|
||||
CGO_CFLAGS=-I${YARA_SRC}/i686-w64-mingw32/include \
|
||||
CGO_LDFLAGS=-L${YARA_SRC}/i686-w64-mingw32/lib \
|
||||
go install --ldflags '-extldflags "-static"' github.com/hillu/go-yara
|
||||
```
|
||||
|
||||
64bit:
|
||||
|
||||
```
|
||||
cd ${YARA_SRC}
|
||||
./configure --host=x86_64-w64-mingw32 --disable-magic --disable-cuckoo --without-crypto
|
||||
make
|
||||
make install prefix=./x86_64-w64-mingw32
|
||||
cd ${GO_YARA_SRC}
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc \
|
||||
CGO_CFLAGS=-I${YARA_SRC}/x86_64-w64-mingw32/include \
|
||||
CGO_LDFLAGS=-L${YARA_SRC}/x86_64-w64-mingw32/lib \
|
||||
go install --ldflags '-extldflags "-static"' github.com/hillu/go-yara
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
BSD 2-clause, see LICENSE file in the source distribution.
|
||||
|
||||
## Author
|
||||
|
||||
Hilko Bengen <bengen@hilluzination.de>
|
|
@ -0,0 +1,56 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
/*
|
||||
The closure type stores (pointers to) arbitrary data, returning a
|
||||
(usually small) uintptr. The uintptr value can be passed through C
|
||||
code to exported callback functions written in Go that can use it to
|
||||
access the data without violating the rules for passing pointers
|
||||
through C code.
|
||||
|
||||
Concurrent access to the stored data is protected through a
|
||||
sync.RWMutex.
|
||||
*/
|
||||
type closure struct {
|
||||
m map[uintptr]interface{}
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *closure) Put(elem interface{}) uintptr {
|
||||
c.Lock()
|
||||
if c.m == nil {
|
||||
c.m = make(map[uintptr]interface{})
|
||||
}
|
||||
defer c.Unlock()
|
||||
for i := uintptr(0); ; i++ {
|
||||
_, ok := c.m[i]
|
||||
if !ok {
|
||||
c.m[i] = elem
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *closure) Get(id uintptr) interface{} {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
if r, ok := c.m[id]; ok {
|
||||
return r
|
||||
}
|
||||
panic("get: element " + strconv.Itoa(int(id)) + " not found")
|
||||
}
|
||||
|
||||
func (c *closure) Delete(id uintptr) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if _, ok := c.m[id]; !ok {
|
||||
panic("delete: element " + strconv.Itoa(int(id)) + " not found")
|
||||
}
|
||||
delete(c.m, id)
|
||||
}
|
||||
|
||||
var callbackData closure
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package yara
|
||||
|
||||
// #cgo LDFLAGS: -lyara
|
||||
import "C"
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package yara
|
||||
|
||||
/*
|
||||
#ifdef _WIN32
|
||||
#define fdopen _fdopen
|
||||
#define dup _dup
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <yara.h>
|
||||
|
||||
// This signature should be generated by cgo from the exported
|
||||
// function below
|
||||
void compilerCallback(int, char*, int, char*, void*);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//export compilerCallback
|
||||
func compilerCallback(errorLevel C.int, filename *C.char, linenumber C.int, message *C.char, userData unsafe.Pointer) {
|
||||
c := callbackData.Get(uintptr(userData)).(*Compiler)
|
||||
msg := CompilerMessage{
|
||||
Filename: C.GoString(filename),
|
||||
Line: int(linenumber),
|
||||
Text: C.GoString(message),
|
||||
}
|
||||
switch errorLevel {
|
||||
case C.YARA_ERROR_LEVEL_ERROR:
|
||||
c.Errors = append(c.Errors, msg)
|
||||
case C.YARA_ERROR_LEVEL_WARNING:
|
||||
c.Warnings = append(c.Warnings, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// A Compiler encapsulates the YARA compiler that transforms rules
|
||||
// into YARA's internal, binary form which in turn is used for
|
||||
// scanning files or memory blocks.
|
||||
type Compiler struct {
|
||||
*compiler
|
||||
Errors []CompilerMessage
|
||||
Warnings []CompilerMessage
|
||||
}
|
||||
|
||||
type compiler struct {
|
||||
cptr *C.YR_COMPILER
|
||||
}
|
||||
|
||||
// A CompilerMessage contains an error or warning message produced
|
||||
// while compiling sets of rules using AddString or AddFile.
|
||||
type CompilerMessage struct {
|
||||
Filename string
|
||||
Line int
|
||||
Text string
|
||||
}
|
||||
|
||||
// NewCompiler creates a YARA compiler.
|
||||
func NewCompiler() (*Compiler, error) {
|
||||
var yrCompiler *C.YR_COMPILER
|
||||
if err := newError(C.yr_compiler_create(&yrCompiler)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &Compiler{compiler: &compiler{cptr: yrCompiler}}
|
||||
runtime.SetFinalizer(c.compiler, (*compiler).finalize)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *compiler) finalize() {
|
||||
C.yr_compiler_destroy(c.cptr)
|
||||
runtime.SetFinalizer(c, nil)
|
||||
}
|
||||
|
||||
// Destroy destroys the YARA data structure representing a compiler.
|
||||
// Since a Finalizer for the underlying YR_COMPILER structure is
|
||||
// automatically set up on creation, it should not be necessary to
|
||||
// explicitly call this method.
|
||||
func (c *Compiler) Destroy() {
|
||||
if c.compiler != nil {
|
||||
c.compiler.finalize()
|
||||
c.compiler = nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddFile compiles rules from a file. Rules are added to the
|
||||
// specified namespace.
|
||||
func (c *Compiler) AddFile(file *os.File, namespace string) (err error) {
|
||||
fd := C.dup(C.int(file.Fd()))
|
||||
fh, err := C.fdopen(fd, C.CString("r"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer C.fclose(fh)
|
||||
var ns *C.char
|
||||
if namespace != "" {
|
||||
ns = C.CString(namespace)
|
||||
defer C.free(unsafe.Pointer(ns))
|
||||
}
|
||||
filename := C.CString(file.Name())
|
||||
defer C.free(unsafe.Pointer(filename))
|
||||
id := callbackData.Put(c)
|
||||
defer callbackData.Delete(id)
|
||||
C.yr_compiler_set_callback(c.cptr, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), unsafe.Pointer(id))
|
||||
numErrors := int(C.yr_compiler_add_file(c.cptr, fh, ns, filename))
|
||||
if numErrors > 0 {
|
||||
var buf [1024]C.char
|
||||
msg := C.GoString(C.yr_compiler_get_error_message(
|
||||
c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
|
||||
err = errors.New(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddString compiles rules from a string. Rules are added to the
|
||||
// specified namespace.
|
||||
func (c *Compiler) AddString(rules string, namespace string) (err error) {
|
||||
var ns *C.char
|
||||
if namespace != "" {
|
||||
ns = C.CString(namespace)
|
||||
defer C.free(unsafe.Pointer(ns))
|
||||
}
|
||||
crules := C.CString(rules)
|
||||
defer C.free(unsafe.Pointer(crules))
|
||||
id := callbackData.Put(c)
|
||||
defer callbackData.Delete(id)
|
||||
C.yr_compiler_set_callback(c.cptr, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), unsafe.Pointer(id))
|
||||
numErrors := int(C.yr_compiler_add_string(c.cptr, crules, ns))
|
||||
if numErrors > 0 {
|
||||
var buf [1024]C.char
|
||||
msg := C.GoString(C.yr_compiler_get_error_message(
|
||||
c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
|
||||
err = errors.New(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DefineVariable defines a named variable for use by the compiler.
|
||||
// Boolean, int64, float64, and string types are supported.
|
||||
func (c *Compiler) DefineVariable(name string, value interface{}) (err error) {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
switch value.(type) {
|
||||
case bool:
|
||||
var v int
|
||||
if value.(bool) {
|
||||
v = 1
|
||||
}
|
||||
err = newError(C.yr_compiler_define_boolean_variable(
|
||||
c.cptr, cname, C.int(v)))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
value := toint64(value)
|
||||
err = newError(C.yr_compiler_define_integer_variable(
|
||||
c.cptr, cname, C.int64_t(value)))
|
||||
case float64:
|
||||
err = newError(C.yr_compiler_define_float_variable(
|
||||
c.cptr, cname, C.double(value.(float64))))
|
||||
case string:
|
||||
cvalue := C.CString(value.(string))
|
||||
defer C.free(unsafe.Pointer(cvalue))
|
||||
err = newError(C.yr_compiler_define_string_variable(
|
||||
c.cptr, cname, cvalue))
|
||||
default:
|
||||
err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetRules returns the compiled ruleset.
|
||||
func (c *Compiler) GetRules() (*Rules, error) {
|
||||
var yrRules *C.YR_RULES
|
||||
if err := newError(C.yr_compiler_get_rules(c.cptr, &yrRules)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &Rules{rules: &rules{cptr: yrRules}}
|
||||
runtime.SetFinalizer(r.rules, (*rules).finalize)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Compile compiles rules and an (optional) set of variables into a
|
||||
// Rules object in a single step.
|
||||
func Compile(rules string, variables map[string]interface{}) (r *Rules, err error) {
|
||||
var c *Compiler
|
||||
if c, err = NewCompiler(); err != nil {
|
||||
return
|
||||
}
|
||||
for k, v := range variables {
|
||||
if err = c.DefineVariable(k, v); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = c.AddString(rules, ""); err != nil {
|
||||
return
|
||||
}
|
||||
r, err = c.GetRules()
|
||||
return
|
||||
}
|
||||
|
||||
// MustCompile is like Compile but panics if the rules and optional
|
||||
// variables can't be compiled. Like regexp.MustCompile, it allows for
|
||||
// simple, safe initialization of global or test data.
|
||||
func MustCompile(rules string, variables map[string]interface{}) (r *Rules) {
|
||||
r, err := Compile(rules, variables)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package yara
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCompiler(t *testing.T) {
|
||||
c, _ := NewCompiler()
|
||||
if err := c.AddString(
|
||||
"rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }", "",
|
||||
); err != nil {
|
||||
t.Errorf("error: %s", err)
|
||||
}
|
||||
if err := c.AddString("xxx", ""); err == nil {
|
||||
t.Error("did not recognize error")
|
||||
} else {
|
||||
t.Logf("expected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPanic(t *testing.T) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
t.Error("MustCompile with broken data did not panic")
|
||||
} else {
|
||||
t.Logf("Everything ok, MustCompile panicked: %v", err)
|
||||
}
|
||||
}()
|
||||
_ = MustCompile("asflkjkl", nil)
|
||||
}
|
||||
|
||||
func TestWarnings(t *testing.T) {
|
||||
c, _ := NewCompiler()
|
||||
c.AddString("rule foo { bar }", "")
|
||||
if len(c.Errors) == 0 {
|
||||
t.Error()
|
||||
}
|
||||
t.Logf("Recorded Errors=%#v, Warnings=%#v", c.Errors, c.Warnings)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Making a copy of Compiler struct should not cause a crash.
|
||||
func TestCompilerFinalizer(t *testing.T) {
|
||||
var c Compiler
|
||||
func() {
|
||||
fmt.Println("Create compiler")
|
||||
c1, _ := NewCompiler()
|
||||
c = *c1
|
||||
}()
|
||||
fmt.Println("Trigger GC")
|
||||
runtime.GC()
|
||||
fmt.Println("Trigger Gosched")
|
||||
runtime.Gosched()
|
||||
fmt.Println("Manually call destructure on copy")
|
||||
c.Destroy()
|
||||
t.Log("Did not crash due to yr_*_destroy() being called twice. Yay.")
|
||||
}
|
||||
|
||||
// Making a copy of Rules struct should not cause a crash.
|
||||
func TestRulesFinalizer(t *testing.T) {
|
||||
var r Rules
|
||||
func() {
|
||||
fmt.Println("Create rules")
|
||||
r1, _ := Compile("rule test { condition: true }", nil)
|
||||
r = *r1
|
||||
}()
|
||||
fmt.Println("Trigger GC")
|
||||
runtime.GC()
|
||||
fmt.Println("Trigger Gosched")
|
||||
runtime.Gosched()
|
||||
fmt.Println("Manually call destructure on copy")
|
||||
r.Destroy()
|
||||
t.Log("Did not crash due to yr_*_destroy() being called twice. Yay.")
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package yara
|
||||
|
||||
// #include <yara.h>
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func newError(code C.int) error {
|
||||
if code == 0 {
|
||||
return nil
|
||||
}
|
||||
if str, ok := errorStrings[code]; ok {
|
||||
return errors.New(str)
|
||||
}
|
||||
return fmt.Errorf("unknown error %d", code)
|
||||
}
|
||||
|
||||
// FIXME: This should be generated from yara/error.h
|
||||
var errorStrings = map[C.int]string{
|
||||
C.ERROR_INSUFICIENT_MEMORY: "insufficient memory",
|
||||
C.ERROR_COULD_NOT_ATTACH_TO_PROCESS: "could not attach to process",
|
||||
C.ERROR_COULD_NOT_OPEN_FILE: "could not open file",
|
||||
C.ERROR_COULD_NOT_MAP_FILE: "could not map file",
|
||||
C.ERROR_INVALID_FILE: "invalid file",
|
||||
C.ERROR_CORRUPT_FILE: "corrupt file",
|
||||
C.ERROR_UNSUPPORTED_FILE_VERSION: "unsupported file version",
|
||||
C.ERROR_INVALID_REGULAR_EXPRESSION: "invalid regular expression",
|
||||
C.ERROR_INVALID_HEX_STRING: "invalid hex string",
|
||||
C.ERROR_SYNTAX_ERROR: "syntax error",
|
||||
C.ERROR_LOOP_NESTING_LIMIT_EXCEEDED: "loop nesting limit exceeded",
|
||||
C.ERROR_DUPLICATED_LOOP_IDENTIFIER: "duplicated loop identifier",
|
||||
C.ERROR_DUPLICATED_IDENTIFIER: "duplicated identifier",
|
||||
C.ERROR_DUPLICATED_TAG_IDENTIFIER: "duplicated tag identifier",
|
||||
C.ERROR_DUPLICATED_META_IDENTIFIER: "duplicated meta identifier",
|
||||
C.ERROR_DUPLICATED_STRING_IDENTIFIER: "duplicated string identifier",
|
||||
C.ERROR_UNREFERENCED_STRING: "unreferenced string",
|
||||
C.ERROR_UNDEFINED_STRING: "undefined string",
|
||||
C.ERROR_UNDEFINED_IDENTIFIER: "undefined identifier",
|
||||
C.ERROR_MISPLACED_ANONYMOUS_STRING: "misplaced anonymous string",
|
||||
C.ERROR_INCLUDES_CIRCULAR_REFERENCE: "includes circular reference",
|
||||
C.ERROR_INCLUDE_DEPTH_EXCEEDED: "include depth exceeded",
|
||||
C.ERROR_WRONG_TYPE: "wrong type",
|
||||
C.ERROR_EXEC_STACK_OVERFLOW: "exec stack overflow",
|
||||
C.ERROR_SCAN_TIMEOUT: "scan timeout",
|
||||
C.ERROR_TOO_MANY_SCAN_THREADS: "too many scan threads",
|
||||
C.ERROR_CALLBACK_ERROR: "callback error",
|
||||
C.ERROR_INVALID_ARGUMENT: "invalid argument",
|
||||
C.ERROR_TOO_MANY_MATCHES: "too many matches",
|
||||
C.ERROR_INTERNAL_FATAL_ERROR: "internal fatal error",
|
||||
C.ERROR_NESTED_FOR_OF_LOOP: "nested for of loop",
|
||||
C.ERROR_INVALID_FIELD_NAME: "invalid field name",
|
||||
C.ERROR_UNKNOWN_MODULE: "unknown module",
|
||||
C.ERROR_NOT_A_STRUCTURE: "not a structure",
|
||||
C.ERROR_NOT_INDEXABLE: "not indexable",
|
||||
C.ERROR_NOT_A_FUNCTION: "not a function",
|
||||
C.ERROR_INVALID_FORMAT: "invalid format",
|
||||
C.ERROR_TOO_MANY_ARGUMENTS: "too many arguments",
|
||||
C.ERROR_WRONG_ARGUMENTS: "wrong arguments",
|
||||
C.ERROR_WRONG_RETURN_TYPE: "wrong return type",
|
||||
C.ERROR_DUPLICATED_STRUCTURE_MEMBER: "duplicated structure member",
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if r, err := Compile(`rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a }`, nil); err != nil {
|
||||
os.Exit(1)
|
||||
} else if err = r.Save("testrules.yac"); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
rc := m.Run()
|
||||
os.Remove("testrules.yac")
|
||||
os.Exit(rc)
|
||||
}
|
|
@ -0,0 +1,430 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// This file contains tests that were ported from yara/yara-python/tests.py
|
||||
|
||||
var pe32file = []byte{
|
||||
0x4d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x01, 0x00,
|
||||
0x5d, 0xbe, 0x45, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xe0, 0x00, 0x03, 0x01, 0x0b, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
|
||||
0x60, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x64, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x60, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x00, 0x60, 0x6a, 0x2a, 0x58, 0xc3,
|
||||
}
|
||||
|
||||
var elf32file = []byte{
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x60, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x28, 0x00,
|
||||
0x04, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x6c, 0x00, 0x00, 0x00,
|
||||
0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb8, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80,
|
||||
0x00, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x69, 0x64, 0x65,
|
||||
0x20, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x32,
|
||||
0x2e, 0x30, 0x35, 0x2e, 0x30, 0x31, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73,
|
||||
0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x06, 0x00, 0x00, 0x00, 0x60, 0x80, 0x04, 0x08, 0x60, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x6c, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
var elf64file = []byte{
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x40, 0x00,
|
||||
0x04, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00,
|
||||
0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x00, 0x54, 0x68, 0x65,
|
||||
0x20, 0x4e, 0x65, 0x74, 0x77, 0x69, 0x64, 0x65, 0x20, 0x41, 0x73, 0x73,
|
||||
0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x32, 0x2e, 0x30, 0x35, 0x2e,
|
||||
0x30, 0x31, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61,
|
||||
0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func TestBooleanOperators(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { condition: true }",
|
||||
"rule test { condition: true or false }",
|
||||
"rule test { condition: true and true }",
|
||||
"rule test { condition: 0x1 and 0x2}",
|
||||
}, []byte("dummy"))
|
||||
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { condition: false }",
|
||||
"rule test { condition: true and false }",
|
||||
"rule test { condition: false or false }",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestComparisonOperators(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { condition: 2 > 1 }",
|
||||
"rule test { condition: 1 < 2 }",
|
||||
"rule test { condition: 2 >= 1 }",
|
||||
"rule test { condition: 1 <= 1 }",
|
||||
"rule test { condition: 1 == 1 }",
|
||||
"rule test { condition: 1.5 == 1.5}",
|
||||
"rule test { condition: 1.0 == 1}",
|
||||
"rule test { condition: 1.5 >= 1.0}",
|
||||
"rule test { condition: 1.5 >= 1}",
|
||||
"rule test { condition: 1.0 >= 1}",
|
||||
"rule test { condition: 0.5 < 1}",
|
||||
"rule test { condition: 0.5 <= 1}",
|
||||
"rule rest { condition: 1.0 <= 1}",
|
||||
"rule rest { condition: \"abc\" == \"abc\"}",
|
||||
"rule rest { condition: \"abc\" <= \"abc\"}",
|
||||
"rule rest { condition: \"abc\" >= \"abc\"}",
|
||||
"rule rest { condition: \"ab\" < \"abc\"}",
|
||||
"rule rest { condition: \"abc\" > \"ab\"}",
|
||||
"rule rest { condition: \"abc\" < \"abd\"}",
|
||||
"rule rest { condition: \"abd\" > \"abc\"}",
|
||||
}, []byte("dummy"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { condition: 1 != 1}",
|
||||
"rule test { condition: 2 > 3}",
|
||||
"rule test { condition: 2 > 3}",
|
||||
"rule test { condition: 2.1 < 2}",
|
||||
"rule test { condition: \"abc\" != \"abc\"}",
|
||||
"rule test { condition: \"abc\" > \"abc\"}",
|
||||
"rule test { condition: \"abc\" < \"abc\"}",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestArithmeticOperators(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { condition: (1 + 1) * 2 == (9 - 1) \\ 2 }",
|
||||
"rule test { condition: 5 % 2 == 1 }",
|
||||
"rule test { condition: 1.5 + 1.5 == 3}",
|
||||
"rule test { condition: 3 \\ 2 == 1}",
|
||||
"rule test { condition: 3.0 \\ 2 == 1.5}",
|
||||
"rule test { condition: 1 + -1 == 0}",
|
||||
"rule test { condition: -1 + -1 == -2}",
|
||||
"rule test { condition: 4 --2 * 2 == 8}",
|
||||
"rule test { condition: -1.0 * 1 == -1.0}",
|
||||
"rule test { condition: 1-1 == 0}",
|
||||
"rule test { condition: -2.0-3.0 == -5}",
|
||||
"rule test { condition: --1 == 1}",
|
||||
"rule test { condition: 1--1 == 2}",
|
||||
"rule test { condition: -0x01 == -1}",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestBitwiseOperators(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { condition: 0x55 | 0xAA == 0xFF }",
|
||||
"rule test { condition: ~0xAA ^ 0x5A & 0xFF == (~0xAA) ^ (0x5A & 0xFF) }",
|
||||
"rule test { condition: ~0x55 & 0xFF == 0xAA }",
|
||||
"rule test { condition: 8 >> 2 == 2 }",
|
||||
"rule test { condition: 1 << 3 == 8 }",
|
||||
"rule test { condition: 1 | 3 ^ 3 == 1 | (3 ^ 3) }",
|
||||
}, []byte("dummy"))
|
||||
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { condition: ~0xAA ^ 0x5A & 0xFF == 0x0F }",
|
||||
"rule test { condition: 1 | 3 ^ 3 == (1 | 3) ^ 3}",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestStrings(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"a\" condition: $a }",
|
||||
"rule test { strings: $a = \"ab\" condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" condition: $a }",
|
||||
"rule test { strings: $a = \"xyz\" condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" nocase fullword condition: $a }",
|
||||
"rule test { strings: $a = \"aBc\" nocase condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" fullword condition: $a }",
|
||||
}, []byte("---- abc ---- xyz"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"a\" fullword condition: $a }",
|
||||
"rule test { strings: $a = \"ab\" fullword condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" wide fullword condition: $a }",
|
||||
}, []byte("---- abc ---- xyz"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"a\" wide condition: $a }",
|
||||
"rule test { strings: $a = \"a\" wide ascii condition: $a }",
|
||||
"rule test { strings: $a = \"ab\" wide condition: $a }",
|
||||
"rule test { strings: $a = \"ab\" wide ascii condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" wide condition: $a }",
|
||||
"rule test { strings: $a = \"abc\" wide nocase fullword condition: $a }",
|
||||
"rule test { strings: $a = \"aBc\" wide nocase condition: $a }",
|
||||
"rule test { strings: $a = \"aBc\" wide ascii nocase condition: $a }",
|
||||
"rule test { strings: $a = \"---xyz\" wide nocase condition: $a }",
|
||||
}, []byte("---- a\x00b\x00c\x00 -\x00-\x00-\x00-\x00x\x00y\x00z\x00"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" fullword condition: $a }",
|
||||
}, []byte("abc"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" fullword condition: $a }",
|
||||
}, []byte("xabcx"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" fullword condition: $a }",
|
||||
}, []byte("xabc"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" fullword condition: $a }",
|
||||
}, []byte("abcx"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" ascii wide fullword condition: $a }",
|
||||
}, []byte("abcx"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" ascii wide fullword condition: $a }",
|
||||
}, []byte("a\x00abc"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" wide fullword condition: $a }",
|
||||
}, []byte("a\x00b\x00c\x00"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" wide fullword condition: $a }",
|
||||
}, []byte("x\x00a\x00b\x00c\x00x\x00"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"ab\" wide fullword condition: $a }",
|
||||
}, []byte("x\x00a\x00b\x00"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" wide fullword condition: $a }",
|
||||
}, []byte("x\x00a\x00b\x00c\x00"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = \"abc\" wide fullword condition: $a }",
|
||||
}, []byte("x\x01a\x00b\x00c\x00"))
|
||||
assertTrueRules(t, []string{"" +
|
||||
"rule test {\n" +
|
||||
" strings:\n" +
|
||||
" $a = \"abcdef\"\n" +
|
||||
" $b = \"cdef\"\n" +
|
||||
" $c = \"ef\"\n" +
|
||||
" condition:\n" +
|
||||
" all of them\n" +
|
||||
"}",
|
||||
}, []byte("abcdef"))
|
||||
}
|
||||
|
||||
func TestWildcardStrings(t *testing.T) {
|
||||
assertTrueRules(t, []string{"" +
|
||||
"rule test {\n" +
|
||||
" strings:\n" +
|
||||
" $s1 = \"abc\"\n" +
|
||||
" $s2 = \"xyz\"\n" +
|
||||
" condition:\n" +
|
||||
" for all of ($*) : ($)\n" +
|
||||
"}",
|
||||
}, []byte("---- abc ---- A\x00B\x00C\x00 ---- xyz"))
|
||||
}
|
||||
|
||||
func TestHexStrings(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { 64 01 00 00 60 01 } condition: $a }",
|
||||
"rule test { strings: $a = { 64 0? 00 00 ?0 01 } condition: $a }",
|
||||
"rule test { strings: $a = { 64 01 [1-3] 60 01 } condition: $a }",
|
||||
"rule test { strings: $a = { 64 01 [1-3] (60|61) 01 } condition: $a }",
|
||||
"rule test { strings: $a = { 4D 5A [-] 6A 2A [-] 58 C3} condition: $a }",
|
||||
"rule test { strings: $a = { 4D 5A [300-] 6A 2A [-] 58 C3} condition: $a }",
|
||||
}, pe32file)
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = { 4D 5A [0-300] 6A 2A } condition: $a }",
|
||||
}, pe32file)
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { 31 32 [-] 38 39 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [-] 33 34 [-] 38 39 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [1] 34 35 [2] 38 39 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [1-] 34 35 [1-] 38 39 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [0-3] 34 35 [1-] 38 39 } condition: $a }",
|
||||
}, []byte("123456789"))
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { 31 32 [-] 38 39 } condition: all of them }",
|
||||
}, []byte("123456789"))
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { strings: $a = { 31 32 [-] 32 33 } condition: $a }",
|
||||
"rule test { strings: $a = { 35 36 [-] 31 32 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [2-] 34 35 } condition: $a }",
|
||||
"rule test { strings: $a = { 31 32 [0-3] 37 38 } condition: $a }",
|
||||
}, []byte("123456789"))
|
||||
|
||||
rules := makeRules(t, "rule test { strings: $a = { 61 [0-3] (62|63) } condition: $a }")
|
||||
matches, _ := rules.ScanMem([]byte("abbb"), 0, 0)
|
||||
if matches[0].Strings[0].Name != "$a" ||
|
||||
matches[0].Strings[0].Offset != 0 ||
|
||||
string(matches[0].Strings[0].Data) != "ab" {
|
||||
t.Error("wrong match")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: TestCount
|
||||
// TODO: TestAt
|
||||
// TODO: TestOffset
|
||||
// TODO: TestOf
|
||||
// TODO: TestFor
|
||||
// TODO: TestRE
|
||||
|
||||
func TestEntrypoint(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { 6a 2a 58 c3 } condition: $a at entrypoint }",
|
||||
}, pe32file)
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }",
|
||||
}, elf32file)
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { strings: $a = { b8 01 00 00 00 bb 2a } condition: $a at entrypoint }",
|
||||
}, elf64file)
|
||||
assertFalseRules(t, []string{
|
||||
"rule test { condition: entrypoint >= 0 }",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestFilesize(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
fmt.Sprintf("rule test { condition: filesize == %d }", len(pe32file)),
|
||||
}, pe32file)
|
||||
}
|
||||
|
||||
// TODO: TestCompileFile
|
||||
// TODO: TestCompileFiles
|
||||
// TODO: TestIncludeFiles
|
||||
|
||||
type params map[string]interface{}
|
||||
|
||||
func TestExternals(t *testing.T) {
|
||||
for _, sample := range []struct {
|
||||
rule string
|
||||
params
|
||||
predicate bool
|
||||
}{
|
||||
{"rule test { condition: ext_int == 15 }", params{"ext_int": 15}, true},
|
||||
{"rule test { condition: ext_int == -15}", params{"ext_int": -15}, true},
|
||||
{"rule test { condition: ext_float == 3.14 }", params{"ext_float": 3.14}, true},
|
||||
{"rule test { condition: ext_float == -0.5 }", params{"ext_float": -0.5}, true},
|
||||
{"rule test { condition: ext_bool }", params{"ext_bool": true}, true},
|
||||
{"rule test { condition: ext_str }", params{"ext_str": ""}, false},
|
||||
{"rule test { condition: ext_str }", params{"ext_str": "foo"}, true},
|
||||
{"rule test { condition: ext_bool }", params{"ext_bool": false}, false},
|
||||
{"rule test { condition: ext_str contains \"ssi\" }", params{"ext_str": "mississippi"}, true},
|
||||
{"rule test { condition: ext_str matches /foo/ }", params{"ext_str": ""}, false},
|
||||
{"rule test { condition: ext_str matches /foo/ }", params{"ext_str": "FOO"}, false},
|
||||
{"rule test { condition: ext_str matches /foo/i }", params{"ext_str": "FOO"}, true},
|
||||
{"rule test { condition: ext_str matches /ssi(s|p)/ }", params{"ext_str": "mississippi"}, true},
|
||||
{"rule test { condition: ext_str matches /ppi$/ }", params{"ext_str": "mississippi"}, true},
|
||||
{"rule test { condition: ext_str matches /ssi$/ }", params{"ext_str": "mississippi"}, false},
|
||||
{"rule test { condition: ext_str matches /^miss/ }", params{"ext_str": "mississippi"}, true},
|
||||
{"rule test { condition: ext_str matches /^iss/ }", params{"ext_str": "mississippi"}, false},
|
||||
{"rule test { condition: ext_str matches /ssi$/ }", params{"ext_str": "mississippi"}, false},
|
||||
} {
|
||||
r, err := Compile(sample.rule, sample.params)
|
||||
if err != nil {
|
||||
t.Errorf("rule=%s params=%+v: Compile error: %s", sample.rule, sample.params, err)
|
||||
continue
|
||||
}
|
||||
m, _ := r.ScanMem([]byte("dummy"), 0, 0)
|
||||
if sample.predicate != (len(m) > 0) {
|
||||
t.Errorf("rule=%s params=%+v: expected %t, got %t",
|
||||
sample.rule, sample.params, sample.predicate, (len(m) > 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: TestCallback
|
||||
// TODO: TestCompare
|
||||
// TODO: TestComments
|
||||
|
||||
func TestModules(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"import \"tests\" rule test { condition: tests.constants.one + 1 == tests.constants.two }",
|
||||
"import \"tests\" rule test { condition: tests.constants.foo == \"foo\" }",
|
||||
"import \"tests\" rule test { condition: tests.struct_array[1].i == 1 }",
|
||||
"import \"tests\" rule test { condition: tests.struct_array[0].i == 1 or true}",
|
||||
"import \"tests\" rule test { condition: tests.integer_array[0] == 0}",
|
||||
"import \"tests\" rule test { condition: tests.integer_array[1] == 1}",
|
||||
"import \"tests\" rule test { condition: tests.string_array[0] == \"foo\"}",
|
||||
"import \"tests\" rule test { condition: tests.string_array[2] == \"baz\"}",
|
||||
"import \"tests\" rule test { condition: tests.string_dict[\"foo\"] == \"foo\"}",
|
||||
"import \"tests\" rule test { condition: tests.string_dict[\"bar\"] == \"bar\"}",
|
||||
"import \"tests\" rule test { condition: tests.isum(1,2) == 3}",
|
||||
"import \"tests\" rule test { condition: tests.isum(1,2,3) == 6}",
|
||||
"import \"tests\" rule test { condition: tests.fsum(1.0,2.0) == 3.0}",
|
||||
"import \"tests\" rule test { condition: tests.fsum(1.0,2.0,3.0) == 6.0}",
|
||||
"import \"tests\" rule test { condition: tests.length(\"dummy\") == 5}",
|
||||
}, []byte("dummy"))
|
||||
assertFalseRules(t, []string{
|
||||
"import \"tests\" rule test { condition: tests.struct_array[0].i == 1 }",
|
||||
"import \"tests\" rule test { condition: tests.isum(1,1) == 3}",
|
||||
"import \"tests\" rule test { condition: tests.fsum(1.0,1.0) == 3.0}",
|
||||
}, []byte("dummy"))
|
||||
}
|
||||
|
||||
func TestIntegerFunctions(t *testing.T) {
|
||||
assertTrueRules(t, []string{
|
||||
"rule test { condition: uint8(0) == 0xAA}",
|
||||
"rule test { condition: uint16(0) == 0xBBAA}",
|
||||
"rule test { condition: uint32(0) == 0xDDCCBBAA}",
|
||||
"rule test { condition: uint8be(0) == 0xAA}",
|
||||
"rule test { condition: uint16be(0) == 0xAABB}",
|
||||
"rule test { condition: uint32be(0) == 0xAABBCCDD}",
|
||||
}, []byte("\xAA\xBB\xCC\xDD"))
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
Use of this source code is governed by the license that can be
|
||||
found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <yara.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
int rules_callback(int message, void *message_data, void *user_data) {
|
||||
if (message == CALLBACK_MSG_RULE_MATCHING) {
|
||||
YR_RULE* rule = (YR_RULE*) message_data;
|
||||
char* ns = rule->ns->name;
|
||||
if(ns == NULL) {
|
||||
ns = "";
|
||||
}
|
||||
newMatch(user_data, ns, (char*)rule->identifier);
|
||||
YR_META* meta;
|
||||
yr_rule_metas_foreach(rule, meta) {
|
||||
switch (meta->type) {
|
||||
case META_TYPE_INTEGER:
|
||||
addMetaInt(user_data, (char*)meta->identifier, meta->integer);
|
||||
break;
|
||||
case META_TYPE_STRING:
|
||||
addMetaString(user_data, (char*)meta->identifier, meta->string);
|
||||
break;
|
||||
case META_TYPE_BOOLEAN:
|
||||
addMetaBool(user_data, (char*)meta->identifier, meta->integer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const char* tag_name;
|
||||
yr_rule_tags_foreach(rule, tag_name) {
|
||||
addTag(user_data, (char*)tag_name);
|
||||
}
|
||||
YR_STRING* string;
|
||||
YR_MATCH* m;
|
||||
yr_rule_strings_foreach(rule, string) {
|
||||
yr_string_matches_foreach(string, m) {
|
||||
#if YR_VERSION_HEX >= 0x030500
|
||||
/* YR_MATCH members have been renamed in YARA 3.5 */
|
||||
addString(user_data, string->identifier, m->offset, m->data, (int)m->data_length);
|
||||
#else
|
||||
addString(user_data, string->identifier, m->offset, m->data, (int)m->length);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
return CALLBACK_CONTINUE;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
Helper function that is merely used to cast fd from int to HANDLE.
|
||||
CGO treats HANDLE (void*) to an unsafe.Pointer. This confuses the
|
||||
go1.4 garbage collector, leading to runtime errors such as:
|
||||
|
||||
runtime: garbage collector found invalid heap pointer *(0x5b80ff14+0x4)=0xa0 s=nil
|
||||
*/
|
||||
int _yr_rules_scan_fd(
|
||||
YR_RULES* rules,
|
||||
int fd,
|
||||
int flags,
|
||||
YR_CALLBACK_FUNC callback,
|
||||
void* user_data,
|
||||
int timeout)
|
||||
{
|
||||
return yr_rules_scan_fd(rules, (YR_FILE_DESCRIPTOR)fd, flags, callback, user_data, timeout);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,241 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package yara provides bindings to the YARA library.
|
||||
package yara
|
||||
|
||||
/*
|
||||
#include <yara.h>
|
||||
|
||||
int rules_callback(int message, void *message_data, void *user_data);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Rules contains a compiled YARA ruleset.
|
||||
type Rules struct {
|
||||
*rules
|
||||
}
|
||||
|
||||
type rules struct {
|
||||
cptr *C.YR_RULES
|
||||
}
|
||||
|
||||
var dummy *[]MatchRule
|
||||
|
||||
// A MatchRule represents a rule successfully matched against a block
|
||||
// of data.
|
||||
type MatchRule struct {
|
||||
Rule string
|
||||
Namespace string
|
||||
Tags []string
|
||||
Meta map[string]interface{}
|
||||
Strings []MatchString
|
||||
}
|
||||
|
||||
// A MatchString represents a string declared and matched in a rule.
|
||||
type MatchString struct {
|
||||
Name string
|
||||
Offset uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func init() {
|
||||
_ = C.yr_initialize()
|
||||
}
|
||||
|
||||
//export newMatch
|
||||
func newMatch(userData unsafe.Pointer, namespace, identifier *C.char) {
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
*matches = append(*matches, MatchRule{
|
||||
Rule: C.GoString(identifier),
|
||||
Namespace: C.GoString(namespace),
|
||||
Tags: []string{},
|
||||
Meta: map[string]interface{}{},
|
||||
Strings: []MatchString{},
|
||||
})
|
||||
}
|
||||
|
||||
//export addMetaInt
|
||||
func addMetaInt(userData unsafe.Pointer, identifier *C.char, value C.int) {
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
i := len(*matches) - 1
|
||||
(*matches)[i].Meta[C.GoString(identifier)] = int32(value)
|
||||
}
|
||||
|
||||
//export addMetaString
|
||||
func addMetaString(userData unsafe.Pointer, identifier *C.char, value *C.char) {
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
i := len(*matches) - 1
|
||||
(*matches)[i].Meta[C.GoString(identifier)] = C.GoString(value)
|
||||
}
|
||||
|
||||
//export addMetaBool
|
||||
func addMetaBool(userData unsafe.Pointer, identifier *C.char, value C.int) {
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
i := len(*matches) - 1
|
||||
(*matches)[i].Meta[C.GoString(identifier)] = bool(value != 0)
|
||||
}
|
||||
|
||||
//export addTag
|
||||
func addTag(userData unsafe.Pointer, tag *C.char) {
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
i := len(*matches) - 1
|
||||
(*matches)[i].Tags = append((*matches)[i].Tags, C.GoString(tag))
|
||||
}
|
||||
|
||||
//export addString
|
||||
func addString(userData unsafe.Pointer, identifier *C.char, offset C.uint64_t, data unsafe.Pointer, length C.int) {
|
||||
ms := MatchString{
|
||||
Name: C.GoString(identifier),
|
||||
Offset: uint64(offset),
|
||||
Data: make([]byte, int(length)),
|
||||
}
|
||||
|
||||
var tmpSlice []byte
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&tmpSlice))
|
||||
hdr.Data = uintptr(data)
|
||||
hdr.Len = int(length)
|
||||
copy(ms.Data, tmpSlice)
|
||||
|
||||
matches := callbackData.Get(uintptr(userData)).(*[]MatchRule)
|
||||
i := len(*matches) - 1
|
||||
(*matches)[i].Strings = append((*matches)[i].Strings, ms)
|
||||
}
|
||||
|
||||
// ScanFlags are used to tweak the behavior of Scan* functions.
|
||||
type ScanFlags int
|
||||
|
||||
const (
|
||||
// ScanFlagsFastMode avoids multiple matches of the same string
|
||||
// when not necessary.
|
||||
ScanFlagsFastMode = C.SCAN_FLAGS_FAST_MODE
|
||||
// ScanFlagsProcessMemory causes the scanned data to be
|
||||
// interpreted like live, in-prcess memory rather than an on-disk
|
||||
// file.
|
||||
ScanFlagsProcessMemory = C.SCAN_FLAGS_PROCESS_MEMORY
|
||||
)
|
||||
|
||||
// ScanMem scans an in-memory buffer using the ruleset.
|
||||
func (r *Rules) ScanMem(buf []byte, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) {
|
||||
var ptr *C.uint8_t
|
||||
if len(buf) > 0 {
|
||||
ptr = (*C.uint8_t)(unsafe.Pointer(&(buf[0])))
|
||||
}
|
||||
id := callbackData.Put(&matches)
|
||||
defer callbackData.Delete(id)
|
||||
err = newError(C.yr_rules_scan_mem(
|
||||
r.cptr,
|
||||
ptr,
|
||||
C.size_t(len(buf)),
|
||||
C.int(flags),
|
||||
C.YR_CALLBACK_FUNC(C.rules_callback),
|
||||
unsafe.Pointer(id),
|
||||
C.int(timeout/time.Second)))
|
||||
return
|
||||
}
|
||||
|
||||
// ScanFile scans a file using the ruleset.
|
||||
func (r *Rules) ScanFile(filename string, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) {
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
id := callbackData.Put(&matches)
|
||||
defer callbackData.Delete(id)
|
||||
err = newError(C.yr_rules_scan_file(
|
||||
r.cptr,
|
||||
cfilename,
|
||||
C.int(flags),
|
||||
C.YR_CALLBACK_FUNC(C.rules_callback),
|
||||
unsafe.Pointer(id),
|
||||
C.int(timeout/time.Second)))
|
||||
return
|
||||
}
|
||||
|
||||
// ScanProc scans a live process using the ruleset.
|
||||
func (r *Rules) ScanProc(pid int, flags int, timeout time.Duration) (matches []MatchRule, err error) {
|
||||
id := callbackData.Put(&matches)
|
||||
defer callbackData.Delete(id)
|
||||
err = newError(C.yr_rules_scan_proc(
|
||||
r.cptr,
|
||||
C.int(pid),
|
||||
C.int(flags),
|
||||
C.YR_CALLBACK_FUNC(C.rules_callback),
|
||||
unsafe.Pointer(id),
|
||||
C.int(timeout/time.Second)))
|
||||
return
|
||||
}
|
||||
|
||||
// Save writes a compiled ruleset to filename.
|
||||
func (r *Rules) Save(filename string) (err error) {
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
err = newError(C.yr_rules_save(r.cptr, cfilename))
|
||||
return
|
||||
}
|
||||
|
||||
// LoadRules retrieves a compiled ruleset from filename.
|
||||
func LoadRules(filename string) (*Rules, error) {
|
||||
r := &Rules{rules: &rules{}}
|
||||
cfilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cfilename))
|
||||
if err := newError(C.yr_rules_load(cfilename,
|
||||
&(r.rules.cptr))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime.SetFinalizer(r.rules, (*rules).finalize)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *rules) finalize() {
|
||||
C.yr_rules_destroy(r.cptr)
|
||||
runtime.SetFinalizer(r, nil)
|
||||
}
|
||||
|
||||
// Destroy destroys the YARA data structure representing a ruleset.
|
||||
// Since a Finalizer for the underlying YR_RULES structure is
|
||||
// automatically set up on creation, it should not be necessary to
|
||||
// explicitly call this method.
|
||||
func (r *Rules) Destroy() {
|
||||
if r.rules != nil {
|
||||
r.rules.finalize()
|
||||
r.rules = nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefineVariable defines a named variable for use by the compiler.
|
||||
// Boolean, int64, float64, and string types are supported.
|
||||
func (r *Rules) DefineVariable(name string, value interface{}) (err error) {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
switch value.(type) {
|
||||
case bool:
|
||||
var v int
|
||||
if value.(bool) {
|
||||
v = 1
|
||||
}
|
||||
err = newError(C.yr_rules_define_boolean_variable(
|
||||
r.cptr, cname, C.int(v)))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
value := toint64(value)
|
||||
err = newError(C.yr_rules_define_integer_variable(
|
||||
r.cptr, cname, C.int64_t(value)))
|
||||
case float64:
|
||||
err = newError(C.yr_rules_define_float_variable(
|
||||
r.cptr, cname, C.double(value.(float64))))
|
||||
case string:
|
||||
cvalue := C.CString(value.(string))
|
||||
defer C.free(unsafe.Pointer(cvalue))
|
||||
err = newError(C.yr_rules_define_string_variable(
|
||||
r.cptr, cname, cvalue))
|
||||
default:
|
||||
err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted")
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func makeRules(t *testing.T, rule string) *Rules {
|
||||
c, err := NewCompiler()
|
||||
if c == nil || err != nil {
|
||||
t.Fatal("NewCompiler():", err)
|
||||
}
|
||||
if err = c.AddString(rule, ""); err != nil {
|
||||
t.Fatal("AddString():", err)
|
||||
}
|
||||
r, err := c.GetRules()
|
||||
if err != nil {
|
||||
t.Fatal("GetRules:", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestSimpleMatch(t *testing.T) {
|
||||
r := makeRules(t,
|
||||
"rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }")
|
||||
m, err := r.ScanMem([]byte(" abc "), 0, 0)
|
||||
if err != nil {
|
||||
t.Errorf("ScanMem: %s", err)
|
||||
}
|
||||
t.Logf("Matches: %+v", m)
|
||||
}
|
||||
|
||||
func TestSimpleFileMatch(t *testing.T) {
|
||||
r, _ := Compile(
|
||||
"rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }",
|
||||
nil)
|
||||
tf, _ := ioutil.TempFile("", "TestSimpleFileMatch")
|
||||
defer os.Remove(tf.Name())
|
||||
tf.Write([]byte(" abc "))
|
||||
tf.Close()
|
||||
m, err := r.ScanFile(tf.Name(), 0, 0)
|
||||
if err != nil {
|
||||
t.Errorf("ScanFile(%s): %s", tf.Name(), err)
|
||||
}
|
||||
t.Logf("Matches: %+v", m)
|
||||
}
|
||||
|
||||
func TestSimpleFileDescriptorMatch(t *testing.T) {
|
||||
r, _ := Compile(
|
||||
"rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }",
|
||||
nil)
|
||||
tf, _ := ioutil.TempFile("", "TestSimpleFileMatch")
|
||||
defer os.Remove(tf.Name())
|
||||
tf.Write([]byte(" abc "))
|
||||
tf.Seek(0, os.SEEK_SET)
|
||||
m, err := r.ScanFileDescriptor(tf.Fd(), 0, 0)
|
||||
if err != nil {
|
||||
t.Errorf("ScanFile(%s): %s", tf.Name(), err)
|
||||
}
|
||||
t.Logf("Matches: %+v", m)
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
r, _ := Compile("rule test { condition: true }", nil)
|
||||
r.ScanMem([]byte{}, 0, 0)
|
||||
t.Log("Scan of null-byte slice did not crash. Yay.")
|
||||
}
|
||||
|
||||
func assertTrueRules(t *testing.T, rules []string, data []byte) {
|
||||
for _, rule := range rules {
|
||||
r := makeRules(t, rule)
|
||||
if m, err := r.ScanMem(data, 0, 0); len(m) == 0 {
|
||||
t.Errorf("Rule < %s > did not match data < %v >", rule, data)
|
||||
} else if err != nil {
|
||||
t.Errorf("Error %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertFalseRules(t *testing.T, rules []string, data []byte) {
|
||||
for _, rule := range rules {
|
||||
r := makeRules(t, rule)
|
||||
if m, err := r.ScanMem(data, 0, 0); len(m) > 0 {
|
||||
t.Errorf("Rule < %s > matched data < %v >", rule, data)
|
||||
} else if err != nil {
|
||||
t.Errorf("Error %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
r, err := LoadRules("testrules.yac")
|
||||
if r == nil || err != nil {
|
||||
t.Fatalf("LoadRules: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
rd, err := os.Open("testrules.yac")
|
||||
if err != nil {
|
||||
t.Fatalf("os.Open: %s", err)
|
||||
}
|
||||
r, err := ReadRules(rd)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadRules: %+v", err)
|
||||
}
|
||||
m, err := r.ScanMem([]byte(" abc "), 0, 0)
|
||||
if err != nil {
|
||||
t.Errorf("ScanMem: %s", err)
|
||||
}
|
||||
t.Logf("Matches: %+v", m)
|
||||
}
|
||||
|
||||
func TestWriter(t *testing.T) {
|
||||
rd, err := os.Open("testrules.yac")
|
||||
if err != nil {
|
||||
t.Fatalf("os.Open: %s", err)
|
||||
}
|
||||
compareBuf, _ := ioutil.ReadAll(rd)
|
||||
r, _ := Compile("rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }",
|
||||
nil)
|
||||
wr := bytes.Buffer{}
|
||||
if err := r.Write(&wr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newBuf := wr.Bytes()
|
||||
if len(compareBuf) != len(newBuf) {
|
||||
t.Errorf("len(compareBuf) = %d, len(newBuf) = %d", len(compareBuf), len(newBuf))
|
||||
}
|
||||
if bytes.Compare(compareBuf, newBuf) != 0 {
|
||||
t.Error("buffers are not equal")
|
||||
}
|
||||
}
|
||||
|
||||
// compress/bzip2 seems to return short reads which apparently leads
|
||||
// to YARA complaining about corrupt files. Tested with Go 1.4, 1.5.
|
||||
func TestReaderBZIP2(t *testing.T) {
|
||||
rulesBuf := bytes.NewBuffer(nil)
|
||||
for i := 0; i < 10000; i++ {
|
||||
fmt.Fprintf(rulesBuf, "rule test%d : tag%d { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }", i, i)
|
||||
}
|
||||
r, _ := Compile(string(rulesBuf.Bytes()), nil)
|
||||
cmd := exec.Command("bzip2", "-c")
|
||||
compressStream, _ := cmd.StdinPipe()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd.Stdout = buf
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Errorf("start bzip2 process: %s", err)
|
||||
return
|
||||
}
|
||||
if err := r.Write(compressStream); err != nil {
|
||||
t.Errorf("pipe to bzip2 process: %s", err)
|
||||
return
|
||||
}
|
||||
compressStream.Close()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Errorf("wait for bzip2 process: %s", err)
|
||||
return
|
||||
}
|
||||
if _, err := ReadRules(bzip2.NewReader(bytes.NewReader(buf.Bytes()))); err != nil {
|
||||
t.Errorf("read using compress/bzip2: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/hillu/go-yara/issues/5
|
||||
func TestScanMemCgoPointer(t *testing.T) {
|
||||
r := makeRules(t,
|
||||
"rule test : tag1 { meta: author = \"Hilko Bengen\" strings: $a = \"abc\" fullword condition: $a }")
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Write([]byte(" abc "))
|
||||
if err := func() (p interface{}) {
|
||||
defer func() { p = recover() }()
|
||||
r.ScanMem(buf.Bytes(), 0, 0)
|
||||
return nil
|
||||
}(); err != nil {
|
||||
t.Errorf("ScanMem panicked: %s", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright © 2015-2016 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file contains functionality that require libyara 3.4 or higher
|
||||
|
||||
// +build !yara3.3
|
||||
|
||||
package yara
|
||||
|
||||
/*
|
||||
#include <yara.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
int _yr_rules_scan_fd(
|
||||
YR_RULES* rules,
|
||||
int fd,
|
||||
int flags,
|
||||
YR_CALLBACK_FUNC callback,
|
||||
void* user_data,
|
||||
int timeout);
|
||||
#else
|
||||
#define _yr_rules_scan_fd yr_rules_scan_fd
|
||||
#endif
|
||||
|
||||
size_t streamRead(void* ptr, size_t size, size_t nmemb, void* user_data);
|
||||
size_t streamWrite(void* ptr, size_t size, size_t nmemb, void* user_data);
|
||||
|
||||
int rules_callback(int message, void *message_data, void *user_data);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ScanFileDescriptor scans a file using the ruleset.
|
||||
func (r *Rules) ScanFileDescriptor(fd uintptr, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) {
|
||||
id := callbackData.Put(&matches)
|
||||
defer callbackData.Delete(id)
|
||||
err = newError(C._yr_rules_scan_fd(
|
||||
r.cptr,
|
||||
C.int(fd),
|
||||
C.int(flags),
|
||||
C.YR_CALLBACK_FUNC(C.rules_callback),
|
||||
unsafe.Pointer(id),
|
||||
C.int(timeout/time.Second)))
|
||||
return
|
||||
}
|
||||
|
||||
// Write writes a compiled ruleset to an io.Writer.
|
||||
func (r *Rules) Write(wr io.Writer) (err error) {
|
||||
id := callbackData.Put(wr)
|
||||
defer callbackData.Delete(id)
|
||||
|
||||
stream := (*C.YR_STREAM)(C.malloc((C.sizeof_YR_STREAM)))
|
||||
defer C.free(unsafe.Pointer(stream))
|
||||
stream.user_data = unsafe.Pointer(id)
|
||||
stream.write = C.YR_STREAM_WRITE_FUNC(C.streamWrite)
|
||||
|
||||
err = newError(C.yr_rules_save_stream(r.cptr, stream))
|
||||
return
|
||||
}
|
||||
|
||||
// ReadRules retrieves a compiled ruleset from an io.Reader
|
||||
func ReadRules(rd io.Reader) (*Rules, error) {
|
||||
r := &Rules{rules: &rules{}}
|
||||
id := callbackData.Put(rd)
|
||||
defer callbackData.Delete(id)
|
||||
|
||||
stream := (*C.YR_STREAM)(C.malloc((C.sizeof_YR_STREAM)))
|
||||
defer C.free(unsafe.Pointer(stream))
|
||||
stream.user_data = unsafe.Pointer(id)
|
||||
stream.read = C.YR_STREAM_READ_FUNC(C.streamRead)
|
||||
|
||||
if err := newError(C.yr_rules_load_stream(stream,
|
||||
&(r.rules.cptr))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime.SetFinalizer(r.rules, (*rules).finalize)
|
||||
return r, nil
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package yara
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// #include <string.h>
|
||||
import "C"
|
||||
|
||||
//export streamRead
|
||||
func streamRead(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
|
||||
if size == 0 || nmemb == 0 {
|
||||
return nmemb
|
||||
}
|
||||
reader := callbackData.Get(uintptr(userData)).(io.Reader)
|
||||
buf := make([]byte, 0)
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||
hdr.Data = uintptr(ptr)
|
||||
hdr.Len = int(size * nmemb)
|
||||
hdr.Cap = hdr.Len
|
||||
s := int(size)
|
||||
for i := 0; i < int(nmemb); i++ {
|
||||
if sz, err := io.ReadFull(reader, buf[i*s:(i+1)*s]); sz < int(size) && err != nil {
|
||||
return C.size_t(i)
|
||||
}
|
||||
}
|
||||
return nmemb
|
||||
}
|
||||
|
||||
func writeFull(w io.Writer, buf []byte) (n int, err error) {
|
||||
var i int
|
||||
for n < len(buf) {
|
||||
i, err = w.Write(buf[n:])
|
||||
n += i
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//export streamWrite
|
||||
func streamWrite(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
|
||||
if size == 0 || nmemb == 0 {
|
||||
return nmemb
|
||||
}
|
||||
writer := callbackData.Get(uintptr(userData)).(io.Writer)
|
||||
buf := make([]byte, 0)
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||
hdr.Data = uintptr(ptr)
|
||||
hdr.Len = int(size * nmemb)
|
||||
hdr.Cap = hdr.Len
|
||||
s := int(size)
|
||||
for i := 0; i < int(nmemb); i++ {
|
||||
if sz, err := writeFull(writer, buf[i*s:(i+1)*s]); sz < int(size) && err != nil {
|
||||
return C.size_t(i)
|
||||
}
|
||||
}
|
||||
return nmemb
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package yara
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileWalk(t *testing.T) {
|
||||
if os.ExpandEnv("${TEST_WALK}") == "" {
|
||||
t.Skip("Set TEST_WALK to enable scanning files from user's HOME with a dummy ruleset.\n" +
|
||||
"(Setting -test.timeout may be a good idea for this.)")
|
||||
}
|
||||
initialPath := os.ExpandEnv("${TEST_WALK_START}")
|
||||
if initialPath == "" {
|
||||
if u, err := user.Current(); err != nil {
|
||||
t.Skip("Could get user's homedir. You can use TEST_WALK_START " +
|
||||
"to set an initial path for filepath.Walk()")
|
||||
} else {
|
||||
initialPath = u.HomeDir
|
||||
}
|
||||
}
|
||||
r, err := Compile("rule test: tag1 tag2 tag3 { meta: foo = 1 bar = \"xxx\" quux = false condition: true }", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 32; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
filepath.Walk(initialPath, func(name string, info os.FileInfo, inErr error) error {
|
||||
fmt.Printf("[%02d] %s\n", i, name)
|
||||
if inErr != nil {
|
||||
fmt.Printf("[%02d] Walk to \"%s\": %s\n", i, name, inErr)
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() && info.Mode()&os.ModeSymlink != 0 {
|
||||
fmt.Printf("[%02d] Walk to \"%s\": Skipping symlink\n", i, name)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !info.Mode().IsRegular() || info.Size() >= 2000000 {
|
||||
return nil
|
||||
}
|
||||
if m, err := r.ScanFile(name, 0, 0); err == nil {
|
||||
fmt.Printf("[%02d] Scan \"%s\": %d\n", i, path.Base(name), len(m))
|
||||
} else {
|
||||
fmt.Printf("[%02d] Scan \"%s\": %s\n", i, path.Base(name), err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a }
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
|
||||
// Use of this source code is governed by the license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package yara
|
||||
|
||||
func toint64(number interface{}) int64 {
|
||||
switch number.(type) {
|
||||
case int:
|
||||
return int64(number.(int))
|
||||
case int8:
|
||||
return int64(number.(int8))
|
||||
case int16:
|
||||
return int64(number.(int16))
|
||||
case int32:
|
||||
return int64(number.(int32))
|
||||
case int64:
|
||||
return int64(number.(int64))
|
||||
case uint:
|
||||
return int64(number.(uint))
|
||||
case uint8:
|
||||
return int64(number.(uint8))
|
||||
case uint16:
|
||||
return int64(number.(uint16))
|
||||
case uint32:
|
||||
return int64(number.(uint32))
|
||||
case uint64:
|
||||
return int64(number.(uint64))
|
||||
}
|
||||
panic("wrong number")
|
||||
}
|
Загрузка…
Ссылка в новой задаче