This changes the way in which comparison operators, and also the "contains" and "matches" operators handle undefined operands. Before this change the result of such operations was undefined, after this change the result is false.
This commit is contained in:
Victor M. Alvarez
2020-03-26 13:50:49 +01:00
parent ba313b4833
commit b7da5d2835
3 changed files with 129 additions and 85 deletions
+18 -5
View File
@@ -1405,7 +1405,7 @@ Undefined values
Modules often leave variables in an undefined state, for example when the
variable doesn't make sense in the current context (think of ``pe.entry_point``
while scanning a non-PE file). YARA handles undefined values in way that allows
while scanning a non-PE file). YARA handles undefined values in a way that allows
the rule to keep its meaningfulness. Take a look at this rule:
.. code-block:: yara
@@ -1431,10 +1431,23 @@ if the condition is changed to:
$a or pe.entry_point == 0x1000
You would expect the rule to match in this case if the file contains the string,
even if it isn't a PE file. That's exactly how YARA behaves. The logic is
simple: any arithmetic, comparison, or boolean operation will result in an
undefined value if one of its operands is undefined, except for *OR* operations
where an undefined operand is interpreted as a False.
even if it isn't a PE file. That's exactly how YARA behaves. The logic is as
follows:
* Arithmetic and bitwise operators return a undefined value if some of its
operands is undefined.
* Boolean operators `and` and `or` will treat undefined operands as `false`.
* Boolean `not` operator returns false if the operand is undefined.
* Comparison operators and any other operator whose result is a boolean (like
the ``contains`` and ``matches`` operators) will return `false` if any of its
operands is undefined.
In the expression above `pe.entry_point == 0x1000` will be false, because
`pe.entry_point` is undefined, and the `==` operator returns false if any of its
operand is undefined.
External variables
+93 -76
View File
@@ -1302,11 +1302,16 @@ int yr_execute_code(
pop(r2);
pop(r1);
ensure_defined(r1);
ensure_defined(r2);
if (is_undef(r1) || is_undef(r2))
{
r1.i = false;
}
else
{
r1.i = memmem(r1.ss->c_string, r1.ss->length,
r2.ss->c_string, r2.ss->length) != NULL;
}
r1.i = memmem(r1.ss->c_string, r1.ss->length,
r2.ss->c_string, r2.ss->length) != NULL;
push(r1);
break;
@@ -1322,14 +1327,10 @@ int yr_execute_code(
break;
case OP_MATCHES:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
if (r1.ss->length == 0)
if (is_undef(r1) || is_undef(r2) || r1.ss->length == 0)
{
r1.i = false;
push(r1);
@@ -1337,15 +1338,15 @@ int yr_execute_code(
}
result = yr_re_exec(
context,
(uint8_t*) r2.re->code,
(uint8_t*) r1.ss->c_string,
r1.ss->length,
0,
r2.re->flags | RE_FLAGS_SCAN,
NULL,
NULL,
&found);
context,
(uint8_t*) r2.re->code,
(uint8_t*) r1.ss->c_string,
r1.ss->length,
0,
r2.re->flags | RE_FLAGS_SCAN,
NULL,
NULL,
&found);
if (result != ERROR_SUCCESS)
stop = true;
@@ -1386,54 +1387,60 @@ int yr_execute_code(
case OP_INT_EQ:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i == r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i == r2.i;
push(r1);
break;
case OP_INT_NEQ:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i != r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i != r2.i;
push(r1);
break;
case OP_INT_LT:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i < r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i < r2.i;
push(r1);
break;
case OP_INT_GT:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i > r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i > r2.i;
push(r1);
break;
case OP_INT_LE:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i <= r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i <= r2.i;
push(r1);
break;
case OP_INT_GE:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.i >= r2.i;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.i >= r2.i;
push(r1);
break;
@@ -1486,54 +1493,60 @@ int yr_execute_code(
case OP_DBL_LT:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.d < r2.d;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.d < r2.d;
push(r1);
break;
case OP_DBL_GT:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.d > r2.d;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.d > r2.d;
push(r1);
break;
case OP_DBL_LE:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.d <= r2.d;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.d <= r2.d;
push(r1);
break;
case OP_DBL_GE:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = r1.d >= r2.d;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = r1.d >= r2.d;
push(r1);
break;
case OP_DBL_EQ:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = fabs(r1.d - r2.d) < DBL_EPSILON;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = fabs(r1.d - r2.d) < DBL_EPSILON;
push(r1);
break;
case OP_DBL_NEQ:
pop(r2);
pop(r1);
ensure_defined(r2);
ensure_defined(r1);
r1.i = fabs(r1.d - r2.d) >= DBL_EPSILON;
if (is_undef(r1) || is_undef(r2))
r1.i = false;
else
r1.i = fabs(r1.d - r2.d) >= DBL_EPSILON;
push(r1);
break;
@@ -1590,29 +1603,33 @@ int yr_execute_code(
pop(r2);
pop(r1);
ensure_defined(r1);
ensure_defined(r2);
switch(opcode)
if (is_undef(r1) || is_undef(r2))
{
case OP_STR_EQ:
r1.i = (sized_string_cmp(r1.ss, r2.ss) == 0);
break;
case OP_STR_NEQ:
r1.i = (sized_string_cmp(r1.ss, r2.ss) != 0);
break;
case OP_STR_LT:
r1.i = (sized_string_cmp(r1.ss, r2.ss) < 0);
break;
case OP_STR_LE:
r1.i = (sized_string_cmp(r1.ss, r2.ss) <= 0);
break;
case OP_STR_GT:
r1.i = (sized_string_cmp(r1.ss, r2.ss) > 0);
break;
case OP_STR_GE:
r1.i = (sized_string_cmp(r1.ss, r2.ss) >= 0);
break;
r1.i = false;
}
else
{
switch(opcode)
{
case OP_STR_EQ:
r1.i = (sized_string_cmp(r1.ss, r2.ss) == 0);
break;
case OP_STR_NEQ:
r1.i = (sized_string_cmp(r1.ss, r2.ss) != 0);
break;
case OP_STR_LT:
r1.i = (sized_string_cmp(r1.ss, r2.ss) < 0);
break;
case OP_STR_LE:
r1.i = (sized_string_cmp(r1.ss, r2.ss) <= 0);
break;
case OP_STR_GT:
r1.i = (sized_string_cmp(r1.ss, r2.ss) > 0);
break;
case OP_STR_GE:
r1.i = (sized_string_cmp(r1.ss, r2.ss) >= 0);
break;
}
}
push(r1);
+18 -4
View File
@@ -1492,7 +1492,6 @@ void test_for()
}",
NULL);
assert_false_rule(
"import \"tests\" \
rule test { \
@@ -1524,16 +1523,31 @@ void test_for()
}",
NULL);
// Test case for issue #1180.
assert_true_rule(
"import \"tests\" \
rule test { \
condition: \
for 0 i in (1..3) : ( \
false \
) \
not tests.string_array[4] contains \"foo\" \
}",
NULL);
// Test case for issue #1180.
assert_true_rule(
"import \"tests\" \
rule test { \
condition: \
not tests.string_dict[\"undefined\"] matches /foo/ \
}",
NULL);
assert_false_rule(
"import \"tests\" \
rule test { \
condition: \
not tests.undefined.i \
}",
NULL);
assert_true_rule(
"import \"tests\" \