use crate::utils::{build_rule, check, check_boreal, check_err, Checker};
#[test]
fn test_variable() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = "X"
$b = "foo"
$c = /re+xv?/
$d = /^bav/
$e = { FF ( ?A | B? [1-3] ?? ) FF }
condition:
any of them
}"#,
);
checker.check(b"nothing", false);
checker.check(b"i Xm", true);
checker.check(b"barfool", true);
checker.check(b"greeex", true);
checker.check(b"bZv", false);
checker.check(b"bavaoze", true);
checker.check(b"abavaoze", false);
checker.check(b"a\xFF\xDC\xFFp", false);
checker.check(b"dbaz\xFF\xDA\xFFeaz", true);
checker.check(b"dbaz\xFF\xBFer\xFFeaz", true);
checker.check(b"dbaz\xFF\xBFerdf\xFFeaz", true);
}
#[test]
fn test_variable_err() {
check_err(
"rule a {
condition:
$a
}",
"mem:3:9: error: unknown variable $a",
);
check_err(
"rule a {
strings:
$a = /aaa/
condition:
true
}",
"mem:3:9: error: variable $a is unused",
);
}
#[test]
fn test_variable_regex_modifiers() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = /f[aF]T[d-g]\x76/ nocase
$b = /foo/ fullword
$c = /bar.{0,3}/ fullword nocase
$d = /.{,2}quu/ nocase fullword
condition:
any of them
}"#,
);
checker.check(b"faTgv", true);
checker.check(b"faTgx", false);
checker.check(b"FATGV", true);
checker.check(b"fftDV", true);
checker.check(b"fftEV", true);
checker.check(b"fftE", false);
checker.check(b"ftEV", false);
checker.check(b"foo", true);
checker.check(b" foo ", true);
checker.check(b"-foo_", true);
checker.check(b"-fooa", false);
checker.check(b"-fooA", false);
checker.check(b"-foo0", false);
checker.check(b"afoo:", false);
checker.check(b"Zfoo:", false);
checker.check(b"0foo:", false);
checker.check(b"bar-", true);
checker.check(b"bara-", true);
checker.check(b"baraa-", true);
checker.check(b"baraaa-", true);
checker.check(b"baraaaa-", false);
checker.check(b"abaraaa-", false);
checker.check(b"|baraaa-", true);
checker.check(b"|bar", true);
checker.check(b"quu", true);
checker.check(b"QUU", true);
checker.check(b"quux", false);
checker.check(b"aQuu", true);
checker.check(b"aqUU", true);
checker.check(b"aaqUu", true);
checker.check(b"aAaQUu", false);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /.{0,2}yay.{0,2}/ fullword
condition:
$a
}"#,
);
checker.check(b"yay", true);
checker.check(b"| yay |a", false);
checker.check(b"a| yay |", true);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /.{0,2}yay.{0,2}/
condition:
#a == 3
}"#,
);
checker.check(b"a| yay |a", true);
}
fn build_checker(regex: &str, modifiers: &str) -> Checker {
Checker::new(&format!(
r#"
rule a {{
strings:
$a = /{}/ {}
condition:
$a
}}"#,
regex, modifiers
))
}
fn to_wide(e: &[u8]) -> Vec<u8> {
let mut ret = Vec::new();
for c in e {
ret.push(*c);
ret.push(b'\0');
}
ret
}
#[test]
fn test_variable_regex_wide() {
let checker = build_checker("abc", "wide");
checker.check(b"abc", false);
checker.check(b"a\0b\0c\0", true);
checker.check(b"a\0b\0c", false);
checker.check(b"ab\0c\0", false);
checker.check(b"\0a\0b\0c\0", true);
checker.check(b"\0a\0b\0c", false);
let checker = build_checker("a+b|cd{2,}", "wide");
checker.check(b"ab", false);
checker.check(b"aaab", false);
checker.check(b"abcd", false);
checker.check(b"cdd", false);
checker.check(b"a\0b\0", true);
checker.check(b"aa\0b\0", true);
checker.check(b"a\0a\0b\0", true);
checker.check(b"a\0a\0a\0a\0b\0", true);
checker.check(b"c\0d\0", false);
checker.check(b"c\0d\0d", false);
checker.check(b"c\0d\0d\0", true);
checker.check(b"c\0d\0d\0d\0d\0", true);
let checker = build_checker("<[a-z][0-9]*>", "wide");
checker.check(b"<a>", false);
checker.check(b"<\x00a\x00>\x00", true);
checker.check(b"<\x00a>\x00", false);
checker.check(b"<\x00\x00\x00>\x00", false);
checker.check(b"<\x00\x00>\x00", false);
checker.check(b"<b22>", false);
checker.check(b"<\x00b\x0022\x00>\x00", false);
checker.check(b"<\x00b\x002\x002\x00>\x00", true);
checker.check(b"<\x00a\x009\x003\x00>\x00", true);
checker.check(b"<\x00a\x009\x00d\x00>\x00", false);
checker.check(b"a\x009\x00", false);
let checker = build_checker(r#"\d[^abc]d$"#, "wide");
checker.check(b"13d", false);
checker.check(b"1\x003\x00d\x00", true);
checker.check(b"1\x003\x00d", false);
checker.check(b"1\x003\x00\x00", false);
checker.check(b"a\x00d\x00d\x00", false);
checker.check(b"1\x00a\x00d\x00", false);
checker.check(b"1\x00d\x00e\x00", false);
checker.check(b"1\x00d\x00d\x00", true);
let checker = build_checker(r"a(b|c+)[def][^g]", "wide ascii");
checker.check(b"abdf", true);
checker.check(b"a\0b\0d\0f\0", true);
checker.check(b"a\0b\0d\0f", false);
checker.check(b"abeg", false);
checker.check(b"a\0b\0e\0g\0", false);
checker.check(b"acccf\0", true);
checker.check(b"a\0c\0c\0c\0f\0\0\0", true);
checker.check(b"a\0c\0c\0c\0f\0\0", false);
}
#[test]
fn test_variable_regex_wide_word_boundaries() {
let checker = build_checker(r"\b", "wide");
checker.check(b"", false);
checker.check(b"\0", false);
checker.check_boreal(b"a\0", true);
checker.check_libyara(b"a\0", false);
checker.check(b"\0a", false);
let checker = build_checker(r"\B", "wide");
checker.check(b"", false);
checker.check_boreal(b"\0", true);
checker.check_libyara(b"\0", false);
checker.check_boreal(b"a\0", true);
checker.check_libyara(b"a\0", false);
checker.check_boreal(b"\0a", true);
checker.check_libyara(b"\0a", false);
let checker = build_checker(r"\ba", "wide");
checker.check(b"", false);
checker.check(b"a", false);
checker.check(b"a\0", true);
checker.check(b"a\0b", true);
checker.check(b"a\0b\0", true);
checker.check(b"a\0>\0", true);
checker.check(b"ba\0", true);
checker.check(b"\0a\0", true);
checker.check(b"b\0a\0", false);
checker.check(b"[\0a\0", true);
checker.check(b"b\ra\0", true);
let checker = build_checker(r"\Ba", "wide");
checker.check(b"", false);
checker.check(b"a", false);
checker.check(b"a\0", false);
checker.check(b"a\0b", false);
checker.check(b"a\0b\0", false);
checker.check(b"a\0>\0", false);
checker.check(b"ba\0", false);
checker.check(b"\0a\0", false);
checker.check(b"b\0a\0", true);
checker.check(b"[\0a\0", false);
checker.check(b"b\ra\0", false);
let checker = build_checker(r"a\b", "wide");
checker.check(b"", false);
checker.check(b"a", false);
checker.check(b"a\0", true);
checker.check(b"a\0b", true);
checker.check(b"a\0b\0", false);
checker.check(b"a\0>\0", true);
checker.check(b"ba\0", true);
checker.check(b"\0a\0", true);
checker.check(b"b\0a\0", true);
checker.check(b"[\0a\0", true);
checker.check(b"b\ra\0", true);
let checker = build_checker(r"a\B", "wide");
checker.check(b"", false);
checker.check(b"a", false);
checker.check(b"a\0", false);
checker.check(b"a\0b", false);
checker.check(b"a\0b\0", true);
checker.check(b"a\0>\0", false);
checker.check(b"ba\0", false);
checker.check(b"\0a\0", false);
checker.check(b"b\0a\0", false);
checker.check(b"[\0a\0", false);
checker.check(b"b\ra\0", false);
let checker = build_checker(r"<.+\bA\b.+>", "wide");
checker.check(&to_wide(b""), false);
checker.check(&to_wide(b"<>"), false);
checker.check(&to_wide(b"<A>"), false);
checker.check(&to_wide(b"<[A]>"), true);
checker.check(&to_wide(b"<[aAa]>"), false);
checker.check(&to_wide(b"<a[A]a>"), true);
checker.check(&to_wide(b"<aaA]a>"), false);
checker.check(&to_wide(b"<aAAAAa>"), false);
checker.check(&to_wide(b"<a[AA]>"), false);
checker.check(&to_wide(b"<a[AAA.A!AA]>"), true);
checker.check(&to_wide(b"<a[A.AAA]>"), true);
checker.check(&to_wide(b"<a[AA.AAA]>"), false);
checker.check(&to_wide(b"<a[AA>A.>"), true);
checker.check(&to_wide(b"<a[AA>AA.>"), false);
let checker = build_checker(r"<.+\BA\B.+>", "wide");
checker.check(&to_wide(b""), false);
checker.check(&to_wide(b"<>"), false);
checker.check(&to_wide(b"<A>"), false);
checker.check(&to_wide(b"<[A]>"), false);
checker.check(&to_wide(b"<[aAa]>"), true);
checker.check(&to_wide(b"<a[A]a>"), false);
checker.check(&to_wide(b"<a[Aa>"), false);
checker.check(&to_wide(b"<aAAAAa>"), true);
checker.check(&to_wide(b"<a[AA]>"), false);
checker.check(&to_wide(b"<a[AAA.A!AA]>"), true);
checker.check(&to_wide(b"<a[A.AAA]>"), true);
checker.check(&to_wide(b"<a[AA.AAA]>"), true);
checker.check(&to_wide(b"<a[AA>A.>"), false);
checker.check(&to_wide(b"<a[AA>AA.>"), false);
let checker = build_checker(r"<\w+\b.a>", "wide");
checker.check(&to_wide(b"<ave|a>"), true);
checker.check(&to_wide(b"<aveva>"), false);
checker.check(&to_wide("<avéva>".as_bytes()), false);
checker.check(&to_wide("<avé|a>".as_bytes()), false);
let checker = build_checker(r"<(c\b.a){2}>", "wide");
checker.check(&to_wide(b"<c|ac.a>"), true);
checker.check(&to_wide(b"<cbac.a>"), false);
checker.check(&to_wide(b"<c|acba>"), false);
checker.check(&to_wide(b"<cbacba>"), false);
}
#[test]
fn test_variable_regex_word_boundaries_edge_cases() {
let build_checker = |regex: &str, modifiers: &str| {
Checker::new(&format!(
r#"
rule a {{
strings:
$a = /{}/ {}
$z = "z"
condition:
(#z == 0 and #a == 0) or (!a == #z)
}}"#,
regex, modifiers,
))
};
let checker = build_checker(r"a.{0,4}\b", "");
checker.check(b"z a", true);
checker.check(b"zz a1", true);
checker.check(b"zzz a12", true);
checker.check(b"zzzzz a1234", true);
checker.check(b"a12345", true);
checker.check(b"zzzzz a1234>", true);
checker.check(b"zzzz a12>34", true);
checker.check(b"zzzz a>>>34", true);
let checker = build_checker(r"a.{0,4}\b", "wide");
checker.check(&to_wide(b"zz a"), true);
checker.check(&to_wide(b"zzzz a1"), true);
checker.check(&to_wide(b"zzzzzz a12"), true);
checker.check(&to_wide(b"zzzzzzzzzz a1234"), true);
checker.check(&to_wide(b"a12345"), true);
checker.check(&to_wide(b"zzzzzzzzzz a1234>"), true);
checker.check(&to_wide(b"zzzzzzzz a12>34"), true);
checker.check(&to_wide(b"zzzzzzzz a>>>34"), true);
let checker = build_checker(r"a.{0,4}?\b", "");
checker.check(b"z a", true);
checker.check(b"zz a1", true);
checker.check(b"zzz a12", true);
checker.check(b"zzzzz a1234", true);
checker.check(b"a12345", true);
checker.check(b"zzzzz a1234>", true);
checker.check(b"zzz a12>34", true);
checker.check(b"z a>>>34", true);
let checker = build_checker(r"a.{0,4}?\b", "wide");
checker.check(&to_wide(b"zz a"), true);
checker.check(&to_wide(b"zzzz a1"), true);
checker.check(&to_wide(b"zzzzzz a12"), true);
checker.check(&to_wide(b"zzzzzzzzzz a1234"), true);
checker.check(&to_wide(b"a12345"), true);
checker.check(&to_wide(b"zzzzzzzzzz a1234>"), true);
checker.check(&to_wide(b"zzzzzz a12>34"), true);
checker.check(&to_wide(b"zz a>>>34"), true);
}
#[test]
fn test_variable_string_modifiers() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = "c\to\x76" nocase
$b = "foo" fullword
$c = "bar" fullword nocase
condition:
any of them
}"#,
);
checker.check(b"c\tov", true);
checker.check(b"C\tOV", true);
checker.check(b"C\tOx", false);
checker.check(b"C\tov", true);
checker.check(b"foo", true);
checker.check(b" foo ", true);
checker.check(b"-foo_", true);
checker.check(b"-fooa", false);
checker.check(b"-fooA", false);
checker.check(b"-foo0", false);
checker.check(b"afoo:", false);
checker.check(b"Zfoo:", false);
checker.check(b"0foo:", false);
checker.check(b"bar", true);
checker.check(b" BAR ", true);
checker.check(b"-baR_", true);
checker.check(b"-baRa", false);
checker.check(b"-barA", false);
checker.check(b"-bAr0", false);
checker.check(b"aBAr:", false);
checker.check(b"Zbar:", false);
checker.check(b"0bAR:", false);
let checker = Checker::new(
r#"
rule a {
strings:
$a = "margit" wide
$b = "morgott" ascii wide
$c = "mohg" ascii
$d = "maliketh" wide fullword
$e = "malenia" wide ascii fullword
condition:
any of them
}"#,
);
checker.check(b"amargita", false);
checker.check(b"a\0m\0a\0r\0g\0i\0t\0a\0", true);
checker.check(b"\0m\0a\0r\0g\0i\0t\0a\0", true);
checker.check(b"m\0a\0r\0g\0i\0t\0a\0", true);
checker.check(b"m\0a\0r\0g\0i\0ta\0", false);
checker.check(b"morgott", true);
checker.check(b"amorgotta", true);
checker.check(b"a\0m\0o\0r\0g\0o\0t\0t\0a\0", true);
checker.check(b"\0m\0o\0r\0g\0o\0t\0t\0a\0", true);
checker.check(b"m\0o\0r\0g\0o\0t\0t\0a\0", true);
checker.check(b"m\0o\0r\0g\0o\0t\0ta\0", false);
checker.check(b"amohgus", true);
checker.check(b"a\0m\0o\0g\0h\0u\0s\0", false);
checker.check(b"<<<maliketh>>>", false);
checker.check(b"<\0<\0<\0m\0a\0l\0i\0k\0e\0t\0h\0>\0>\0>\0", true);
checker.check(b"<\0<\0a\0m\0a\0l\0i\0k\0e\0t\0h\0>\0>\0>\0", false);
checker.check(b"a\0m\0a\0l\0i\0k\0e\0t\0h\0b\0", false);
checker.check(b"a\0m\0a\0l\0i\0k\0e\0t\0h\0>\0", false);
checker.check(b"<\0m\0a\0l\0i\0k\0e\0t\0h\0b\0", false);
checker.check(b"<\0m\0a\0l\0i\0k\0e\0t\0h\0>\0", true);
checker.check(b"\0m\0a\0l\0i\0k\0e\0t\0h\0>\0", true);
checker.check(b"<\0m\0a\0l\0i\0k\0e\0t\0h\0>", true);
checker.check(b"<\0m\0a\0l\0i\0k\0e\0t\0h\0", true);
checker.check(b"\0m\0a\0l\0i\0k\0e\0t\0h\0", true);
checker.check(b"m\0a\0l\0i\0k\0e\0t\0h\0", true);
checker.check(b"<\0maliketh\0>\0", false);
checker.check(b"<malenia>", true);
checker.check(b"<\0m\0a\0l\0e\0n\0i\0a\0>\0", true);
checker.check(b"amalenia>", false);
checker.check(b"<maleniab", false);
checker.check(b"malenia", true);
checker.check(b"a\0m\0a\0l\0e\0n\0i\0a\0>\0", false);
checker.check(b"<\0m\0a\0l\0e\0n\0i\0a\0b\0", false);
checker.check(b"am\0a\0l\0e\0n\0i\0a\0b\0", false);
checker.check(b"m\0a\0l\0e\0n\0i\0a\0b\0", false);
checker.check(b"am\0a\0l\0e\0n\0i\0a\0b", true);
checker.check(b"a\0malenia<\0", true);
let rule = r#"
rule a {
strings:
$a = "rykard" xor
$b = "rennala" xor(20-30)
$c = "radagon" wide xor(10)
$d = "radahn" wide ascii xor
condition:
any of them
}"#;
let checker = Checker::new(rule);
let check_xor = |mem: &[u8], xor_byte: u8, expected_res: bool| {
let mut out = Vec::new();
out.extend(b"abc");
out.extend(mem.iter().map(|c| c ^ xor_byte));
out.extend(b"xyz");
checker.check(&out, expected_res);
checker.check(&out[1..], expected_res);
};
let rykard = b"rykard";
let rybard = b"rybard";
for x in 0..=255 {
check_xor(rykard, x, true);
check_xor(rybard, x, false);
}
let rennala = b"rennala";
let wide_rennala = b"r\0e\0n\0n\0a\0l\0a\0";
for x in 0..=255 {
check_xor(rennala, x, (20..=30).contains(&x));
check_xor(wide_rennala, x, false);
}
let radagon = b"radagon";
let wide_radagon = b"r\0a\0d\0a\0g\0o\0n\0";
for x in 0..=255 {
check_xor(radagon, x, false);
check_xor(wide_radagon, x, x == 10);
}
let radahn = b"radahn";
let wide_radahn = b"r\0a\0d\0a\0h\0n\0";
for x in 0..=255 {
check_xor(radahn, x, true);
check_xor(wide_radahn, x, true);
}
}
#[test]
fn test_variable_base64_small() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = "a" base64
condition:
$a
}"#,
);
checker.check(b"", false);
checker.check(base64::encode("`").as_bytes(), true);
checker.check(base64::encode("b").as_bytes(), true);
checker.check(base64::encode("c").as_bytes(), true);
checker.check(base64::encode("!").as_bytes(), false);
checker.check(base64::encode(b"\xA1").as_bytes(), false);
checker.check(base64::encode(b"\xE1").as_bytes(), false);
checker.check(base64::encode(b"\xA0").as_bytes(), false);
checker.check(base64::encode(b"ha").as_bytes(), false);
}
#[test]
fn test_variable_base64() {
let checker = Checker::new(
r#"
rule a {
strings:
$a0 = "Hello world" base64
$a1 = "mangue" wide base64
$a2 = "fraise" ascii wide base64
condition:
any of them
}"#,
);
checker.check(b"aaaSGVsbG8gd29ybGbbb", true);
checker.check(b"aaahlbGxvIHdvcmxkbbb", true);
checker.check(b"aaaIZWxsbyB3b3JsZbbb", true);
checker.check(b"SGVsbG8gd29yb", false);
checker.check(b"hlbGxvIHdvcmx", false);
checker.check(b"IZWxsbyB3b3Js", false);
checker.check(b"GVsbG8gd29ybG", false);
checker.check(b"lbGxvIHdvcmxk", false);
checker.check(b"ZWxsbyB3b3JsZ", false);
checker.check(b"aaaSGVsbG9gd29ybGbbb", false);
checker.check(b"aaahlbGxvVHdvcmxkbbb", false);
checker.check(b"aaaIZWxsbyB4b3JsZbbb", false);
checker.check(b"aaahlbGxvIHdvcmxkbbb", true);
checker.check(b"a\0h\0l\0b\0G\0x\0v\0I\0H\0d\0v\0c\0m\0x\0k\0b\0", false);
checker.check(b"mangue", false);
checker.check(base64::encode("mangue").as_bytes(), false);
checker.check(base64::encode(" mangue").as_bytes(), false);
checker.check(base64::encode(" mangue").as_bytes(), false);
checker.check(b"b\0W\0F\0u\0Z\x003\0V\0l\0", false);
checker.check(b"1\0h\0b\0m\0d\x001\0Z\0", false);
checker.check(b"t\0Y\0W\x005\0n\0d\0W\0", false);
checker.check(base64::encode("m\0a\0n\0g\0u\0e\0").as_bytes(), true);
checker.check(base64::encode(" m\0a\0n\0g\0u\0e\0").as_bytes(), true);
checker.check(base64::encode(" m\0a\0n\0g\0u\0e\0").as_bytes(), true);
checker.check(b"fraise", false);
checker.check(base64::encode("fraise").as_bytes(), true);
checker.check(base64::encode(" fraise").as_bytes(), true);
checker.check(base64::encode(" fraise").as_bytes(), true);
checker.check(b"Z\0n\0J\0h\0a\0X\0N\0l\0", false);
checker.check(b"Z\0y\0Y\0W\0l\0z\0Z\0", false);
checker.check(b"m\0c\0m\0F\0p\0c\x002\0", false);
checker.check(base64::encode("f\0r\0a\0i\0s\0e\0").as_bytes(), true);
checker.check(base64::encode(" f\0r\0a\0i\0s\0e\0").as_bytes(), true);
checker.check(base64::encode(" f\0r\0a\0i\0s\0e\0").as_bytes(), true);
}
#[test]
fn test_variable_base64wide() {
let checker = Checker::new(
r#"
rule a {
strings:
$a0 = "framboise" base64wide
$a1 = "mures" wide base64wide
$a2 = "myrtille" wide ascii base64wide
condition:
any of them
}"#,
);
checker.check(b"framboise", false);
checker.check(base64::encode("framboise").as_bytes(), false);
checker.check(base64::encode(" framboise").as_bytes(), false);
checker.check(base64::encode(" framboise").as_bytes(), false);
checker.check(b"Z\0n\0J\0h\0b\0W\0J\0v\0a\0X\0N\0l\0", true);
checker.check(b"Z\0y\0Y\0W\x001\0i\0b\x002\0l\0z\0Z\0", true);
checker.check(b"m\0c\0m\0F\0t\0Y\0m\09\0p\0c\x002\0", true);
checker.check(
base64::encode("f\0r\0a\0m\0b\0o\0i\0s\0e\0").as_bytes(),
false,
);
checker.check(
base64::encode(" f\0r\0a\0m\0b\0o\0i\0s\0e\0").as_bytes(),
false,
);
checker.check(
base64::encode(" f\0r\0a\0m\0b\0o\0i\0s\0e\0").as_bytes(),
false,
);
checker.check(b"mures", false);
checker.check(base64::encode("mures").as_bytes(), false);
checker.check(base64::encode(" mures").as_bytes(), false);
checker.check(base64::encode(" mures").as_bytes(), false);
checker.check(base64::encode("b\0X\0V\0y\0Z\0X\0").as_bytes(), false);
checker.check(base64::encode("1\x01\0c\0m\0V\0z\0").as_bytes(), false);
checker.check(base64::encode("t\0d\0X\0J\0l\0c\0").as_bytes(), false);
checker.check(base64::encode("m\0u\0r\0e\0s\0").as_bytes(), false);
checker.check(base64::encode(" m\0u\0r\0e\0s\0").as_bytes(), false);
checker.check(base64::encode(" m\0u\0r\0e\0s\0").as_bytes(), false);
checker.check(b"b\0Q\0B\x001\0A\0H\0I\0A\0Z\0Q\0B\0z\0A\0", true);
checker.check(b"0\0A\0d\0Q\0B\0y\0A\0G\0U\0A\0c\0w\0", true);
checker.check(b"t\0A\0H\0U\0A\0c\0g\0B\0l\0A\0H\0M\0A\0", true);
checker.check(b"myrtille", false);
checker.check(base64::encode("myrtille").as_bytes(), false);
checker.check(base64::encode(" myrtille").as_bytes(), false);
checker.check(base64::encode(" myrtille").as_bytes(), false);
checker.check(b"b\0X\0l\0y\0d\0G\0l\0s\0b\0G\0", true);
checker.check(b"1\x005\0c\0n\0R\0p\0b\0G\0x\0l\0", true);
checker.check(b"t\0e\0X\0J\x000\0a\0W\0x\0s\0Z\0", true);
checker.check(base64::encode("m\0y\0r\0t\0i\0l\0l\0e\0").as_bytes(), false);
checker.check(
base64::encode(" m\0y\0r\0t\0i\0l\0l\0e\0").as_bytes(),
false,
);
checker.check(
base64::encode(" m\0y\0r\0t\0i\0l\0l\0e\0").as_bytes(),
false,
);
checker.check(
b"b\0Q\0B\x005\0A\0H\0I\0A\0d\0A\0B\0p\0A\0G\0w\0A\0b\0A\0B\0l\0A\0",
true,
);
checker.check(
b"0\0A\0e\0Q\0B\0y\0A\0H\0Q\0A\0a\0Q\0B\0s\0A\0G\0w\0A\0Z\0Q\0",
true,
);
checker.check(
b"t\0A\0H\0k\0A\0c\0g\0B\x000\0A\0G\0k\0A\0b\0A\0B\0s\0A\0G\0U\0A\0",
true,
);
}
#[test]
fn test_variable_base64_base64wide() {
let checker = Checker::new(
r#"
rule a {
strings:
$a0 = "boreal forest" ascii base64 base64wide
$a1 = "noix de coco" wide base64 base64wide
$a2 = "comcombre" ascii wide base64 base64wide
condition:
any of them
}"#,
);
checker.check(b"boreal forest", false);
checker.check(base64::encode("boreal forest").as_bytes(), true);
checker.check(base64::encode(" boreal forest").as_bytes(), true);
checker.check(base64::encode(" boreal forest").as_bytes(), true);
checker.check(b"Y\0m\09\0y\0Z\0W\0F\0s\0I\0G\0Z\0v\0c\0m\0V\0z\0d\0", true);
checker.check(
b"J\0v\0c\0m\0V\0h\0b\0C\0B\0m\0b\x003\0J\0l\0c\x003\0",
true,
);
checker.check(
b"i\0b\x003\0J\0l\0Y\0W\0w\0g\0Z\0m\09\0y\0Z\0X\0N\x000\0",
true,
);
checker.check(
base64::encode("b\0o\0r\0e\0a\0l\0 \0f\0o\0r\0e\0s\0t\0").as_bytes(),
false,
);
checker.check(
base64::encode(" b\0o\0r\0e\0a\0l\0 \0f\0o\0r\0e\0s\0t\0").as_bytes(),
false,
);
checker.check(
base64::encode(" b\0o\0r\0e\0a\0l\0 \0f\0o\0r\0e\0s\0t\0").as_bytes(),
false,
);
checker.check(
b"Y\0g\0B\0v\0A\0H\0I\0A\0Z\0Q\0B\0h\0A\0G\0w\0A\0I\0A\0B\0m\0A\0G\08\0A\0c\0g\0B\0l\0A\0H\0M\0A\0d\0A\0",
false
);
checker.check(
b"I\0A\0b\0w\0B\0y\0A\0G\0U\0A\0Y\0Q\0B\0s\0A\0C\0A\0A\0Z\0g\0B\0v\0A\0H\0I\0A\0Z\0Q\0B\0z\0A\0H\0Q\0A\0",
false,
);
checker.check(
b"i\0A\0G\08\0A\0c\0g\0B\0l\0A\0G\0E\0A\0b\0A\0A\0g\0A\0G\0Y\0A\0b\0w\0B\0y\0A\0G\0U\0A\0c\0w\0B\x000\0A\0",
false,
);
checker.check(b"noix de coco", false);
checker.check(base64::encode("noix de coco").as_bytes(), false);
checker.check(base64::encode(" noix de coco").as_bytes(), false);
checker.check(base64::encode(" noix de coco").as_bytes(), false);
checker.check(b"b\0m\09\0p\0e\0C\0B\0k\0Z\0S\0B\0j\0b\x002\0N\0v\0", false);
checker.check(b"5\0v\0a\0X\0g\0g\0Z\0G\0U\0g\0Y\x002\09\0j\0b\0", false);
checker.check(
b"u\0b\x002\0l\x004\0I\0G\0R\0l\0I\0G\0N\0v\0Y\x002\0",
false,
);
checker.check(
base64::encode("n\0o\0i\0x\0 \0d\0e\0 \0c\0o\0c\0o\0").as_bytes(),
true,
);
checker.check(
base64::encode(" n\0o\0i\0x\0 \0d\0e\0 \0c\0o\0c\0o\0").as_bytes(),
true,
);
checker.check(
base64::encode(" n\0o\0i\0x\0 \0d\0e\0 \0c\0o\0c\0o\0").as_bytes(),
true,
);
checker.check(
b"b\0g\0B\0v\0A\0G\0k\0A\0e\0A\0A\0g\0A\0G\0Q\0A\0Z\0Q\0A\0g\0A\0G\0M\0A\0b\0w\0B\0j\0A\0G\08\0A\0",
true
);
checker.check(
b"4\0A\0b\0w\0B\0p\0A\0H\0g\0A\0I\0A\0B\0k\0A\0G\0U\0A\0I\0A\0B\0j\0A\0G\08\0A\0Y\0w\0B\0v\0A\0",
true,
);
checker.check(
b"u\0A\0G\08\0A\0a\0Q\0B\x004\0A\0C\0A\0A\0Z\0A\0B\0l\0A\0C\0A\0A\0Y\0w\0B\0v\0A\0G\0M\0A\0b\0w\0",
true,
);
checker.check(b"comcombre", false);
checker.check(base64::encode("comcombre").as_bytes(), true);
checker.check(base64::encode(" comcombre").as_bytes(), true);
checker.check(base64::encode(" comcombre").as_bytes(), true);
checker.check(b"Y\x002\09\0t\0Y\x002\09\0t\0Y\0n\0J\0l\0", true);
checker.check(b"N\0v\0b\0W\0N\0v\0b\0W\0J\0y\0Z\0", true);
checker.check(b"j\0b\x002\x001\0j\0b\x002\x001\0i\0c\0m\0", true);
checker.check(
base64::encode("c\0o\0m\0c\0o\0m\0b\0r\0e\0").as_bytes(),
true,
);
checker.check(
base64::encode(" c\0o\0m\0c\0o\0m\0b\0r\0e\0").as_bytes(),
true,
);
checker.check(
base64::encode(" c\0o\0m\0c\0o\0m\0b\0r\0e\0").as_bytes(),
true,
);
checker.check(
b"Y\0w\0B\0v\0A\0G\x000\0A\0Y\0w\0B\0v\0A\0G\x000\0A\0Y\0g\0B\0y\0A\0G\0U\0A\0",
true,
);
checker.check(
b"M\0A\0b\0w\0B\0t\0A\0G\0M\0A\0b\0w\0B\0t\0A\0G\0I\0A\0c\0g\0B\0l\0A\0",
true,
);
checker.check(
b"j\0A\0G\08\0A\0b\0Q\0B\0j\0A\0G\08\0A\0b\0Q\0B\0i\0A\0H\0I\0A\0Z\0Q\0",
true,
);
}
#[test]
fn test_variable_find() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = "45"
condition:
$a
}"#,
);
checker.check(b"12345678", true);
checker.check(b"45678", true);
checker.check(b"45", true);
checker.check(b"345", true);
checker.check(b"1234678", false);
checker.check(b"465", false);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /4.5+/
condition:
$a
}"#,
);
checker.check(b"445", true);
checker.check(b"34\x3D555", true);
checker.check(b"123", false);
checker.check(b"44", false);
checker.check(b"4\n5", false);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /fo{2,}/i
condition:
$a
}"#,
);
checker.check(b"foo", true);
checker.check(b"FoOoOoO", true);
checker.check(b"barFOOObaz", true);
checker.check(b"fo", false);
checker.check(b"FO", false);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /a.*b/s
condition:
$a
}"#,
);
checker.check(b"ab", true);
checker.check(b"ba\n\n ba", true);
checker.check(b"AB", false);
checker.check(b"ec", false);
}
#[test]
fn test_variable_find_at() {
fn check_at(mem: &[u8], at: u64, res: bool) {
let rule = format!(
r#"
rule a {{
strings:
$a = "34"
$b = /[a-z]{{2}}/
condition:
for any of them: ($ at {})
}}"#,
at
);
check(&rule, mem, res);
}
check_at(b"01234567", 3, true);
check_at(b"342342", 3, true);
check_at(b"34", 0, true);
check_at(b"234", 2, false);
check_at(b"234", 0, false);
check_at(b"01234", 15, false);
check_at(b"abc", 0, true);
check_at(b"abc", 1, true);
check_at(b"abc", 2, false);
}
#[test]
fn test_variable_find_in() {
fn check_in(mem: &[u8], from: u64, to: u64, res: bool) {
let rule = format!(
r#"
rule a {{
strings:
$a = "345"
condition:
$a in ({}..{})
}}"#,
from, to
);
check(&rule, mem, res);
}
check_in(b"01234567", 0, 20, true);
check_in(b"01234567", 2, 6, true);
check_in(b"01234567", 3, 5, true);
check_in(b"01234567", 3, 4, true);
check_in(b"01234567", 3, 3, true);
check_in(b"01234567", 2, 3, true);
check_in(b"01234567", 1, 2, false);
check_in(b"34353435", 1, 6, false);
}
#[test]
fn test_variable_find_at_invalid() {
check(&build_rule("defined ($a0 at (#a0-10))"), b"", true);
check(&build_rule("$a0 at (#a0-10)"), b"", false);
check_boreal(
&build_rule("defined ($a0 at tests.integer_array[5])"),
b"",
false,
);
check(&build_rule("$a0 at tests.integer_array[5]"), b"", false);
}
#[test]
fn test_variable_find_in_invalid() {
check(&build_rule("defined ($a0 in (0..(#a0-1)))"), b"", true);
check(&build_rule("defined ($a0 in ((#a0-1)..0))"), b"", true);
check(&build_rule("$a0 in (0..(#a0-1))"), b"", false);
check(&build_rule("$a0 in ((#a0-1)..0)"), b"", false);
check(
&build_rule("defined ($a0 in (0..tests.integer_array[5]))"),
b"",
false,
);
check(
&build_rule("defined ($a0 in ((tests.integer_array[5])..3))"),
b"",
false,
);
}
#[test]
fn test_variable_hex_string_masks() {
let checker = Checker::new(
r#"
rule a {
strings:
$a = { ?C }
$b = { C? }
condition:
any of them
}
"#,
);
checker.check_full_matches(
&(0_u8..=255_u8).collect::<Vec<_>>(),
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"\x0c", 12, 1),
(b"\x1c", 28, 1),
(b"\x2c", 44, 1),
(b"\x3c", 60, 1),
(b"\x4c", 76, 1),
(b"\x5c", 92, 1),
(b"\x6c", 108, 1),
(b"\x7c", 124, 1),
(b"\x8c", 140, 1),
(b"\x9c", 156, 1),
(b"\xac", 172, 1),
(b"\xbc", 188, 1),
(b"\xcc", 204, 1),
(b"\xdc", 220, 1),
(b"\xec", 236, 1),
(b"\xfc", 252, 1),
],
),
(
"b",
vec![
(b"\xc0", 192, 1),
(b"\xc1", 193, 1),
(b"\xc2", 194, 1),
(b"\xc3", 195, 1),
(b"\xc4", 196, 1),
(b"\xc5", 197, 1),
(b"\xc6", 198, 1),
(b"\xc7", 199, 1),
(b"\xc8", 200, 1),
(b"\xc9", 201, 1),
(b"\xca", 202, 1),
(b"\xcb", 203, 1),
(b"\xcc", 204, 1),
(b"\xcd", 205, 1),
(b"\xce", 206, 1),
(b"\xcf", 207, 1),
],
),
],
)],
);
let checker = Checker::new(
r#"
rule a {
strings:
$a = { AB ?C DE }
$b = { AB C? DE }
$c = { AB ?? }
$d = { ?? DE }
condition:
any of them
}
"#,
);
checker.check_full_matches(b"", vec![]);
checker.check_full_matches(
b"\xab\xde \xab\xcc\xde \xab \xde \xde \xde\xde \xab\xab\xcf\xde\xde\xde\xab",
vec![(
"default:a".to_owned(),
vec![
("a", vec![(b"\xab\xcc\xde", 3, 3)]),
("b", vec![(b"\xab\xcc\xde", 3, 3), (b"\xab\xcf\xde", 17, 3)]),
(
"c",
vec![
(b"\xab\xde", 0, 2),
(b"\xab\xcc", 3, 2),
(b"\xab ", 7, 2),
(b"\xab\xab", 16, 2),
(b"\xab\xcf", 17, 2),
],
),
(
"d",
vec![
(b"\xab\xde", 0, 2),
(b"\xcc\xde", 4, 2),
(b" \xde", 8, 2),
(b" \xde", 10, 2),
(b" \xde", 12, 2),
(b"\xde\xde", 13, 2),
(b"\xcf\xde", 18, 2),
(b"\xde\xde", 19, 2),
(b"\xde\xde", 20, 2),
],
),
],
)],
);
}
#[test]
fn test_variable_hex_string_jumps() {
let checker = Checker::new(
r#"
rule a {
strings:
// 61 is 'a', 62 is 'b'
$a = { 61 [1-2] 62 }
$b = { 61 [1-] 62 }
$c = { 61 [0-1] 62 }
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"b ab aab abbb aaaab aaaaabb ababaabb abbaaabab",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"aab", 5, 3),
(b"abb", 9, 3),
(b"aaab", 15, 4),
(b"aab", 16, 3),
(b"aaab", 22, 4),
(b"aab", 23, 3),
(b"abb", 24, 3),
(b"abab", 28, 4),
(b"aab", 32, 3),
(b"abb", 33, 3),
(b"abb", 37, 3),
(b"aaab", 40, 4),
(b"aab", 41, 3),
(b"abab", 42, 4),
],
),
(
"b",
vec![
(b"ab aab", 2, 6),
(b"aab", 5, 3),
(b"ab ab", 6, 5),
(b"abb", 9, 3),
(b"aaaab", 14, 5),
(b"aaab", 15, 4),
(b"aab", 16, 3),
(b"ab aaaaab", 17, 9),
(b"aaaaab", 20, 6),
(b"aaaab", 21, 5),
(b"aaab", 22, 4),
(b"aab", 23, 3),
(b"abb", 24, 3),
(b"abab", 28, 4),
(b"abaab", 30, 5),
(b"aab", 32, 3),
(b"abb", 33, 3),
(b"abb", 37, 3),
(b"aaab", 40, 4),
(b"aab", 41, 3),
(b"abab", 42, 4),
],
),
(
"c",
vec![
(b"ab", 2, 2),
(b"aab", 5, 3),
(b"ab", 6, 2),
(b"ab", 9, 2),
(b"aab", 16, 3),
(b"ab", 17, 2),
(b"aab", 23, 3),
(b"ab", 24, 2),
(b"ab", 28, 2),
(b"ab", 30, 2),
(b"aab", 32, 3),
(b"ab", 33, 2),
(b"ab", 37, 2),
(b"aab", 41, 3),
(b"ab", 42, 2),
(b"ab", 44, 2),
],
),
],
)],
);
}
#[test]
fn test_variable_hex_string_alternations() {
let checker = Checker::new(
r#"
rule a {
strings:
// 61 is 'a', 67 is 'g'
$a = { 61 (62 | 63 | 64 65 | 66 ) 67 }
$b = { 61 (62 | ( 63 | 64 ) 65 ) }
$c = { ( ( 61 | 62 ) | 63 64 ) }
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"ag abcdefg abg cde ace df acg adeg adfg afg egadadce",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"abg", 11, 3),
(b"acg", 26, 3),
(b"adeg", 30, 4),
(b"afg", 40, 3),
],
),
(
"b",
vec![
(b"ab", 3, 2),
(b"ab", 11, 2),
(b"ace", 19, 3),
(b"ade", 30, 3),
],
),
(
"c",
vec![
(b"a", 0, 1),
(b"a", 3, 1),
(b"b", 4, 1),
(b"cd", 5, 2),
(b"a", 11, 1),
(b"b", 12, 1),
(b"cd", 15, 2),
(b"a", 19, 1),
(b"a", 26, 1),
(b"a", 30, 1),
(b"a", 35, 1),
(b"a", 40, 1),
(b"a", 46, 1),
(b"a", 48, 1),
],
),
],
)],
);
}
#[test]
fn test_variable_hex_string_atoms() {
let checker = Checker::new(
r#"
rule a {
strings:
// whole atom on the left side
$a = { 64 65 66 67 (?? | 68) ?9 }
// whole atom on the right side
$b = { 6? (62 | 63 ??) 64 65 66 67 }
// whole atom in the middle
$c = { (?2 | 61) 64 65 66 67 ?? 69 }
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"abcdefghi defgri defghh abdefg acadefg adefggi bdefg.i",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"defghi", 3, 6),
(b"defgri", 10, 6),
(b"defggi", 40, 6),
(b"defg.i", 48, 6),
],
),
("b", vec![(b"abdefg", 24, 6), (b"acadefg", 31, 7)]),
("c", vec![(b"adefggi", 39, 7), (b"bdefg.i", 47, 7)]),
],
)],
);
let checker = Checker::new(
r#"
rule a {
strings:
// atom from a whole alternation
$a = { 61 ?? ( 62 63 | 64) 65 }
// atom from a split alternation
$b = { 61 ( ?? 62 63 | 64) 65 }
$c = { 61 62 ( 63 ?? | 64) 65 }
// atom from two split alternations
$d = { 61 ( ?? 62 | 63 ) ( 64 | 65 ?? ) 66 }
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"aabce a.de ade ace abe abde abcce aabdf a.bdf aceef a.be.f",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![(b"aabce", 0, 5), (b"a.de", 6, 4), (b"abde", 23, 4)],
),
("b", vec![(b"aabce", 0, 5), (b"ade", 11, 3)]),
("c", vec![(b"abde", 23, 4), (b"abcce", 28, 5)]),
(
"d",
vec![
(b"aabdf", 34, 5),
(b"a.bdf", 40, 5),
(b"aceef", 46, 5),
(b"a.be.f", 52, 6),
],
),
],
)],
);
}
#[test]
fn test_hex_string_atoms_multiple_matches() {
let checker = Checker::new(
r#"
rule a {
strings:
// 61 is 'a', 62 is 'b'
$a = { 61 [1-2] 62 62 }
$b = { 61 [1-] 62 62 }
$c = { 61 [0-1] 62 62 }
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"b ab aab abbb aaaab aaaaabb ababaabb abbaaabab",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"abbb", 9, 4),
(b"aaabb", 22, 5),
(b"aabb", 23, 4),
(b"aabb", 32, 4),
],
),
(
"b",
vec![
(b"ab aab abb", 2, 10),
(b"aab abb", 5, 7),
(b"ab abb", 6, 6),
(b"abbb", 9, 4),
(b"aaaab aaaaabb", 14, 13),
(b"aaab aaaaabb", 15, 12),
(b"aab aaaaabb", 16, 11),
(b"ab aaaaabb", 17, 10),
(b"aaaaabb", 20, 7),
(b"aaaabb", 21, 6),
(b"aaabb", 22, 5),
(b"aabb", 23, 4),
(b"abb ababaabb", 24, 12),
(b"ababaabb", 28, 8),
(b"abaabb", 30, 6),
(b"aabb", 32, 4),
(b"abb abb", 33, 7),
],
),
(
"c",
vec![
(b"abb", 9, 3),
(b"aabb", 23, 4),
(b"abb", 24, 3),
(b"aabb", 32, 4),
(b"abb", 33, 3),
(b"abb", 37, 3),
],
),
],
)],
);
let checker = Checker::new(
r#"
rule a {
strings:
$a = /a.{1,2}bb/
$b = /a[^z]+bb/
$c = /a.?bb/
// Regression test, hide the greedy repetitions inside an expression not
// visited by the atom visitor.
$d = /a([^ ]+|z).aa/
condition:
any of them
}
"#,
);
checker.check_full_matches(
b"b ab aab abbb aaaab aaaaabb ababaabb abbaaabab",
vec![(
"default:a".to_owned(),
vec![
(
"a",
vec![
(b"abbb", 9, 4),
(b"aaabb", 22, 5),
(b"aabb", 23, 4),
(b"aabb", 32, 4),
],
),
(
"b",
vec![
(b"ab aab abbb aaaab aaaaabb ababaabb abb", 2, 38),
(b"aab abbb aaaab aaaaabb ababaabb abb", 5, 35),
(b"ab abbb aaaab aaaaabb ababaabb abb", 6, 34),
(b"abbb aaaab aaaaabb ababaabb abb", 9, 31),
(b"aaaab aaaaabb ababaabb abb", 14, 26),
(b"aaab aaaaabb ababaabb abb", 15, 25),
(b"aab aaaaabb ababaabb abb", 16, 24),
(b"ab aaaaabb ababaabb abb", 17, 23),
(b"aaaaabb ababaabb abb", 20, 20),
(b"aaaabb ababaabb abb", 21, 19),
(b"aaabb ababaabb abb", 22, 18),
(b"aabb ababaabb abb", 23, 17),
(b"abb ababaabb abb", 24, 16),
(b"ababaabb abb", 28, 12),
(b"abaabb abb", 30, 10),
(b"aabb abb", 32, 8),
(b"abb abb", 33, 7),
],
),
(
"c",
vec![
(b"abbb", 9, 4),
(b"aabb", 23, 4),
(b"abb", 24, 3),
(b"aabb", 32, 4),
(b"abb", 33, 3),
(b"abb", 37, 3),
],
),
(
"d",
vec![
(b"ab aa", 2, 5),
(b"abbb aa", 9, 7),
(b"aaaab aa", 14, 8),
(b"aaab aa", 15, 7),
(b"aab aa", 16, 6),
(b"ab aa", 17, 5),
(b"aaaaa", 20, 5),
(b"ababaa", 28, 6),
(b"abbaaa", 37, 6),
],
),
],
)],
);
}