hillu / go-yara

Go bindings for YARA
BSD 2-Clause "Simplified" License
350 stars 112 forks source link

unstable rules #133

Closed RRancio closed 7 months ago

RRancio commented 8 months ago

I figured out that when im using common Yara executable and try to use the Golang binding it works quite different:

Go-Yara:

Using this code:

filePath := "C:\\Users\\Rancio\\Documents\\detections\\rules\\test.yar"

    rulesFile, _ := os.Open(filePath)
    defer rulesFile.Close()

    err = sc.AddFile(rulesFile, "")
    if err != nil {
        fmt.Println(err)
    }

    rolol, err := sc.GetRules()
    if err != nil {
        fmt.Println(err)
    }

    var m yara.MatchRules

    for _, filename := range result {
        filenameLower := strings.ToLower(filename)
        if !strings.Contains(filenameLower, "temp") {
            fileInfo, err := os.Stat(filename)
            if err != nil {
                continue
            }

            if fileInfo.Size() > 20*1024*1024 {
                continue
            }

            err = rolol.ScanFile(filenameLower, yara.ScanFlagsFastMode, 0, &m)
            if err != nil {
                fmt.Println(err)
                continue
            }

            if len(m) > 0 {
                for _, match := range m {
                    rule := strings.Replace(match.Rule, "_", " ", -1)
                    fmt.Println(filenameLower, rule)
                }
            }
        }
    }

then, for some reason, it throws results like this:

c:\windows\system32\msxml3.dll DLL
c:\windows\syswow64\windowspowershell\v1.0\powershell_ise.exe DLL
c:\users\rancio\appdata\local\microsoft\windows\explorer\iconcache_1280.db DLL
c:\program files\cheat engine 7.5\tutorial-x86_64.exe DLL
c:\program files\desktop.ini DLL
c:\windows\system32\win32calc.exe DLL
c:\windows\system32\iscsicpl.exe DLL
c:\windows\system32\staterepository.core.dll DLL
c:\users\rancio\appdata\local\microsoft\windows\fonts\inter-black.ttf DLL
c:\users\rancio\documents\python\rem.py DLL
c:\users\public\desktop.ini DLL
c:\windows\system32\networkuxbroker.dll DLL
c:\windows\system32\windows.staterepositoryclient.dll DLL

but when i try to check if was a rule issue, using the installed Yara on Windows, it shows nothing, like there wasn't something detected.

C:\Users\Rancio\Documents\Code\Ocean\FiveM\client>yara C:\Users\Rancio\Documents\detections\rules\test.yar "c:\program files\cheat engine 7.5\unins000.exe"

C:\Users\Rancio\Documents\Code\Ocean\FiveM\client>yara C:\Users\Rancio\Documents\detections\rules\test.yar c:\windows\system32\ehstorapi.dll

C:\Users\Rancio\Documents\Code\Ocean\FiveM\client>yara C:\Users\Rancio\Documents\detections\rules\test.yar c:\program files (x86)\bandicam\bdcam_nonadmin.exe
error: could not open file: c:\program

C:\Users\Rancio\Documents\Code\Ocean\FiveM\client>yara C:\Users\Rancio\Documents\detections\rules\test.yar "c:\program files (x86)\bandicam\bdcam_nonadmin.exe"

C:\Users\Rancio\Documents\Code\Ocean\FiveM\client>

im doing something wrong?

edit: im trying to make it work same as the non-golang binding

hillu commented 8 months ago

I can't tell you how you are using yara.exe wrong (or how it may be broken), but one thing that comes to mind is that you need to quote your paths that contain strings.

Does running yara --help output anything?

RRancio commented 8 months ago

After some time i figured that maybe it is a code issue, i tested using: https://github.com/EvilAres/saferwall/blob/edec505dab55a2a1e0c0cf24b436194835510f0f/cmd/yara/main.go#L8

and it seems working fine whitout unstable flags, ill return here if i solve it by my own

but yeah, the command is working fine, every yara module is working fine, is just the scanfile that doesn't follow rules conditions and flags every possible file.

Microsoft Windows [Versión 10.0.17763.1]
(c) 2018 Microsoft Corporation. Todos los derechos reservados.

C:\Users\Rancio>yara --help
YARA 4.3.2, the pattern matching swiss army knife.
Usage: yara [OPTION]... [NAMESPACE:]RULES_FILE... FILE | DIR | PID

Mandatory arguments to long options are mandatory for short options too.

       --atom-quality-table=FILE           path to a file with the atom quality table
  -C,  --compiled-rules                    load compiled rules
  -c,  --count                             print only number of matches
  -d,  --define=VAR=VALUE                  define external variable
       --fail-on-warnings                  fail on warnings
  -f,  --fast-scan                         fast matching mode
  -h,  --help                              show this help and exit
  -i,  --identifier=IDENTIFIER             print only rules named IDENTIFIER
       --max-process-memory-chunk=NUMBER   set maximum chunk size while reading process memory (default=1073741824)
  -l,  --max-rules=NUMBER                  abort scanning after matching a NUMBER of rules
       --max-strings-per-rule=NUMBER       set maximum number of strings per rule (default=10000)
  -x,  --module-data=MODULE=FILE           pass FILE's content as extra data to MODULE
  -n,  --negate                            print only not satisfied rules (negate)
  -N,  --no-follow-symlinks                do not follow symlinks when scanning
  -w,  --no-warnings                       disable warnings
  -m,  --print-meta                        print metadata
  -D,  --print-module-data                 print module data
  -M,  --module-names                      show module names
  -e,  --print-namespace                   print rules' namespace
  -S,  --print-stats                       print rules' statistics
  -s,  --print-strings                     print matching strings
  -L,  --print-string-length               print length of matched strings
  -X,  --print-xor-key                     print xor key and plaintext of matched strings
  -g,  --print-tags                        print tags
  -r,  --recursive                         recursively search directories
       --scan-list                         scan files listed in FILE, one per line
  -z,  --skip-larger=NUMBER                skip files larger than the given size when scanning a directory
  -k,  --stack-size=SLOTS                  set maximum stack size (default=16384)
  -t,  --tag=TAG                           print only rules tagged as TAG
  -p,  --threads=NUMBER                    use the specified NUMBER of threads to scan a directory
  -a,  --timeout=SECONDS                   abort scanning after the given number of SECONDS
  -v,  --version                           show version information

Send bug reports and suggestions to: vmalvarez@virustotal.com.

C:\Users\Rancio>
hillu commented 8 months ago

It seems to me that you are re-using your MatchRules object without resetting it between scans. Matching results just get appended to it.

Please also have a look at the simple-yara example and the tests.

RRancio commented 8 months ago

Hey, after some minutes i fixed the issue, this is the final code:

set rule and scan:

package API

import (
    "strings"

    yara "github.com/hillu/go-yara/v4"
)

func NewFromReader(ruleReader *strings.Reader) (*yara.Scanner, error) {
    r, err := LoadRuleFromReader(ruleReader)
    if err != nil {
        return nil, err
    }

    s, err := yara.NewScanner(r)
    if err != nil {
        return nil, err
    }

    return s, nil
}

func LoadRuleFromReader(reader *strings.Reader) (*yara.Rules, error) {
    rules, err := yara.ReadRules(reader)
    if err != nil {
        return nil, err
    }
    return rules, nil
}

func ScanFile(scanner *yara.Scanner, filePath string) ([]yara.MatchRule, error) {
    var matches yara.MatchRules
    err := scanner.SetCallback(&matches).ScanFile(filePath)
    return matches, err
}

func ScanProc(scanner *yara.Scanner, pid int) ([]yara.MatchRule, error) {
    var matches yara.MatchRules
    err := scanner.SetCallback(&matches).ScanProc(pid)
    return matches, err
}

file for scan:

sc, err := yara.NewCompiler()
    if sc == nil || err != nil {
        fmt.Println(err)
        return
    }

    detections := req.A(req.GENERICCRIPT)

    reader := strings.NewReader(string(detections))

    scanner, err := API.NewFromReader(reader)
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, filename := range result {
        filenameLower := strings.ToLower(filename)
        if !strings.Contains(filenameLower, "temp") {
            fileInfo, err := os.Stat(filename)
            if err != nil {
                continue
            }

            if fileInfo.Size() > 20*1024*1024 {
                continue
            }

            matches, err := API.ScanFile(scanner, filenameLower)
            if err != nil {
                fmt.Println(err)
                continue
            }

            if len(matches) > 0 {
                for _, match := range matches {
                    rule := strings.Replace(match.Rule, "_", " ", -1)
                    fmt.Println(rule, filenameLower)
                    other.SuspiciousAdd(fmt.Sprintf("%s:::Generic %s", filename, rule))
                }
            }
        }
    }

now i have a question, where should i add the yara.ScanFlagsFastMode?, on the another code i was using it on the same "ScanFile" function, but using callback seems quite different

hillu commented 8 months ago

The Scanner object has a SetFlags method.

Otherwise, you can still use the Rules object directly, just make sure that your match objects are reset.