diff --git a/.travis.yml b/.travis.yml index 3790a973..e6ef601b 100644 --- a/.travis.yml +++ b/.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 diff --git a/Dockerfile b/Dockerfile index ba2e371b..b05c039f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Makefile b/Makefile index 6ec52a84..9fa88a6c 100644 --- a/Makefile +++ b/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 \ diff --git a/client/mig-console/available_modules.go b/client/mig-console/available_modules.go index 27276f35..cde23d27 100644 --- a/client/mig-console/available_modules.go +++ b/client/mig-console/available_modules.go @@ -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" ) diff --git a/client/mig/available_modules.go b/client/mig/available_modules.go index 27276f35..cde23d27 100644 --- a/client/mig/available_modules.go +++ b/client/mig/available_modules.go @@ -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" ) diff --git a/conf/available_modules.go b/conf/available_modules.go index 27276f35..cde23d27 100644 --- a/conf/available_modules.go +++ b/conf/available_modules.go @@ -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" ) diff --git a/mig-agent/available_modules.go b/mig-agent/available_modules.go index 27276f35..cde23d27 100644 --- a/mig-agent/available_modules.go +++ b/mig-agent/available_modules.go @@ -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" ) diff --git a/modules/yara/doc.rst b/modules/yara/doc.rst new file mode 100644 index 00000000..22abc721 --- /dev/null +++ b/modules/yara/doc.rst @@ -0,0 +1,83 @@ +================================= +Mozilla InvestiGator: yara module +================================= +:Author: Aaron Meihm + +.. 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 `_ 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 .' diff --git a/modules/yara/paramscreator.go b/modules/yara/paramscreator.go new file mode 100644 index 00000000..e6f9457f --- /dev/null +++ b/modules/yara/paramscreator.go @@ -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 - yara rules path + ex: path ./myrules.yar + processes yara rules on agent + +%sfiles - 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() +} diff --git a/modules/yara/yara.go b/modules/yara/yara.go new file mode 100644 index 00000000..25948093 --- /dev/null +++ b/modules/yara/yara.go @@ -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{} +} diff --git a/modules/yara/yara_test.go b/modules/yara/yara_test.go new file mode 100644 index 00000000..ab11a724 --- /dev/null +++ b/modules/yara/yara_test.go @@ -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") +} diff --git a/tools/standalone_install.sh b/tools/standalone_install.sh index 3550b855..618a7c95 100644 --- a/tools/standalone_install.sh +++ b/tools/standalone_install.sh @@ -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 diff --git a/vendor/github.com/hillu/go-yara/.travis.yml b/vendor/github.com/hillu/go-yara/.travis.yml new file mode 100644 index 00000000..a74a8a25 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/.travis.yml @@ -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 diff --git a/vendor/github.com/hillu/go-yara/LICENSE b/vendor/github.com/hillu/go-yara/LICENSE new file mode 100644 index 00000000..e0e7ce91 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Hilko Bengen +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. diff --git a/vendor/github.com/hillu/go-yara/README.md b/vendor/github.com/hillu/go-yara/README.md new file mode 100644 index 00000000..5d6b0202 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/README.md @@ -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 diff --git a/vendor/github.com/hillu/go-yara/callback-util.go b/vendor/github.com/hillu/go-yara/callback-util.go new file mode 100644 index 00000000..882aba06 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/callback-util.go @@ -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 diff --git a/vendor/github.com/hillu/go-yara/cgo.go b/vendor/github.com/hillu/go-yara/cgo.go new file mode 100644 index 00000000..75f7f79f --- /dev/null +++ b/vendor/github.com/hillu/go-yara/cgo.go @@ -0,0 +1,8 @@ +// Copyright © 2015 Hilko Bengen . 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" diff --git a/vendor/github.com/hillu/go-yara/compiler.go b/vendor/github.com/hillu/go-yara/compiler.go new file mode 100644 index 00000000..4c5a493f --- /dev/null +++ b/vendor/github.com/hillu/go-yara/compiler.go @@ -0,0 +1,215 @@ +// Copyright © 2015 Hilko Bengen . 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 +#include + +#include + +// 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 +} diff --git a/vendor/github.com/hillu/go-yara/compiler_test.go b/vendor/github.com/hillu/go-yara/compiler_test.go new file mode 100644 index 00000000..f91de734 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/compiler_test.go @@ -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) +} diff --git a/vendor/github.com/hillu/go-yara/crash_test.go b/vendor/github.com/hillu/go-yara/crash_test.go new file mode 100644 index 00000000..dc29d70e --- /dev/null +++ b/vendor/github.com/hillu/go-yara/crash_test.go @@ -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.") +} diff --git a/vendor/github.com/hillu/go-yara/error.go b/vendor/github.com/hillu/go-yara/error.go new file mode 100644 index 00000000..07a70e3e --- /dev/null +++ b/vendor/github.com/hillu/go-yara/error.go @@ -0,0 +1,67 @@ +// Copyright © 2015 Hilko Bengen . All rights reserved. +// Use of this source code is governed by the license that can be +// found in the LICENSE file. + +package yara + +// #include +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", +} diff --git a/vendor/github.com/hillu/go-yara/main_test.go b/vendor/github.com/hillu/go-yara/main_test.go new file mode 100644 index 00000000..30fa2ba3 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/main_test.go @@ -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) +} diff --git a/vendor/github.com/hillu/go-yara/ported_test.go b/vendor/github.com/hillu/go-yara/ported_test.go new file mode 100644 index 00000000..7af1b19c --- /dev/null +++ b/vendor/github.com/hillu/go-yara/ported_test.go @@ -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")) +} diff --git a/vendor/github.com/hillu/go-yara/rules-callback.c b/vendor/github.com/hillu/go-yara/rules-callback.c new file mode 100644 index 00000000..ebd5ec45 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/rules-callback.c @@ -0,0 +1,70 @@ +/* + Copyright © 2015 Hilko Bengen . All rights reserved. + Use of this source code is governed by the license that can be + found in the LICENSE file. +*/ + +#include +#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 diff --git a/vendor/github.com/hillu/go-yara/rules.go b/vendor/github.com/hillu/go-yara/rules.go new file mode 100644 index 00000000..1aff56bf --- /dev/null +++ b/vendor/github.com/hillu/go-yara/rules.go @@ -0,0 +1,241 @@ +// Copyright © 2015 Hilko Bengen . 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 + +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 +} diff --git a/vendor/github.com/hillu/go-yara/rules_test.go b/vendor/github.com/hillu/go-yara/rules_test.go new file mode 100644 index 00000000..3d64cea9 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/rules_test.go @@ -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) + } +} diff --git a/vendor/github.com/hillu/go-yara/rules_yara34.go b/vendor/github.com/hillu/go-yara/rules_yara34.go new file mode 100644 index 00000000..19fc8259 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/rules_yara34.go @@ -0,0 +1,84 @@ +// Copyright © 2015-2016 Hilko Bengen . 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 + +#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 +} diff --git a/vendor/github.com/hillu/go-yara/stream.go b/vendor/github.com/hillu/go-yara/stream.go new file mode 100644 index 00000000..a9ffa87b --- /dev/null +++ b/vendor/github.com/hillu/go-yara/stream.go @@ -0,0 +1,66 @@ +// Copyright © 2015 Hilko Bengen . 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 +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 +} diff --git a/vendor/github.com/hillu/go-yara/stress_test.go b/vendor/github.com/hillu/go-yara/stress_test.go new file mode 100644 index 00000000..2dd477cb --- /dev/null +++ b/vendor/github.com/hillu/go-yara/stress_test.go @@ -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() +} diff --git a/vendor/github.com/hillu/go-yara/testdata/rules.yar b/vendor/github.com/hillu/go-yara/testdata/rules.yar new file mode 100644 index 00000000..9d41b6e3 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/testdata/rules.yar @@ -0,0 +1 @@ +rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a } diff --git a/vendor/github.com/hillu/go-yara/util.go b/vendor/github.com/hillu/go-yara/util.go new file mode 100644 index 00000000..c4767de7 --- /dev/null +++ b/vendor/github.com/hillu/go-yara/util.go @@ -0,0 +1,31 @@ +// Copyright © 2015 Hilko Bengen . 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") +}