Skip to content

Commit

Permalink
Add stringtoint() and inttostring(). (#1767)
Browse files Browse the repository at this point in the history
* Add stringtoint() and inttostring().

Add math.stringtoint() and math.inttostring() which let you convert between an
integer and a string representation of it, and back.

* Rename functions and use a stack allocation and snprintf()

* Use errno.h, not sys/errno.h
  • Loading branch information
wxsBSD authored Aug 25, 2022
1 parent 181783b commit 86edc1f
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 1 deletion.
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 <errno.h>
#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 @@ -721,6 +726,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 @@ -744,6 +796,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

0 comments on commit 86edc1f

Please sign in to comment.