Skip to content

Commit

Permalink
feat: implement C and Go APIs for setting global variables in scanner.
Browse files Browse the repository at this point in the history
  • Loading branch information
plusvic committed Feb 27, 2024
1 parent 180f00f commit caa1b46
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 3 deletions.
20 changes: 20 additions & 0 deletions capi/include/yara-x.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,24 @@ enum YRX_RESULT yrx_scanner_set_module_output(struct YRX_SCANNER *scanner,
const uint8_t *data,
size_t len);

// Sets the value of a global variable of type string.
enum YRX_RESULT yrx_scanner_set_global_str(struct YRX_SCANNER *scanner,
const char *ident,
const char *value);

// Sets the value of a global variable of type bool.
enum YRX_RESULT yrx_scanner_set_global_bool(struct YRX_SCANNER *scanner,
const char *ident,
bool value);

// Sets the value of a global variable of type int.
enum YRX_RESULT yrx_scanner_set_global_int(struct YRX_SCANNER *scanner,
const char *ident,
int64_t value);

// Sets the value of a global variable of type float.
enum YRX_RESULT yrx_scanner_set_global_float(struct YRX_SCANNER *scanner,
const char *ident,
double value);

#endif /* YARA_X */
76 changes: 76 additions & 0 deletions capi/src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,82 @@ pub unsafe extern "C" fn yrx_scanner_set_module_output(
}
}

unsafe extern "C" fn yrx_scanner_set_global<
T: TryInto<yara_x::Variable, Error = yara_x::VariableError>,
>(
scanner: *mut YRX_SCANNER,
ident: *const c_char,
value: T,
) -> YRX_RESULT {
if scanner.is_null() {
return YRX_RESULT::INVALID_ARGUMENT;
}

let ident = match CStr::from_ptr(ident).to_str() {
Ok(ident) => ident,
Err(_) => return YRX_RESULT::INVALID_ARGUMENT,
};

let scanner = scanner.as_mut().unwrap();

match scanner.inner.set_global(ident, value) {
Ok(_) => {
LAST_ERROR.set(None);
YRX_RESULT::SUCCESS
}
Err(err) => {
LAST_ERROR.set(Some(CString::new(err.to_string()).unwrap()));
YRX_RESULT::SCAN_ERROR
}
}
}

/// Sets the value of a global variable of type string.
#[no_mangle]
pub unsafe extern "C" fn yrx_scanner_set_global_str(
scanner: *mut YRX_SCANNER,
ident: *const c_char,
value: *const c_char,
) -> YRX_RESULT {
let value = if let Ok(value) = CStr::from_ptr(value).to_str() {
value
} else {
return YRX_RESULT::INVALID_ARGUMENT;
};

yrx_scanner_set_global(scanner, ident, value)
}

/// Sets the value of a global variable of type bool.
#[no_mangle]
pub unsafe extern "C" fn yrx_scanner_set_global_bool(
scanner: *mut YRX_SCANNER,
ident: *const c_char,
value: bool,
) -> YRX_RESULT {
yrx_scanner_set_global(scanner, ident, value)
}

/// Sets the value of a global variable of type int.
#[no_mangle]
pub unsafe extern "C" fn yrx_scanner_set_global_int(
scanner: *mut YRX_SCANNER,
ident: *const c_char,
value: i64,
) -> YRX_RESULT {
yrx_scanner_set_global(scanner, ident, value)
}

/// Sets the value of a global variable of type float.
#[no_mangle]
pub unsafe extern "C" fn yrx_scanner_set_global_float(
scanner: *mut YRX_SCANNER,
ident: *const c_char,
value: f64,
) -> YRX_RESULT {
yrx_scanner_set_global(scanner, ident, value)
}

unsafe fn slice_from_ptr_and_len<'a>(
data: *const u8,
len: usize,
Expand Down
14 changes: 11 additions & 3 deletions capi/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::compiler::{
use crate::{
yrx_buffer_destroy, yrx_rules_deserialize, yrx_rules_serialize,
yrx_scanner_create, yrx_scanner_destroy, yrx_scanner_on_matching_rule,
yrx_scanner_scan, YRX_BUFFER, YRX_RULE,
yrx_scanner_scan, yrx_scanner_set_global_bool, YRX_BUFFER, YRX_RULE,
};
use std::ffi::{c_void, CString};

Expand Down Expand Up @@ -75,8 +75,16 @@ fn capi() {
);

yrx_scanner_scan(scanner, std::ptr::null(), 0);
yrx_scanner_destroy(scanner);

assert_eq!(matches, 1);

matches = 0;

// After changing the value of `some_bool` to false, the rule doesn't
// match anymore.
yrx_scanner_set_global_bool(scanner, some_bool.as_ptr(), false);
yrx_scanner_scan(scanner, std::ptr::null(), 0);
assert_eq!(matches, 0);

yrx_scanner_destroy(scanner);
}
}
41 changes: 41 additions & 0 deletions go/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package yara_x
import "C"
import (
"errors"
"fmt"
"math"
"runtime"
"runtime/cgo"
Expand Down Expand Up @@ -89,6 +90,46 @@ func (s *Scanner) Timeout(timeout time.Duration) {

var ErrTimeout = errors.New("timeout")

// SetGlobal sets the value of a global variable.
//
// The variable must has been previously defined by calling Compiler.DefineGlobal
// and the type it has during the definition must match the type of the new
// value.
//
// The variable will retain the new value in subsequent scans, unless this
// function is called again for setting a new value.
func (s *Scanner) SetGlobal(ident string, value interface{}) error {
cIdent := C.CString(ident)
defer C.free(unsafe.Pointer(cIdent))
var ret C.int

runtime.LockOSThread()
defer runtime.UnlockOSThread()

switch v := value.(type) {
case int:
ret = C.int(C.yrx_scanner_set_global_int(s.cScanner, cIdent, C.int64_t(v)))
case bool:
ret = C.int(C.yrx_scanner_set_global_bool(s.cScanner, cIdent, C.bool(v)))
case string:
cValue := C.CString(v)
defer C.free(unsafe.Pointer(cValue))
ret = C.int(C.yrx_scanner_set_global_str(s.cScanner, cIdent, cValue))
case float64:
ret = C.int(C.yrx_scanner_set_global_float(s.cScanner, cIdent, C.double(v)))
default:
return fmt.Errorf("variable `%s` has unsupported type: %T", ident, v)
}

runtime.KeepAlive(s)

if ret == C.VARIABLE_ERROR {
return errors.New(C.GoString(C.yrx_last_error()))
}

return nil
}

// SetModuleOutput sets the output data for a YARA module.
//
// Each YARA module generates an output consisting of a data structure that
Expand Down
14 changes: 14 additions & 0 deletions go/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ func TestScanner2(t *testing.T) {
runtime.GC()
}

func TestScanner3(t *testing.T) {
r, _ := Compile(
`rule t { condition: var_bool }`,
GlobalVars(map[string]interface{}{"var_bool": true}))

s := NewScanner(r)
matchingRules, _ := s.Scan([]byte{})
assert.Len(t, matchingRules, 1)

s.SetGlobal("var_bool", false)
matchingRules, _ = s.Scan([]byte{})
assert.Len(t, matchingRules, 0)
}

func TestScannerTimeout(t *testing.T) {
r, _ := Compile("rule t { strings: $a = /a(.*)*a/ condition: $a }")
s := NewScanner(r)
Expand Down

0 comments on commit caa1b46

Please sign in to comment.