From 70962808c758c93d897078cf9759aee2f4d275a0 Mon Sep 17 00:00:00 2001 From: Wesley Shields Date: Mon, 15 Aug 2022 09:47:25 -0400 Subject: [PATCH 1/3] 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. --- libyara/modules/math/math.c | 53 ++++++++++++++ tests/test-math.c | 134 ++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) diff --git a/libyara/modules/math/math.c b/libyara/modules/math/math.c index 9c06a7ef58..52908c582d 100644 --- a/libyara/modules/math/math.c +++ b/libyara/modules/math/math.c @@ -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 +#include #include #include #include @@ -715,6 +717,53 @@ define_function(mode_global) return_integer(most_common); } +define_function(inttostring) +{ + int64_t i = integer_argument(1); + char *str = NULL; + asprintf(&str, "%lld", i); + return_string(str == NULL ? (char*) YR_UNDEFINED : str); +} + +define_function(inttostring_base) +{ + int64_t i = integer_argument(1); + int64_t base = integer_argument(2); + char *str = NULL; + char *fmt; + switch (base) + { + case 10: + fmt = "%lld"; + break; + case 8: + fmt = "%llo"; + break; + case 16: + fmt = "%llx"; + break; + default: + return_string(YR_UNDEFINED); + } + asprintf(&str, fmt, i); + return_string(str == NULL ? (char*) YR_UNDEFINED : str); +} + +define_function(stringtoint) +{ + char* s = string_argument(1); + int64_t result = strtoll(s, NULL, 0); + return_integer(result == 0 && errno ? YR_UNDEFINED : result); +} + +define_function(stringtoint_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); @@ -738,6 +787,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("inttostring", "i", "s", inttostring); + declare_function("inttostring", "ii", "s", inttostring_base); + declare_function("stringtoint", "s", "i", stringtoint); + declare_function("stringtoint", "si", "i", stringtoint_base); end_declarations int module_initialize(YR_MODULE* module) diff --git a/tests/test-math.c b/tests/test-math.c index 4fec0cd71f..cec9f65d87 100644 --- a/tests/test-math.c +++ b/tests/test-math.c @@ -223,6 +223,140 @@ int main(int argc, char** argv) }", "123ABCDEF123456987DE"); + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.inttostring(1234) == \"1234\" \ + }", + NULL); + + // We use signed integers by default if no base is specified. + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.inttostring(-1) == \"-1\" \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.inttostring(32, 16) == \"20\" \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.inttostring(32, 8) == \"40\" \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.inttostring(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.inttostring(-1, 10) == \"-1\" and \ + math.inttostring(-1, 16) == \"ffffffffffffffff\" and \ + math.inttostring(-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.inttostring(32, 9)) \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"1234\") == 1234 \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"-1\") == -1 \ + }", + NULL); + + // Leading spaces and + are allowed. + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\" +1\") == 1 \ + }", + NULL); + + // Strings can be prefixed with 0x and will be interpreted as hexadecimal. + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"0x10\") == 16 \ + }", + NULL); + + // Strings prefixed with 0 will be interpreted as octal. + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"010\") == 8 \ + }", + NULL); + + // Strings that are only partially converted are still fine. + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"10A20\") == 10 \ + }", + NULL); + + assert_true_rule( + "import \"math\" \ + rule test { \ + condition: \ + math.stringtoint(\"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.stringtoint(\"010\", 0) == 8 and \ + math.stringtoint(\"0x10\", 0) == 16 and \ + math.stringtoint(\"10\", 0) == 10 \ + }", + NULL); + yr_finalize(); YR_DEBUG_FPRINTF( From 005139088ac627d28c1bbca8b19d594f44b0da51 Mon Sep 17 00:00:00 2001 From: Wesley Shields Date: Mon, 22 Aug 2022 14:11:29 -0400 Subject: [PATCH 2/3] Rename functions and use a stack allocation and snprintf() --- docs/modules/math.rst | 45 ++++++++++++++++++++++++++++++++++++- libyara/modules/math/math.c | 31 +++++++++++++------------ tests/test-math.c | 38 +++++++++++++++---------------- 3 files changed, 80 insertions(+), 34 deletions(-) diff --git a/docs/modules/math.rst b/docs/modules/math.rst index 79ba011133..bd4c855c57 100644 --- a/docs/modules/math.rst +++ b/docs/modules/math.rst @@ -157,7 +157,7 @@ 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 @@ -165,3 +165,46 @@ file and create signatures based on those results. *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"* diff --git a/libyara/modules/math/math.c b/libyara/modules/math/math.c index 52908c582d..23c9cb8706 100644 --- a/libyara/modules/math/math.c +++ b/libyara/modules/math/math.c @@ -37,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++ @@ -717,19 +720,19 @@ define_function(mode_global) return_integer(most_common); } -define_function(inttostring) +define_function(to_string) { int64_t i = integer_argument(1); - char *str = NULL; - asprintf(&str, "%lld", i); - return_string(str == NULL ? (char*) YR_UNDEFINED : str); + char str[INT64_MAX_STRING]; + snprintf(str, INT64_MAX_STRING, "%lld", i); + return_string(&str); } -define_function(inttostring_base) +define_function(to_string_base) { int64_t i = integer_argument(1); int64_t base = integer_argument(2); - char *str = NULL; + char str[INT64_MAX_STRING]; char *fmt; switch (base) { @@ -745,18 +748,18 @@ define_function(inttostring_base) default: return_string(YR_UNDEFINED); } - asprintf(&str, fmt, i); - return_string(str == NULL ? (char*) YR_UNDEFINED : str); + snprintf(str, INT64_MAX_STRING, fmt, i); + return_string(&str); } -define_function(stringtoint) +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(stringtoint_base) +define_function(to_int_base) { char* s = string_argument(1); int64_t base = integer_argument(2); @@ -787,10 +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("inttostring", "i", "s", inttostring); - declare_function("inttostring", "ii", "s", inttostring_base); - declare_function("stringtoint", "s", "i", stringtoint); - declare_function("stringtoint", "si", "i", stringtoint_base); + 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) diff --git a/tests/test-math.c b/tests/test-math.c index cec9f65d87..e95470134f 100644 --- a/tests/test-math.c +++ b/tests/test-math.c @@ -227,7 +227,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(1234) == \"1234\" \ + math.to_string(1234) == \"1234\" \ }", NULL); @@ -236,7 +236,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(-1) == \"-1\" \ + math.to_string(-1) == \"-1\" \ }", NULL); @@ -244,7 +244,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(32, 16) == \"20\" \ + math.to_string(32, 16) == \"20\" \ }", NULL); @@ -252,7 +252,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(32, 8) == \"40\" \ + math.to_string(32, 8) == \"40\" \ }", NULL); @@ -260,7 +260,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(32, 10) == \"32\" \ + math.to_string(32, 10) == \"32\" \ }", NULL); @@ -269,9 +269,9 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.inttostring(-1, 10) == \"-1\" and \ - math.inttostring(-1, 16) == \"ffffffffffffffff\" and \ - math.inttostring(-1, 8) == \"1777777777777777777777\" \ + math.to_string(-1, 10) == \"-1\" and \ + math.to_string(-1, 16) == \"ffffffffffffffff\" and \ + math.to_string(-1, 8) == \"1777777777777777777777\" \ }", NULL); @@ -280,7 +280,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - not defined(math.inttostring(32, 9)) \ + not defined(math.to_string(32, 9)) \ }", NULL); @@ -288,7 +288,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"1234\") == 1234 \ + math.to_int(\"1234\") == 1234 \ }", NULL); @@ -296,7 +296,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"-1\") == -1 \ + math.to_int(\"-1\") == -1 \ }", NULL); @@ -305,7 +305,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\" +1\") == 1 \ + math.to_int(\" +1\") == 1 \ }", NULL); @@ -314,7 +314,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"0x10\") == 16 \ + math.to_int(\"0x10\") == 16 \ }", NULL); @@ -323,7 +323,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"010\") == 8 \ + math.to_int(\"010\") == 8 \ }", NULL); @@ -332,7 +332,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"10A20\") == 10 \ + math.to_int(\"10A20\") == 10 \ }", NULL); @@ -340,7 +340,7 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"10\", 8) == 8 \ + math.to_int(\"10\", 8) == 8 \ }", NULL); @@ -351,9 +351,9 @@ int main(int argc, char** argv) "import \"math\" \ rule test { \ condition: \ - math.stringtoint(\"010\", 0) == 8 and \ - math.stringtoint(\"0x10\", 0) == 16 and \ - math.stringtoint(\"10\", 0) == 10 \ + math.to_int(\"010\", 0) == 8 and \ + math.to_int(\"0x10\", 0) == 16 and \ + math.to_int(\"10\", 0) == 10 \ }", NULL); From 38e9522b3666c3c9c7d028ed9f5dba052d104c49 Mon Sep 17 00:00:00 2001 From: Wesley Shields Date: Tue, 23 Aug 2022 08:55:38 -0400 Subject: [PATCH 3/3] Use errno.h, not sys/errno.h --- libyara/modules/math/math.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyara/modules/math/math.c b/libyara/modules/math/math.c index 23c9cb8706..36b1340235 100644 --- a/libyara/modules/math/math.c +++ b/libyara/modules/math/math.c @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include +#include #include #include #include