Skip to content

Commit

Permalink
Allow iterating over compiled ruleset before scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
jpohls1 authored Nov 26, 2023
1 parent c752dd3 commit 6fd43c5
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 9 deletions.
26 changes: 24 additions & 2 deletions examples/tutorial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,39 @@ const RULES: &str = r#"
$rust
}
"#;
const RULES2: &str = r#"
rule contains_rust_too {
strings:
$more_rust = "rust" nocase
condition:
$more_rust
}
"#;

fn main() {
let compiler = Compiler::new().unwrap();
let compiler = compiler
.add_rules_str(RULES)
.expect("Should have parsed rule");
let rules = compiler
let compiler = compiler
.add_rules_str(RULES2)
.expect("Should have parsed rule");
let ruleset = compiler
.compile_rules()
.expect("Should have compiled rules");
let results = rules

let mut rules = ruleset.get_rules();
for (i, rule) in rules.iter_mut().enumerate() {
println!("{}: {}", i, rule.identifier);
if i % 2 == 1 {
rule.disable()
}
}

let results = ruleset
.scan_mem("I love Rust!".as_bytes(), 5)
.expect("Should have scanned");

assert_eq!(results.len(), 1);
assert!(results.iter().any(|r| r.identifier == "contains_rust"));
}
43 changes: 38 additions & 5 deletions src/internals/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::errors::*;
use crate::internals::meta::MetadataIterator;
use crate::internals::string::YrStringIterator;
use crate::{Metadata, Rule, YrString};
use crate::rules::RulesetRule;

pub fn rules_destroy(rules: *mut yara_sys::YR_RULES) {
unsafe {
Expand All @@ -33,6 +34,30 @@ pub fn scanner_destroy(scanner: *mut yara_sys::YR_SCANNER) {
}
}

pub fn get_rules<'a>(ruleset: *mut yara_sys::YR_RULES) -> Vec<RulesetRule<'a>> {
let num_rules = unsafe { yara_sys::get_num_rules(ruleset) };
let mut rules: Vec<*mut yara_sys::YR_RULE> = Vec::with_capacity(num_rules);

unsafe {
let n = yara_sys::get_rules(ruleset, rules.as_mut_ptr().cast(), num_rules);
rules.set_len(n);
};

let mut result: Vec<RulesetRule> = Vec::with_capacity(rules.len());
for rule in &rules {
let rule_data = Rule::from(unsafe { & **rule });
result.push(RulesetRule {
inner: *rule,
identifier: rule_data.identifier,
namespace: rule_data.namespace,
tags: rule_data.tags,
metadatas: rule_data.metadatas,
});
}

result
}

// TODO Check if non mut
pub fn rules_save(rules: *mut yara_sys::YR_RULES, filename: &str) -> Result<(), YaraError> {
let filename = CString::new(filename).unwrap();
Expand Down Expand Up @@ -88,8 +113,8 @@ where
})
}

impl<'a> From<(&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)> for Rule<'a> {
fn from((context, rule): (&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)) -> Self {
impl<'a> From<&'a yara_sys::YR_RULE> for Rule<'a> {
fn from(rule: &'a yara_sys::YR_RULE) -> Self {
let identifier = unsafe { CStr::from_ptr(rule.get_identifier()) }
.to_str()
.unwrap();
Expand All @@ -100,9 +125,7 @@ impl<'a> From<(&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)> for Rule<'
let tags = TagIterator::from(rule)
.map(|c| c.to_str().unwrap())
.collect();
let strings = YrStringIterator::from(rule)
.map(|s| YrString::from((context, s)))
.collect();
let strings: Vec<YrString> = Vec::new();

Rule {
identifier,
Expand All @@ -114,6 +137,16 @@ impl<'a> From<(&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)> for Rule<'
}
}

impl<'a> From<(&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)> for Rule<'a> {
fn from((context, rule): (&'a yara_sys::YR_SCAN_CONTEXT, &'a yara_sys::YR_RULE)) -> Self {
let mut result = Rule::from(rule);
result.strings = YrStringIterator::from(rule)
.map(|s| YrString::from((context, s)))
.collect();
result
}
}

struct TagIterator<'a> {
head: *const c_char,
_marker: marker::PhantomData<&'a c_char>,
Expand Down
32 changes: 32 additions & 0 deletions src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ impl Rules {
}

impl Rules {
pub fn get_rules(&self) -> Vec<RulesetRule> {
internals::get_rules(self.inner)
}

/// Create a [`Scanner`](crate::scanner::Scanner) from this set of rules.
///
/// You can create as many scanners as you want, and they each can have
Expand Down Expand Up @@ -316,6 +320,34 @@ impl Drop for Rules {
}
}

/// A rule contained in a ruleset.
pub struct RulesetRule<'r> {
pub(crate) inner: *mut yara_sys::YR_RULE,
/// Name of the rule.
pub identifier: &'r str,
/// Namespace of the rule.
pub namespace: &'r str,
/// Metadatas of the rule.
pub metadatas: Vec<Metadata<'r>>,
/// Tags of the rule.
pub tags: Vec<&'r str>,
}

impl<'r> RulesetRule<'r> {
pub fn enable(&mut self) {
unsafe {
(*self.inner).enable();
}
}
pub fn disable(&mut self) {
unsafe {
(*self.inner).disable();
}
}
}


/// A rule that matched during a scan.
#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions yara-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ edition = "2018"
[features]
default = ["bindgen", 'module-dotnet', 'module-dex', 'module-macho', 'module-hash', 'ndebug']
bundled-4_3_1 = []
vendored = ["cc", "glob", "fs_extra"]
vendored = ["glob", "fs_extra"]
module-cuckoo = []
module-magic = []
module-macho = []
Expand All @@ -29,7 +29,7 @@ yara-static = []

[build-dependencies]
bindgen = { version = "0.68", optional = true, default-features = false, features = [ "runtime" ] }
cc = { version = "1.0", optional = true }
cc = { version = "1.0", optional = false }
glob = { version = "0.3", optional = true }
fs_extra = { version = "1.2", optional = true }

Expand Down
12 changes: 12 additions & 0 deletions yara-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
fn main() {
build::build_and_link();
bindings::add_bindings();
println!("cargo:rerun-if-changed=src/get_rules.c");

let mut cc = cc::Build::new();
cc.file("src/get_rules.c");
if let Some(yara_include_dir) = get_target_env_var("YARA_INCLUDE_DIR").filter(|dir| !dir.is_empty()) {
cc.include(yara_include_dir);
}
if let Some(yara_library_path) = get_target_env_var("YARA_LIBRARY_PATH").filter(|path| !path.is_empty()) {
println!("cargo:rustc-link-search=native={}", yara_library_path);
}

cc.compile("get_rules");
}

pub fn cargo_rerun_if_env_changed(env_var: &str) {
Expand Down
24 changes: 24 additions & 0 deletions yara-sys/src/get_rules.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "yara.h"
#include "yara/rules.h"

size_t get_rules(YR_RULES *ruleset, YR_RULE *rules[], size_t n) {
YR_RULE* rule;
size_t i = 0;
yr_rules_foreach(ruleset, rule) {
if (i < n)
{
rules[i] = rule;
i++;
}
}
return i;
}

size_t get_num_rules(YR_RULES *ruleset) {
YR_RULE* rule;
size_t n = 0;
yr_rules_foreach(ruleset, rule) {
n++;
}
return n;
}
17 changes: 17 additions & 0 deletions yara-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ pub mod scan_flags {
};
}

extern "C" {
pub fn get_rules(ruleset: *mut YR_RULES, rules: *mut *mut YR_RULE, n: usize) -> usize;
pub fn get_num_rules(ruleset: *mut YR_RULES) -> usize;
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MetaType {
Integer,
Expand Down Expand Up @@ -90,6 +95,18 @@ impl YR_RULE {
pub fn get_ns(&self) -> *const YR_NAMESPACE {
unsafe { self.__bindgen_anon_5.ns }
}

pub fn enable(&mut self) {
unsafe {
yr_rule_enable(self);
}
}

pub fn disable(&mut self) {
unsafe {
yr_rule_disable(self);
}
}
}

impl YR_STRING {
Expand Down

0 comments on commit 6fd43c5

Please sign in to comment.