pax_global_header00006660000000000000000000000064132411253540014512gustar00rootroot0000000000000052 comment=1f4fc4461545789560fdf7530e32c7db36441194 go-yara-1.0.7/000077500000000000000000000000001324112535400130565ustar00rootroot00000000000000go-yara-1.0.7/.travis.yml000066400000000000000000000012311324112535400151640ustar00rootroot00000000000000language: go go: - 1.6.x - 1.7.x - 1.8.x - 1.9.x - 1.10.x - tip gobuild_args: -tags no_pkg_config dist: xenial addons: apt: packages: - bison - flex - automake - autoconf - libtool - make - gcc before_install: - wget --no-verbose -O- https://github.com/VirusTotal/yara/archive/v3.7.0.tar.gz | tar -C ${TRAVIS_BUILD_DIR} -xzf - - ( cd ${TRAVIS_BUILD_DIR}/yara-3.7.0 && ./bootstrap.sh && ./configure && make ) - export CGO_CFLAGS=-I${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/include - export CGO_LDFLAGS=-L${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/.libs - export LD_LIBRARY_PATH=${TRAVIS_BUILD_DIR}/yara-3.7.0/libyara/.libs go-yara-1.0.7/LICENSE000066400000000000000000000024551324112535400140710ustar00rootroot00000000000000Copyright (c) 2015-2017 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. go-yara-1.0.7/README.md000066400000000000000000000103131324112535400143330ustar00rootroot00000000000000![Logo](/goyara-logo.png) # 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 Report Card](https://goreportcard.com/badge/github.com/hillu/go-yara)](https://goreportcard.com/report/github.com/hillu/go-yara) Go bindings for [YARA](https://virustotal.github.io/yara/), staying as close as sensible to the library's C-API while taking inspiration from the `yara-python` implementation. ## Installation ### Unix On a Unix system with _libyara_, its header files, and _pkg-config_ installed, the following should simply work, provided that `GOPATH` is set: ``` go get github.com/hillu/go-yara go install github.com/hillu/go-yara ``` The _pkg-config_ program should be able to output the correct compiler and linker flags from the `yara.pc` file that has been generated and installed by _YARA_'s build system. If _libyara_ has been installed to a custom location, the `PKG_CONFIG_PATH` environment variable can be used to point _pkg-config_ at the right `yara.pc` file. If _pkg-config_ cannot be used at all, please see "Build Tags" below. Linker errors in the compiler output such as undefined reference to `yr_compiler_add_file' indicate that the linker is probably looking at an old version of _libyara_. ### Cross-building for Windows _go-yara_ can be cross-built on a current Debian system using the MinGW cross compiler (_gcc-mingw-w64_) if the Go compiler contains Windows runtime libraries with CGO support ([cf.](https://github.com/hillu/golang-go-cross)). After _libyara_ has been built from the source tree with the MinGW compiler using the usual `./configure && make && make install`, _go-yara_ can be built and installed. Some environment variables need to be set when running `go build` or `go install`: - `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). - `PKG_CONFIG_PATH` is set so the _go_ tool can determine correct locations for headers and libraries through _pkg-config_. 32bit: ``` $ cd ${YARA_SRC} \ && ./bootstrap.sh \ && ./configure --host=i686-w64-mingw32 --disable-magic --disable-cuckoo --without-crypto --prefix=${YARA_SRC}/i686-w64-mingw32 \ && make -C ${YARA_SRC} \ && make -C ${YARA_SRC} install $ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 \ CC=i686-w64-mingw32-gcc \ PKG_CONFIG_PATH=${YARA_SRC}/i686-w64-mingw32/lib/pkgconfig \ go install -ldflags '-extldflags "-static"' github.com/hillu/go-yara ``` 64bit: ``` $ cd ${YARA_SRC} \ && ./bootstrap.sh \ && ./configure --host=x86_64-w64-mingw32 --disable-magic --disable-cuckoo --without-crypto --prefix=${YARA_SRC}/x86_64-w64-mingw32 \ && make -C ${YARA_SRC} \ && make -C ${YARA_SRC} install $ GOOS=windows GOARCH=amd64 CGO_ENABLED=1 \ CC=x86_64-w64-mingw32-gcc \ PKG_CONFIG_PATH=${YARA_SRC}/x86_64-w64-mingw32/lib/pkgconfig \ go install -ldflags '-extldflags "-static"' github.com/hillu/go-yara ``` ## Build Tags _go-yara_ is tested with the latest stable version of YARA, currently 3.7. If you need to to build with an older version of YARA, certain features that are not present in older versions can be excluded by passing a build tag such as `yara3.3`, `yara3.4`, `yara3.5`. If you want to build with a git snapshot of YARA, you may use a build tag corresponding to the upcoming stable YARA version, currently `yara3.8`. The build tag `yara_static` can be used to tell the Go toolchain to run _pkg-config_ with the `--static` switch. The build tag `no_pkg_config` can be used to tell the Go toolchain not to use _pkg-config_'s output. In this case, any compiler or linker flags have to be set via the `CGO_CFLAGS` and `CGO_LDFLAGS` environment variables, e.g.: ``` export CGO_CFLAGS="-I${YARA_SRC}/libyara/include" export CGO_LDFLAGS="-L${YARA_SRC}/libyara/.libs -lyara" go install github.com/hillu/go-yara -tags no_pkg_config ``` ## License BSD 2-clause, see LICENSE file in the source distribution. ## Author Hilko Bengen go-yara-1.0.7/_examples/000077500000000000000000000000001324112535400150335ustar00rootroot00000000000000go-yara-1.0.7/_examples/simple-yara/000077500000000000000000000000001324112535400172565ustar00rootroot00000000000000go-yara-1.0.7/_examples/simple-yara/simple-yara.go000066400000000000000000000044501324112535400220330ustar00rootroot00000000000000package main import ( "github.com/hillu/go-yara" "errors" "flag" "log" "os" "strconv" "strings" ) type rule struct{ namespace, filename string } type rules []rule func (r *rules) Set(arg string) error { if len(arg) == 0 { return errors.New("empty rule specification") } a := strings.SplitN(arg, ":", 2) switch len(a) { case 1: *r = append(*r, rule{filename: a[0]}) case 2: *r = append(*r, rule{namespace: a[0], filename: a[1]}) } return nil } func (r *rules) String() string { var s string for _, rule := range *r { if len(s) > 0 { s += " " } if rule.namespace != "" { s += rule.namespace + ":" } s += rule.filename } return s } func printMatches(m []yara.MatchRule, err error) { if err == nil { if len(m) > 0 { for _, match := range m { log.Printf("- [%s] %s ", match.Namespace, match.Rule) } } else { log.Print("no matches.") } } else { log.Printf("error: %s.", err) } } func main() { var ( rules rules processScan bool pids []int ) flag.BoolVar(&processScan, "processes", false, "scan processes instead of files") flag.Var(&rules, "rule", "add rule") flag.Parse() if len(rules) == 0 { log.Fatal("no rules specified") } args := flag.Args() if len(args) == 0 { log.Fatal("no files or processes specified") } if processScan { for _, arg := range args { if pid, err := strconv.Atoi(arg); err != nil { log.Fatalf("Could not parse %s ad number", arg) } else { pids = append(pids, pid) } } } c, err := yara.NewCompiler() if err != nil { log.Fatalf("Failed to initialize YARA compiler: %s", err) } for _, rule := range rules { f, err := os.Open(rule.filename) if err != nil { log.Fatalf("Could not open rule file %s: %s", rule.filename, err) } err = c.AddFile(f, rule.namespace) f.Close() if err != nil { log.Fatalf("Could not parse rule file %s: %s", rule.filename, err) } } r, err := c.GetRules() if err != nil { log.Fatalf("Failed to compile rules: %s", err) } if processScan { for _, pid := range pids { log.Printf("Scanning process %d...", pid) m, err := r.ScanProc(pid, 0, 0) printMatches(m, err) } } else { for _, filename := range args { log.Printf("Scanning file %s... ", filename) m, err := r.ScanFile(filename, 0, 0) printMatches(m, err) } } } go-yara-1.0.7/callback-util.go000066400000000000000000000021201324112535400161070ustar00rootroot00000000000000package 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 go-yara-1.0.7/cgo.go000066400000000000000000000006031324112535400141540ustar00rootroot00000000000000// Copyright © 2015-2017 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 !no_pkg_config,!yara_static pkg-config: yara // #cgo !no_pkg_config,yara_static pkg-config: --static yara // #cgo no_pkg_config LDFLAGS: -lyara import "C" go-yara-1.0.7/compiler.go000066400000000000000000000137351324112535400152300ustar00rootroot00000000000000// Copyright © 2015-2017 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 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) } keepAlive(id) keepAlive(c) 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) } keepAlive(id) keepAlive(c) return } // DefineVariable defines a named variable for use by the compiler. // Boolean, int64, float64, and string types are supported. func (c *Compiler) DefineVariable(identifier string, value interface{}) (err error) { cid := C.CString(identifier) defer C.free(unsafe.Pointer(cid)) switch value.(type) { case bool: var v int if value.(bool) { v = 1 } err = newError(C.yr_compiler_define_boolean_variable( c.cptr, cid, 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, cid, C.int64_t(value))) case float64: err = newError(C.yr_compiler_define_float_variable( c.cptr, cid, 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, cid, cvalue)) default: err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted") } keepAlive(c) 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) keepAlive(c) 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 } go-yara-1.0.7/compiler_test.go000066400000000000000000000015411324112535400162570ustar00rootroot00000000000000package 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) } go-yara-1.0.7/compiler_yara37.go000066400000000000000000000051651324112535400164140ustar00rootroot00000000000000// Copyright © 2015-2017 Hilko Bengen // All rights reserved. // // Use of this source code is governed by the license that can be // found in the LICENSE file. //+build !yara3.3,!yara3.4,!yara3.5,!yara3.6 package yara /* #include #include #include char* includeCallback(char*, char*, char*, void*); void freeCallback(char*, void*); */ import "C" import ( "reflect" "unsafe" ) // CompilerIncludeFunc is the type of the function that can be // registered through SetIncludeCallback. It is called for every // include statement encountered by the compiler. The argument "name" // specifies the rule file to be included, "filename" specifies the // name of the rule file where the include statement has been // encountered, and "namespace" specifies the rule namespace. The sole // return value is a byte slice containing the contents of the // included file. A return value of nil signals an error to the YARA // compiler. // // See also: yr_compiler_set_include_callback in the YARA C API // documentation. type CompilerIncludeFunc func(name, filename, namespace string) []byte // DisableIncludes disables all include statements in the compiler. // See yr_compiler_set_include_callbacks. func (c *Compiler) DisableIncludes() { C.yr_compiler_set_include_callback(c.compiler.cptr, nil, nil, nil) keepAlive(c) return } //export includeCallback func includeCallback(name, filename, namespace *C.char, userData unsafe.Pointer) *C.char { id := *((*uintptr)(userData)) callbackFunc := callbackData.Get(id).(CompilerIncludeFunc) if buf := callbackFunc( C.GoString(name), C.GoString(filename), C.GoString(namespace), ); buf != nil { ptr := C.calloc(1, C.size_t(len(buf)+1)) if ptr == nil { return nil } outbuf := make([]byte, 0) hdr := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf)) hdr.Data, hdr.Len = uintptr(ptr), len(buf)+1 copy(outbuf, buf) return (*C.char)(ptr) } return nil } //export freeCallback func freeCallback(callbackResultPtr *C.char, userData unsafe.Pointer) { if callbackResultPtr != nil { C.free(unsafe.Pointer(callbackResultPtr)) } return } // SetIncludeCallback sets up cb as an include callback that is called // (through Go glue code) by the YARA compiler for every include // statement. func (c *Compiler) SetIncludeCallback(cb CompilerIncludeFunc) { if cb == nil { c.DisableIncludes() return } id := callbackData.Put(cb) C.yr_compiler_set_include_callback( c.compiler.cptr, C.YR_COMPILER_INCLUDE_CALLBACK_FUNC(C.includeCallback), C.YR_COMPILER_INCLUDE_FREE_FUNC(C.freeCallback), unsafe.Pointer(&id), ) keepAlive(id) keepAlive(c) return } go-yara-1.0.7/compiler_yara37_test.go000066400000000000000000000020671324112535400174510ustar00rootroot00000000000000//+build !yara3.3,!yara3.4,!yara3.5,!yara3.6 package yara import ( "testing" ) func setupCompiler(t *testing.T) *Compiler { c, err := NewCompiler() if err != nil { t.Fatal(err) } c.SetIncludeCallback(func(name, rulefile, namespace string) []byte { t.Logf(`Processing include "%s" (from ns="%s", file="%s")`, name, namespace, rulefile) if name == "existing" { return []byte(`rule ext { condition: true }`) } return nil }) return c } func TestCompilerIncludeCallback(t *testing.T) { c := setupCompiler(t) var err error if err = c.AddString(`include "existing"`, ""); err != nil { t.Fatalf(`Failed to include "existing" rule "file": %s`, err) } if err = c.AddString(`rule int { condition: ext }`, ""); err != nil { t.Fatalf(`Failed to define rule referring to included rule: %s`, err) } c = setupCompiler(t) if err = c.AddString(`include "non-existing"`, ""); err != nil { t.Logf("Compiler returned error on attempt to include non-existing rule: %s", err) } else { t.Fatal(`Compiler did not return error on non-existing include rule`) } } go-yara-1.0.7/crash_test.go000066400000000000000000000027011324112535400155440ustar00rootroot00000000000000package 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.") } // Adapted from test in https://github.com/hillu/go-yara/issues/22 func TestCompilerCrash(t *testing.T) { done := make(chan bool) go func(t *testing.T, done <-chan bool) { for i := 0; ; i++ { select { case <-done: return default: t.Logf("GC %d", i) runtime.GC() } } }(t, done) for i := 0; i < 10000; i++ { t.Logf("compile %d", i) makeRules(t, "rule test { strings: $a = /a.*a/ condition: $a }") } close(done) t.Log("Callback data intact after compiler.AddString() invocation. Yay.") return } go-yara-1.0.7/error.go000066400000000000000000000060121324112535400145350ustar00rootroot00000000000000// Copyright © 2015-2017 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", } go-yara-1.0.7/error_yara34.go000066400000000000000000000006141324112535400157220ustar00rootroot00000000000000// Copyright © 2017 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 an additional error code introduced with yara 3.4.0. // +build !yara3.3 package yara // #include import "C" func init() { errorStrings[C.ERROR_EMPTY_STRING] = "empty string" } go-yara-1.0.7/error_yara35.go000066400000000000000000000013611324112535400157230ustar00rootroot00000000000000// Copyright © 2017 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 additional error codes introduced with yara 3.5.0. // +build !yara3.3,!yara3.4 package yara // #include import "C" func init() { errorStrings[C.ERROR_DIVISION_BY_ZERO] = "division by zero" errorStrings[C.ERROR_REGULAR_EXPRESSION_TOO_LARGE] = "regular expression too large" errorStrings[C.ERROR_TOO_MANY_RE_FIBERS] = "too many regular expression fibers" errorStrings[C.ERROR_COULD_NOT_READ_PROCESS_MEMORY] = "could not read process memory" errorStrings[C.ERROR_INVALID_EXTERNAL_VARIABLE_TYPE] = "invalid external variable type" } go-yara-1.0.7/error_yara36.go000066400000000000000000000010031324112535400157150ustar00rootroot00000000000000// Copyright © 2017 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 additional error codes introduced with yara 3.6.0. // +build !yara3.3,!yara3.4,!yara3.5 package yara // #include import "C" func init() { errorStrings[C.ERROR_REGULAR_EXPRESSION_TOO_COMPLEX] = "regular expression too complex" errorStrings[C.ERROR_INVALID_MODULE_NAME] = "invalid module name" } go-yara-1.0.7/error_yara37.go000066400000000000000000000007521324112535400157300ustar00rootroot00000000000000// Copyright © 2017 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 additional error codes introduced with yara 3.7.0. // +build !yara3.3,!yara3.4,!yara3.5,!yara3.6 package yara // #include import "C" func init() { errorStrings[C.ERROR_TOO_MANY_STRINGS] = "too many strings" errorStrings[C.ERROR_INTEGER_OVERFLOW] = "integer overflow" } go-yara-1.0.7/goyara-logo.png000066400000000000000000000207071324112535400160120ustar00rootroot00000000000000PNG  IHDR=iVtEXtSoftwareAdobe ImageReadyqe<!iIDATx PU׵o0 JuPSHM TiHuXt3k+&_1ӾWvIi'F}L $H FB\['}>>5\s7^kܺu%W?Ƌc8}-#$GM"3L^dEB?J9t_g<#DC>.!zb''~(`0ADJ W;01joD RD~w'8W ,l,t$ա0Awzb Xd3WJ'/܋H!25àIVEa#jPH#` |٩u qTޱ$:+4 <3 5"hRqSO,}p.V0g;I?1UﺽaGi:T)f؇gi D;ux=lUkv;PNB|xb" z.(N"e? <,YT8utt5>Y0hN6 )su^$!$h0} 5 bF@9*%k`HyN}Z1Wio ILx9 !Fk^wyvn"C0]G<-0p汞+rCZl\c,]Xv0# h3BG4 mb=yĔxP.O:ƱϏ~XL#j4wR^PlmܢfA4x~yn WmXю`_ 'o @|Aߡ=cjaaL݉OE}}}kkKooϠ 2 `0p/?Gkx^qpif+slCCvӉ6H&]߽~ tz&Eb `4[]^gJk羴H֡`%S2m^n:zP}w#tz`4;$ƕԾJq9 ѓ.c/ldv2, o"~Q_Pk}͛7k'`غu+C`+c-Z]^U?eee4Hz|䦗f(A~3-UTvڕa"naM}B@JӋ^i&'J9AYpYbZ2$@u${Һ#W ֋:I =OlF JϑF:԰|Qc@q80Э xN(!YE$kM㌂_WMIP++kCwh) vG|xIeuzI۶p7ye_0:_MʡRSF4xGõm}<&]TT ΢Ip$my[3u $xc=WlN&]TR T:Hd+#⇒w .iD ?"a8qux*Nv"i:>~a :w۷o'K"$8i7eh*m6MCƉTTQvZ]uF,!;sD}׿~iii#6ommuyWR`έf\u`4/?d͜+Zm ,E˕ 1Z *s?oܸ|CCT99PwySPP@VLy)j_z UUU6˛7m {e$;HBh'G.^xʕ~46T&z7лtYT; O.677wt\ϼT A}|̆!dZ WU5oݺ*+O^?$@/#P0LY?qF _tQQ1yf e0T46 X1ȩnk׮\Y b!mVIl>裀P)+!kn4[n0 Mp\w61I$;+׼⪪Kns _$OaX"bLsB,Y|4Y>,[  ݪ`,׷v<2(]44Y6 o~ܣ`FC,YOU-*Uzs!d*2JTPtum&-!4 *vZ*:- 'IwiPSc Hww#oDe,FoPGW*˭fΜ3#ڻe"I4*XTXbrg /`X7Ae$B .a&*OuZٛ.KIZZڜ9sΥ&h{%XB^ưA,Zhx)粬m6 þ.+YTO~USS[YYIPՓ0o&'ܴ̀ibЋi&ޜ`E@dժb駷{oL~DMȓi1<~fg礦{LN5hV&qc0DuplatK\Ɨ= s6 I[3'|M1J, *gfB -! H DS> (3ImG[ ,sbixWJ&[Ȫ\d4[x(..BxC2 夔7~8xؐ; U,g/#ZR_]%9¦@e^w-$$Ӗ5چ/x:S5CX0A#KhQ>pezJ)>U,bBn4.ZZ~IGS/$slzJ`, i%zΕ"va42aDxJ@駷nkUzHIB`!@6pC??D)M{kvݨt Hp6m2ͦ11jr3L"xk'M U]a˗/7ds=•!,IRCyBwI" J}4ԍsc1G0sG<¿Φ)=W)#&[:7%ndWyI 6FAx2,?xGp9*x(((iNz ƨ̰,n/be#̀vyCd)Iy u]YMY!Vwt֋CA/.J,|P+5a/Kk+m^)0Uj١w'/Ac,kI+=*+++NT1'iKՒ.`YXwH|}n݉P"M<ᮀ4,L艏:HHz|_WwhF^|Txgkl7Bo⪍; (;mݦT.M 7~篝J6QRRlmm!?}t9RzoQ` TLy'hnRp #f@$7 *NvFiݷ՚5!S;q_N2%66vxxƍ2\ql@b^E&45vaaG5 r[# ,T\#8DԊ$'谼1d FZhV'\0B$29 k₍}ܨ24VAx"{G7ȑ5%[1i4f{#\XDtɊG1hD7~G^Ͻ?ץes?77~mzAw:πrB4=BK`R_$4έL#0P]oJ|Gԟ>p c鳾xOtC!|#DEepQs=MTC{z'r d$z54>-M|gh,]) #; -;^/T_+ g<ë>k:.tuAiISU%`MeK4zvH$0 %sZ8i A9„%zGDl CN:F2R vY0M8Ȑ. }.,<Q7g a4zp.|uW6'] GȜKre0h<6rBQbz @~ ``@Y<'_VHKE7u+Sf&L!Jr`nt:!.浝}x %gWOm\ g?EhL~0(]ϟ:3QoZsp:9sf__/Bj[WC0X8KkhHA *JW䦗f_جUiR݉EEŅnJ>!U|<(9d&J$ dkf%eUe*LWwuu*^w%͟?moo ẊE+ h dS&e*&*tpOkz A EYY4 phsXHJaaaj w7N^0/0kaPtnRAjқv ˛oArܱ ёxNL1Ip .,՛|s ?(qB^3ztIUyN ӥIX[E2 `h:!',Yڣ7αGZ]ǧn$c0f&|8+*K^1 gcKW F瓧}TG/kf%R8sMZ#1AQ1Fxv&9W5kը"oxe.Vd(3:zBDFaAǕw$Hkòn0UZATڀ⇵5g CƔɊ" &p߮7\VVVYY M9~ظekmcSuW4ӭh$WUȋ/3~z+ȹ ) \pPdHD "c3Ρ Eѧ_7x ,pWcyƫBpUٟ24q3G4X׶3PkK'ͅ} =3voIpgGJϾ]b i\Ӌڻa -!u6Un06?֢uhkt0-ssh٩\`pښ F$A35qL0Vnx;h)M|a. 4" g bPy}4Њ:' u&g)-bI&8 [RHe,VGƁ[M8٩"0`.:F~|'NC`6l FਈufL/`c^1b㇮cԏo-&B\w8$)ACsd~̿ LUp('7INT| ~&>!<"!s)9R%n xF0Υc go-yara-1.0.7/keepalive.go000066400000000000000000000003671324112535400153600ustar00rootroot00000000000000// Copyright © 2015-2017 Hilko Bengen . All rights reserved. // Use of this source code is governed by the license that can be // found in the LICENSE file. // +build !go1.7 package yara func keepAlive(interface{}) {} go-yara-1.0.7/keepalive_go17.go000066400000000000000000000006511324112535400162110ustar00rootroot00000000000000// Copyright © 2015-2017 Hilko Bengen . All rights reserved. // Use of this source code is governed by the license that can be // found in the LICENSE file. // +build go1.7 package yara import ( "runtime" ) // The post-Go-1.7 version of keepAlive() contains a "call" to // runtime.KeepAlive which is recognized as a hint by the compiler. func keepAlive(i interface{}) { runtime.KeepAlive(i) } go-yara-1.0.7/main_test.go000066400000000000000000000005411324112535400153700ustar00rootroot00000000000000package 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) } go-yara-1.0.7/object.go000066400000000000000000000001321324112535400146470ustar00rootroot00000000000000package yara /* #include */ import "C" type Object struct{ cptr *C.YR_OBJECT } go-yara-1.0.7/ported_test.go000066400000000000000000000474231324112535400157530ustar00rootroot00000000000000package 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")) } go-yara-1.0.7/rule.go000066400000000000000000000116741324112535400143650ustar00rootroot00000000000000// Copyright © 2015-2017 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 // rule_identifier is a union accessor function. static const char* rule_identifier(YR_RULE* r) { return r->identifier; } // rule_namespace is a union accessor function. static const char* rule_namespace(YR_RULE* r) { return r->ns->name; } // rule_tags returns pointers to the tag names associated with a rule, // using YARA's own implementation static void rule_tags(YR_RULE* r, const char *tags[], int *n) { const char *tag; int i = 0; yr_rule_tags_foreach(r, tag) { if (i < *n) tags[i] = tag; i++; }; *n = i; return; } // rule_tags returns pointers to the meta variables associated with a // rule, using YARA's own implementation static void rule_metas(YR_RULE* r, const YR_META *metas[], int *n) { const YR_META *meta; int i = 0; yr_rule_metas_foreach(r, meta) { if (i < *n) metas[i] = meta; i++; }; *n = i; return; } // meta_get is an accessor function for unions that are not directly // accessible from Go because CGO does not understand them. static void meta_get(YR_META *m, const char** identifier, char** string) { *identifier = m->identifier; *string = m->string; return; } // rule_strings returns pointers to the matching strings associated // with a rule, using YARA's own implementation. static void rule_strings(YR_RULE* r, const YR_STRING *strings[], int *n) { const YR_STRING *string; int i = 0; yr_rule_strings_foreach(r, string) { if (i < *n) strings[i] = string; i++; } *n = i; return; } // string_identifier is a union accessor function. static const char* string_identifier(YR_STRING* s) { return s->identifier; } // string_matches static void string_matches(YR_STRING* s, const YR_MATCH *matches[], int *n) { const YR_MATCH *match; int i = 0; yr_string_matches_foreach(s, match) { if (i < *n) matches[i] = match; i++; }; *n = i; return; } */ import "C" import "unsafe" // Rule represents a single rule as part of a ruleset type Rule struct{ cptr *C.YR_RULE } // Identifier returns the rule's name. func (r *Rule) Identifier() string { return C.GoString(C.rule_identifier(r.cptr)) } // Namespace returns the rule's namespace. func (r *Rule) Namespace() string { return C.GoString(C.rule_namespace(r.cptr)) } // Tags returns the rule's tags. func (r *Rule) Tags() (tags []string) { var size C.int C.rule_tags(r.cptr, nil, &size) if size == 0 { return } tagptrs := make([]*C.char, int(size)) C.rule_tags(r.cptr, &tagptrs[0], &size) for _, t := range tagptrs { tags = append(tags, C.GoString(t)) } return } // Metas returns a map containing the rule's meta variables. Values // can be of type string, int, bool, or nil. func (r *Rule) Metas() (metas map[string]interface{}) { metas = make(map[string]interface{}) var size C.int C.rule_metas(r.cptr, nil, &size) if size == 0 { return } mptrs := make([]*C.YR_META, int(size)) C.rule_metas(r.cptr, &mptrs[0], &size) for _, m := range mptrs { var id, str *C.char C.meta_get(m, &id, &str) switch m._type { case C.META_TYPE_NULL: metas[C.GoString(id)] = nil case C.META_TYPE_STRING: metas[C.GoString(id)] = C.GoString(str) case C.META_TYPE_INTEGER: metas[C.GoString(id)] = int(m.integer) case C.META_TYPE_BOOLEAN: metas[C.GoString(id)] = m.integer != 0 } } return } // String represents a string as part of a rule type String struct{ cptr *C.YR_STRING } // Strings returns the rule's strings func (r *Rule) Strings() (strs []String) { var size C.int C.rule_strings(r.cptr, nil, &size) if size == 0 { return } ptrs := make([]*C.YR_STRING, int(size)) C.rule_strings(r.cptr, &ptrs[0], &size) for _, ptr := range ptrs { strs = append(strs, String{ptr}) } return } // Identifier returns the string's name func (s *String) Identifier() string { return C.GoString(C.string_identifier(s.cptr)) } // Match represents a string match type Match struct{ cptr *C.YR_MATCH } // Matches returns all matches that have been recorded for the string. func (s *String) Matches() (matches []Match) { var size C.int C.string_matches(s.cptr, nil, &size) ptrs := make([]*C.YR_MATCH, int(size)) if size == 0 { return } C.string_matches(s.cptr, &ptrs[0], &size) for _, ptr := range ptrs { matches = append(matches, Match{ptr}) } return } // Data returns the blob of data associated with the string match func (m *Match) Data() []byte { return C.GoBytes(unsafe.Pointer(m.cptr.data), C.int(m.cptr.data_length)) } // Offset returns the offset at which the string match occurred func (m *Match) Offset() int64 { return int64(m.cptr.offset) } func (r *Rule) getMatchStrings() (matchstrings []MatchString) { for _, s := range r.Strings() { for _, m := range s.Matches() { matchstrings = append(matchstrings, MatchString{ Name: s.Identifier(), Offset: uint64(m.Offset()), Data: m.Data(), }) } } return } go-yara-1.0.7/rule_yara37.go000066400000000000000000000007131324112535400155430ustar00rootroot00000000000000// Copyright © 2015-2017 Hilko Bengen // All rights reserved. // // Use of this source code is governed by the license that can be // found in the LICENSE file. //+build !yara3.3,!yara3.4,!yara3.5,!yara3.6 package yara // #include import "C" // Enable enables a single rule func (r *Rule) Enable() { C.yr_rule_enable(r.cptr) } // Disable disables a single rule func (r *Rule) Disable() { C.yr_rule_disable(r.cptr) } go-yara-1.0.7/rules.go000066400000000000000000000147061324112535400145470ustar00rootroot00000000000000// Copyright © 2015-2017 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 scanCallbackFunc(int, void*, void*); */ import "C" import ( "errors" "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() } // 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, returning // matches via a list of MatchRule objects. func (r *Rules) ScanMem(buf []byte, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) { cb := MatchRules{} err = r.ScanMemWithCallback(buf, flags, timeout, &cb) matches = cb return } // ScanMemWithCallback scans an in-memory buffer using the ruleset. // For every event emitted by libyara, the appropriate method on the // ScanCallback object is called. func (r *Rules) ScanMemWithCallback(buf []byte, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { var ptr *C.uint8_t if len(buf) > 0 { ptr = (*C.uint8_t)(unsafe.Pointer(&(buf[0]))) } id := callbackData.Put(cb) 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.scanCallbackFunc), unsafe.Pointer(&id), C.int(timeout/time.Second))) keepAlive(id) keepAlive(r) return } // ScanFile scans a file using the ruleset, returning matches via a // list of MatchRule objects. func (r *Rules) ScanFile(filename string, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) { cb := MatchRules{} err = r.ScanFileWithCallback(filename, flags, timeout, &cb) matches = cb return } // ScanFileWithCallback scans a file using the ruleset. For every // event emitted by libyara, the appropriate method on the // ScanCallback object is called. func (r *Rules) ScanFileWithCallback(filename string, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) id := callbackData.Put(cb) defer callbackData.Delete(id) err = newError(C.yr_rules_scan_file( r.cptr, cfilename, C.int(flags), C.YR_CALLBACK_FUNC(C.scanCallbackFunc), unsafe.Pointer(&id), C.int(timeout/time.Second))) keepAlive(id) keepAlive(r) return } // ScanProc scans a live process using the ruleset, returning matches // via a list of MatchRule objects. func (r *Rules) ScanProc(pid int, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) { cb := MatchRules{} err = r.ScanProcWithCallback(pid, flags, timeout, &cb) matches = cb return } // ScanProcWithCallback scans a live process using the ruleset. For // every event emitted by libyara, the appropriate method on the // ScanCallback object is called. func (r *Rules) ScanProcWithCallback(pid int, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { id := callbackData.Put(cb) defer callbackData.Delete(id) err = newError(C.yr_rules_scan_proc( r.cptr, C.int(pid), C.int(flags), C.YR_CALLBACK_FUNC(C.scanCallbackFunc), unsafe.Pointer(&id), C.int(timeout/time.Second))) keepAlive(id) keepAlive(r) 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)) keepAlive(r) 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(identifier string, value interface{}) (err error) { cid := C.CString(identifier) defer C.free(unsafe.Pointer(cid)) switch value.(type) { case bool: var v int if value.(bool) { v = 1 } err = newError(C.yr_rules_define_boolean_variable( r.cptr, cid, 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, cid, C.int64_t(value))) case float64: err = newError(C.yr_rules_define_float_variable( r.cptr, cid, 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, cid, cvalue)) default: err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted") } keepAlive(r) return } // GetRules returns a slice of rule objects that are part of the // ruleset func (r *Rules) GetRules() (rv []Rule) { for p := unsafe.Pointer(r.cptr.rules_list_head); (*C.YR_RULE)(p).g_flags&C.RULE_GFLAGS_NULL == 0; p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(*r.cptr.rules_list_head)) { rv = append(rv, Rule{(*C.YR_RULE)(p)}) } return } go-yara-1.0.7/rules_callback.go000066400000000000000000000070371324112535400163620ustar00rootroot00000000000000package yara /* #include #include */ import "C" import ( "reflect" "unsafe" ) // ScanCallback is a placeholder for different interfaces that may be // implemented by the callback object that is passed to the // (*Rules).Scan*WithCallback methods. type ScanCallback interface{} // ScanCallbackMatch is used to record rules that matched during a // scan. The RuleMatching method corresponds to YARA's // CALLBACK_MSG_RULE_MATCHING message. type ScanCallbackMatch interface { RuleMatching(*Rule) (bool, error) } // ScanCallbackNoMatch is used to record rules that did not match // during a scan. The RuleNotMatching method corresponds to YARA's // CALLBACK_MSG_RULE_NOT_MATCHING mssage. type ScanCallbackNoMatch interface { RuleNotMatching(*Rule) (bool, error) } // ScanCallbackFinished is used to signal that a scan has finished. // The ScanFinished method corresponds to YARA's // CALLBACK_MSG_SCAN_FINISHED message. type ScanCallbackFinished interface { ScanFinished() (bool, error) } // ScanCallbackModuleImport is used to provide data to a YARA module. // The ImportModule method corresponds to YARA's // CALLBACK_MSG_IMPORT_MODULE message. type ScanCallbackModuleImport interface { ImportModule(string) ([]byte, bool, error) } // ScanCallbackModuleImportFinished can be used to free resources that // have been used in the ScanCallbackModuleImport implementation. The // ModuleImported method corresponds to YARA's // CALLBACK_MSG_MODULE_IMPORTED message. type ScanCallbackModuleImportFinished interface { ModuleImported(*Object) (bool, error) } //export scanCallbackFunc func scanCallbackFunc(message C.int, messageData, userData unsafe.Pointer) C.int { id := *(*uintptr)(userData) cb := callbackData.Get(id) var abort bool var err error switch message { case C.CALLBACK_MSG_RULE_MATCHING: if c, ok := cb.(ScanCallbackMatch); ok { r := (*C.YR_RULE)(messageData) abort, err = c.RuleMatching(&Rule{r}) } case C.CALLBACK_MSG_RULE_NOT_MATCHING: if c, ok := cb.(ScanCallbackNoMatch); ok { r := (*C.YR_RULE)(messageData) abort, err = c.RuleNotMatching(&Rule{r}) } case C.CALLBACK_MSG_SCAN_FINISHED: if c, ok := cb.(ScanCallbackFinished); ok { abort, err = c.ScanFinished() } case C.CALLBACK_MSG_IMPORT_MODULE: if c, ok := cb.(ScanCallbackModuleImport); ok { mi := (*C.YR_MODULE_IMPORT)(messageData) var buf []byte if buf, abort, err = c.ImportModule(C.GoString(mi.module_name)); len(buf) == 0 { break } // FIXME: Memory leak: When/how should this buffer be free()d? cbuf := C.calloc(1, C.size_t(len(buf))) outbuf := make([]byte, 0) hdr := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf)) hdr.Data, hdr.Len = uintptr(cbuf), len(buf) copy(outbuf, buf) mi.module_data, mi.module_data_size = unsafe.Pointer(&outbuf[0]), C.size_t(len(outbuf)) } case C.CALLBACK_MSG_MODULE_IMPORTED: if c, ok := cb.(ScanCallbackModuleImportFinished); ok { obj := (*C.YR_OBJECT)(messageData) abort, err = c.ModuleImported(&Object{obj}) } } if err != nil { return C.CALLBACK_ERROR } if abort { return C.CALLBACK_ABORT } return C.CALLBACK_CONTINUE } // MatchRules is used to collect matches that are returned by the // simple (*Rules).Scan* methods. type MatchRules []MatchRule // RuleMatching implements the ScanCallbackMatch interface for // MatchRules. func (mr *MatchRules) RuleMatching(r *Rule) (abort bool, err error) { *mr = append(*mr, MatchRule{ Rule: r.Identifier(), Namespace: r.Namespace(), Tags: r.Tags(), Meta: r.Metas(), Strings: r.getMatchStrings(), }) return } go-yara-1.0.7/rules_test.go000066400000000000000000000174311324112535400156040ustar00rootroot00000000000000package yara import ( "bytes" "compress/bzip2" "fmt" "io/ioutil" "os" "os/exec" "reflect" "runtime" "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") } } // in Go 1.8 this code does not work in go-yara 1.0.2 // go 1.8/debian stretch panics // go 1.8/darwin produces stack overflow func TestWriterBuffer(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) buf := new(bytes.Buffer) if err := r.Write(buf); err != nil { t.Fatalf("write to bytes.Buffer: %s", err) } } // 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, err := Compile(string(rulesBuf.Bytes()), nil) if err != nil { t.Fatalf("compile text for bzip2 rule compression: %s", err) } cmd := exec.Command("bzip2", "-c") compressStream, _ := cmd.StdinPipe() buf := bytes.NewBuffer(nil) cmd.Stdout = buf if err := cmd.Start(); err != nil { t.Fatalf("start bzip2 process: %s", err) } if err := r.Write(compressStream); err != nil { t.Fatalf("pipe to bzip2 process: %s", err) } compressStream.Close() if err := cmd.Wait(); err != nil { t.Fatalf("wait for bzip2 process: %s", err) } if _, err := ReadRules(bzip2.NewReader(bytes.NewReader(buf.Bytes()))); err != nil { t.Fatalf("read using compress/bzip2: %s", err) } } // 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) } } func TestRule(t *testing.T) { r := makeRules(t, ` rule t1 : tag1 { meta: author = "Author One" strings: $a = "abc" fullword condition: $a } rule t2 : tag2 x y { meta: author = "Author Two" strings: $b = "def" condition: $b } rule t3 : tag3 x y z { meta: author = "Author Three" strings: $c = "ghi" condition: $c } rule t4 { strings: $d = "qwe" condition: $d }`) for _, r := range r.GetRules() { t.Logf("%s:%s %#v", r.Namespace(), r.Identifier(), r.Tags()) switch r.Identifier() { case "t1": if !reflect.DeepEqual(r.Tags(), []string{"tag1"}) { t.Error("Got wrong tags for t1") } if !reflect.DeepEqual(r.Metas(), map[string]interface{}{"author": "Author One"}) { t.Error("Got wrong meta variables for t1") } case "t2": if !reflect.DeepEqual(r.Tags(), []string{"tag2", "x", "y"}) { t.Error("Got wrong tags for t2") } if !reflect.DeepEqual(r.Metas(), map[string]interface{}{"author": "Author Two"}) { t.Error("Got wrong meta variables for t2") } case "t3": if !reflect.DeepEqual(r.Tags(), []string{"tag3", "x", "y", "z"}) { t.Error("Got wrong tags for t3") } if !reflect.DeepEqual(r.Metas(), map[string]interface{}{"author": "Author Three"}) { t.Error("Got wrong meta variables for t3") } case "t4": if len(r.Tags()) != 0 { t.Error("Got tags for t4") } if !reflect.DeepEqual(r.Metas(), map[string]interface{}{}) { t.Error("Got wrong meta variables for t4") } default: t.Errorf("Found unexpected rule name: %#v", r.Identifier()) } } } type mycb struct{ t *testing.T } func (c mycb) RuleMatching(r *Rule) (bool, error) { c.t.Logf("RuleMatching callback called: rule=%s", r.Identifier()) return false, nil } func (c mycb) RuleNotMatching(r *Rule) (bool, error) { c.t.Logf("RuleNotMatching callback called: rule=%s", r.Identifier()) return false, nil } func (c mycb) ScanFinished() (bool, error) { c.t.Log("ScanFinished callback called") return false, nil } func (c *mycb) ImportModule(s string) ([]byte, bool, error) { c.t.Logf("ImportModule callback called: module=%s", s) return []byte("{}"), false, nil } func (c *mycb) ModuleImported(*Object) (bool, error) { c.t.Log("ModuleImported callback called") return false, nil } func TestImportDataCallback(t *testing.T) { r := makeRules(t, `import "tests" import "pe" rule t1 { condition: true } rule t2 { condition: false }`) if err := r.ScanMemWithCallback([]byte(""), 0, 0, &mycb{t}); err != nil { t.Error(err) } runtime.GC() } go-yara-1.0.7/rules_yara34.go000066400000000000000000000061701324112535400157260ustar00rootroot00000000000000// Copyright © 2015-2017 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 // 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); } #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 scanCallbackFunc(int, void*, void*); */ import "C" import ( "io" "runtime" "time" "unsafe" ) // ScanFileDescriptor scans a file using the ruleset, returning // matches via a list of MatchRule objects. func (r *Rules) ScanFileDescriptor(fd uintptr, flags ScanFlags, timeout time.Duration) (matches []MatchRule, err error) { cb := MatchRules{} err = r.ScanFileDescriptorWithCallback(fd, flags, timeout, &cb) matches = cb return } // ScanFileDescriptor scans a file using the ruleset. For every event // emitted by libyara, the appropriate method on the ScanCallback // object is called. func (r *Rules) ScanFileDescriptorWithCallback(fd uintptr, flags ScanFlags, timeout time.Duration, cb ScanCallback) (err error) { id := callbackData.Put(cb) defer callbackData.Delete(id) err = newError(C._yr_rules_scan_fd( r.cptr, C.int(fd), C.int(flags), C.YR_CALLBACK_FUNC(C.scanCallbackFunc), unsafe.Pointer(&id), C.int(timeout/time.Second))) keepAlive(id) keepAlive(r) 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{ write: C.YR_STREAM_WRITE_FUNC(C.streamWrite), // The complaint from go vet about possible misuse of // unsafe.Pointer is wrong: user_data will be interpreted as // an uintptr on the other side of the callback user_data: unsafe.Pointer(id), } err = newError(C.yr_rules_save_stream(r.cptr, &stream)) keepAlive(id) keepAlive(r) 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{ read: C.YR_STREAM_READ_FUNC(C.streamRead), // The complaint from go vet about possible misuse of // unsafe.Pointer is wrong, see above. user_data: unsafe.Pointer(id), } if err := newError(C.yr_rules_load_stream(&stream, &(r.rules.cptr))); err != nil { return nil, err } runtime.SetFinalizer(r.rules, (*rules).finalize) keepAlive(id) return r, nil } go-yara-1.0.7/stream.go000066400000000000000000000031341324112535400147010ustar00rootroot00000000000000// Copyright © 2015-2017 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 } // writeFull does its best to write all of buf to w. See io.ReadFull. 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 } go-yara-1.0.7/stress_test.go000066400000000000000000000030621324112535400157700ustar00rootroot00000000000000package 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() } go-yara-1.0.7/testdata/000077500000000000000000000000001324112535400146675ustar00rootroot00000000000000go-yara-1.0.7/testdata/rules.yar000066400000000000000000000001361324112535400165360ustar00rootroot00000000000000rule test : tag1 { meta: author = "Hilko Bengen" strings: $a = "abc" fullword condition: $a } go-yara-1.0.7/util.go000066400000000000000000000013361324112535400143650ustar00rootroot00000000000000// Copyright © 2015-2017 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") }