Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add to_int() and to_string() to "math" module. #1767

Merged
merged 3 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion docs/modules/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,54 @@ file and create signatures based on those results.
.. c:function:: mode(offset, size)

.. versionadded:: 4.2.0

Returns the most common byte, starting at *offset* and looking at the next
*size* bytes. When scanning a
running process the *offset* argument should be a virtual address within
the process address space. The returned value is a float.
*offset* and *size* are optional; if left empty, the complete file is searched.

*Example: math.mode(0, filesize) == 0xFF*

.. c:function:: to_string(int)

.. versionadded:: 4.3.0

Convert the given integer to a string. Note: integers in YARA are signed.

*Example: math.to_string(10) == "10"*
*Example: math.to_string(-1) == "-1"*

.. c:function:: to_string(int, base)

.. versionadded:: 4.3.0

Convert the given integer to a string in the given base. Supported bases are
10, 8 and 16. Note: integers in YARA are signed.

*Example: math.to_string(32, 16) == "20"*
*Example: math.to_string(-1, 16) == "ffffffffffffffff"*

.. c:function:: to_int(string)

.. versionadded:: 4.3.0

Convert the given string to a signed integer. If the string starts with "0x"
it is treated as base 16. If the string starts with "0" it is treated base
8. Leading '+' or '-' is also supported.

*Example: math.to_int("1234") == 1234*
*Example: math.to_int("-10") == -10*
*Example: math.to_int("-010" == -8*

.. c:function:: to_int(string, base)

.. versionadded:: 4.3.0

Convert the given string, interpreted with the given base, to a signed
integer. Base must be 0 or between 2 and 32 inclusive. If it is zero then
the string will be intrepreted as base 16 if it starts with "0x" or as base
8 if it starts with "0". Leading '+' or '-' is also supported.

*Example: math.to_int("011", 8) == "9"*
*Example: math.to_int("-011", 0) == "-9"*
56 changes: 56 additions & 0 deletions libyara/modules/math/math.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <sys/errno.h>
wxsBSD marked this conversation as resolved.
Show resolved Hide resolved
#include <math.h>
#include <yara/mem.h>
#include <yara/modules.h>
Expand All @@ -35,6 +37,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define MODULE_NAME math

#define PI 3.141592653589793
// This is more than enough space to hold the maximum signed 64bit integer as a
// string in decimal, hex or octal, including the sign and NULL terminator.
#define INT64_MAX_STRING 30

// log2 is not defined by math.h in VC++

Expand Down Expand Up @@ -715,6 +720,53 @@ define_function(mode_global)
return_integer(most_common);
}

define_function(to_string)
{
int64_t i = integer_argument(1);
char str[INT64_MAX_STRING];
snprintf(str, INT64_MAX_STRING, "%lld", i);
return_string(&str);
}

define_function(to_string_base)
{
int64_t i = integer_argument(1);
int64_t base = integer_argument(2);
char str[INT64_MAX_STRING];
char *fmt;
switch (base)
{
case 10:
fmt = "%lld";
break;
case 8:
fmt = "%llo";
break;
case 16:
fmt = "%llx";
break;
default:
return_string(YR_UNDEFINED);
}
snprintf(str, INT64_MAX_STRING, fmt, i);
return_string(&str);
}

define_function(to_int)
{
char* s = string_argument(1);
int64_t result = strtoll(s, NULL, 0);
return_integer(result == 0 && errno ? YR_UNDEFINED : result);
}

define_function(to_int_base)
{
char* s = string_argument(1);
int64_t base = integer_argument(2);
int64_t result = strtoll(s, NULL, base);
return_integer(result == 0 && errno ? YR_UNDEFINED : result);
}

begin_declarations
declare_float("MEAN_BYTES");
declare_function("in_range", "fff", "i", in_range);
Expand All @@ -738,6 +790,10 @@ begin_declarations
declare_function("percentage", "i", "f", percentage_global);
declare_function("mode", "ii", "i", mode_range);
declare_function("mode", "", "i", mode_global);
declare_function("to_string", "i", "s", to_string);
declare_function("to_string", "ii", "s", to_string_base);
declare_function("to_int", "s", "i", to_int);
declare_function("to_int", "si", "i", to_int_base);
end_declarations

int module_initialize(YR_MODULE* module)
Expand Down
134 changes: 134 additions & 0 deletions tests/test-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,140 @@ int main(int argc, char** argv)
}",
"123ABCDEF123456987DE");

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(1234) == \"1234\" \
}",
NULL);

// We use signed integers by default if no base is specified.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(-1) == \"-1\" \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(32, 16) == \"20\" \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(32, 8) == \"40\" \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(32, 10) == \"32\" \
}",
NULL);

// Base 10 is always a signed integer, all other bases are unsigned.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_string(-1, 10) == \"-1\" and \
math.to_string(-1, 16) == \"ffffffffffffffff\" and \
math.to_string(-1, 8) == \"1777777777777777777777\" \
}",
NULL);

// Passing a base that is not 10, 8 or 16 will result in UNDEFINED.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
not defined(math.to_string(32, 9)) \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"1234\") == 1234 \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"-1\") == -1 \
}",
NULL);

// Leading spaces and + are allowed.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\" +1\") == 1 \
}",
NULL);

// Strings can be prefixed with 0x and will be interpreted as hexadecimal.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"0x10\") == 16 \
}",
NULL);

// Strings prefixed with 0 will be interpreted as octal.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"010\") == 8 \
}",
NULL);

// Strings that are only partially converted are still fine.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"10A20\") == 10 \
}",
NULL);

assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"10\", 8) == 8 \
}",
NULL);

// Base 0 is a special case that tries to interpret the string by prefix, or
// default to decimal. We aren't doing anything special to get this, it is
// part of strtoll by default.
assert_true_rule(
"import \"math\" \
rule test { \
condition: \
math.to_int(\"010\", 0) == 8 and \
math.to_int(\"0x10\", 0) == 16 and \
math.to_int(\"10\", 0) == 10 \
}",
NULL);

yr_finalize();

YR_DEBUG_FPRINTF(
Expand Down