!strcmp(get_buffer(), ips.get_buffer());
}
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+
+class StubIpsOption : public IpsOption
+{
+public:
+ StubIpsOption(const char* name, option_type_t option_type) :
+ IpsOption(name, option_type)
+ { }
+};
+
+TEST_CASE("IpsOption test", "[ips_option]")
+{
+ StubIpsOption main_ips("ips_test",
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+
+ SECTION("IpsOperator == test")
+ {
+ StubIpsOption case_diff_name("not_hello_world",
+ option_type_t::RULE_OPTION_TYPE_BUFFER_USE);
+ REQUIRE((main_ips == case_diff_name) == false);
+
+ StubIpsOption case_diff_option("hello_world",
+ option_type_t::RULE_OPTION_TYPE_CONTENT);
+ REQUIRE((main_ips == case_diff_option) == false);
+
+ StubIpsOption case_option_na("hello_world",
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+ REQUIRE((main_ips == case_option_na) == false);
+ }
+
+ SECTION("hash test")
+ {
+ StubIpsOption main_ips("ips_test",
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+
+ SECTION("hash test with short string")
+ {
+ StubIpsOption main_ips_short("ips_test",
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+ REQUIRE((main_ips.hash() == main_ips_short.hash()) == true);
+
+ StubIpsOption main_ips_short_diff("not_ips_test",
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+ REQUIRE((main_ips.hash() == main_ips_short_diff.hash()) == false);
+ }
+
+ SECTION("hash test with long string")
+ {
+ std::string really_long_string =
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101" \
+ "101010101010101010101010101010101010101010101010101010101010101";
+
+ StubIpsOption main_ips_long_first(really_long_string.c_str(),
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+ StubIpsOption main_ips_long_second(really_long_string.c_str(),
+ option_type_t::RULE_OPTION_TYPE_OTHER);
+ REQUIRE(main_ips_long_first.hash() == main_ips_long_second.hash());
+
+ REQUIRE(main_ips_long_first.hash() != main_ips.hash());
+ }
+ }
+}
+
+#endif
add_dynamic_module(ips_ber_data ips_options ips_ber_data.cc)
add_dynamic_module(ips_ber_skip ips_options ips_ber_skip.cc)
add_dynamic_module(ips_bufferlen ips_options ips_bufferlen.cc)
- add_dynamic_module(ips_byte_extract ips_options ips_byte_extract.cc)
- add_dynamic_module(ips_byte_jump ips_options ips_byte_jump.cc)
- add_dynamic_module(ips_byte_math ips_options ips_byte_math.cc)
- add_dynamic_module(ips_byte_test ips_options ips_byte_test.cc)
+ add_dynamic_module(ips_byte_extract ips_options extract.cc ips_byte_extract.cc)
+ add_dynamic_module(ips_byte_jump ips_options extract.cc ips_byte_jump.cc)
+ add_dynamic_module(ips_byte_math ips_options extract.cc ips_byte_math.cc)
+ add_dynamic_module(ips_byte_test ips_options extract.cc ips_byte_test.cc)
add_dynamic_module(ips_cvs ips_options ips_cvs.cc)
add_dynamic_module(ips_enable ips_options ips_enable.cc)
add_dynamic_module(ips_file_type ips_options ips_file_type.cc)
#include "extract.h"
+#include "framework/ips_option.h"
#include "log/messages.h"
#include "utils/snort_bounds.h"
#include "utils/util_cstring.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
+#include "service_inspectors/dce_rpc/dce_common.h"
#endif
using namespace snort;
namespace snort
{
+
/* Given a variable name, retrieve its index.*/
int8_t GetVarByName(const char* name)
{
used at this point */
int GetVarValueByIndex(uint32_t* dst, uint8_t var_number)
{
- if (dst == nullptr || var_number >= NUM_IPS_OPTIONS_VARS)
+ if (dst == nullptr or var_number >= NUM_IPS_OPTIONS_VARS)
return IPS_OPTIONS_NO_VAR;
*dst = extracted_values[var_number];
void set_byte_order(uint8_t& order, uint8_t flag, const char* opt)
{
- if ( order )
+ if (order)
ParseWarning(WARN_RULES, "%s specifies multiple byte orders, using last", opt);
order = flag;
const uint8_t* start, const uint8_t* end,
uint32_t* value)
{
- if (endianness != ENDIAN_LITTLE && endianness != ENDIAN_BIG)
- {
- /* we only support 2 byte formats */
- return -2;
- }
+ if (endianness != ENDIAN_LITTLE and endianness != ENDIAN_BIG)
+ return -2; /* we only support 2 byte formats */
/* make sure the data to grab stays in bounds */
if (!inBounds(start,end,ptr + (bytes_to_grab - 1)))
- {
return -3;
- }
if (!inBounds(start,end,ptr))
- {
return -3;
- }
/*
* We only support grabbing 1, 2, or 4 bytes of binary data.
* @returns 0 on success, otherwise failure
*/
int string_extract(int bytes_to_grab, int base, const uint8_t* ptr,
- const uint8_t* start, const uint8_t* end,
- uint32_t* value)
+ const uint8_t* start, const uint8_t* end, uint32_t* value)
{
char byte_array[TEXTLEN];
char* parse_helper;
int x; /* counter */
- if (bytes_to_grab > (TEXTLEN - 1) || bytes_to_grab <= 0)
- {
+ if (bytes_to_grab > (TEXTLEN - 1) or bytes_to_grab <= 0)
return -1;
- }
/* make sure the data to grab stays in bounds */
if (!inBounds(start,end,ptr + (bytes_to_grab - 1)))
- {
return -3;
- }
if (!inBounds(start,end,ptr))
- {
return -3;
- }
for (x=0; x<bytes_to_grab; x++)
- {
byte_array[x] = *(ptr+x);
- }
byte_array[bytes_to_grab] = '\0';
#ifdef TEST_BYTE_EXTRACT
printf("[----]\n");
- for (x=0; (x<TEXTLEN) && (byte_array[x] != '\0'); x++)
+ for (x=0; (x<TEXTLEN) and (byte_array[x] != '\0'); x++)
printf("%c", byte_array[x]);
printf("\n");
printf("converted value: 0x%08X (%u) %s\n", *value, *value, (char*)byte_array);
#endif /* TEST_BYTE_EXTRACT */
- return(parse_helper - byte_array); /* Return the number of bytes actually extracted */
+ /* Return the number of bytes actually extracted */
+ return(parse_helper - byte_array);
+}
+
+void set_cursor_bounds(const ByteData& settings, const Cursor& c,
+ const uint8_t*& start, const uint8_t*& ptr, const uint8_t*& end)
+{
+ start = c.buffer();
+ end = start + c.size();
+
+ ptr = settings.relative_flag ? c.start() : c.buffer();
+ ptr += settings.offset;
+}
+
+int32_t data_extraction(const ByteData& settings, Packet* p,
+ uint32_t& result_var, const uint8_t* start,
+ const uint8_t* ptr, const uint8_t* end)
+{
+ if (p == nullptr)
+ return IpsOption::NO_MATCH;
+
+ // check bounds
+ if (ptr < start or ptr >= end)
+ return IpsOption::NO_MATCH;
+
+ uint8_t endian = settings.endianness;
+ if (settings.endianness == ENDIAN_FUNC)
+ {
+ if (!p->endianness or
+ !p->endianness->get_offset_endianness(ptr - p->data, endian))
+ return IpsOption::NO_MATCH;
+ }
+
+ // do the extraction
+ int32_t bytes_read = 0;
+ uint32_t value = 0;
+ if (!settings.string_convert_flag)
+ {
+ int ret = 0;
+ ret = byte_extract(endian, settings.bytes_to_extract, ptr, start, end, &value);
+ if (ret < 0)
+ return IpsOption::NO_MATCH;
+
+ bytes_read = settings.bytes_to_extract;
+ }
+ else
+ {
+ bytes_read = string_extract(settings.bytes_to_extract, settings.base,
+ ptr, start, end, &value);
+ if (bytes_read < 0)
+ return IpsOption::NO_MATCH;
+ }
+
+ if (settings.bitmask_val != 0)
+ {
+ uint32_t num_tailing_zeros_bitmask =
+ getNumberTailingZerosInBitmask(settings.bitmask_val);
+ value = value & settings.bitmask_val;
+ if (value and num_tailing_zeros_bitmask)
+ value = value >> num_tailing_zeros_bitmask;
+ }
+
+ result_var = value;
+ return bytes_read;
+}
+
+int32_t extract_data(const ByteData& settings, const Cursor& c, Packet* p,
+ uint32_t& result_var)
+{
+ const uint8_t* start = nullptr;
+ const uint8_t* ptr = nullptr;
+ const uint8_t* end = nullptr;
+ set_cursor_bounds(settings, c, start, ptr, end);
+ return data_extraction(settings, p, result_var, start, ptr, end);
}
uint32_t getNumberTailingZerosInBitmask(uint32_t bitmask)
uint8_t numBytesInBitmask(uint32_t bitmask_value)
{
uint8_t num_bytes;
- if ( bitmask_value <= 0xFF )
+ if (bitmask_value <= 0xFF)
num_bytes = 1;
- else if ( bitmask_value <= 0xFFFF )
+ else if (bitmask_value <= 0xFFFF)
num_bytes = 2;
- else if ( bitmask_value <= 0xFFFFFF )
+ else if (bitmask_value <= 0xFFFFFF)
num_bytes = 3;
else
num_bytes = 4;
} // namespace snort
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
#ifdef UNIT_TEST
+
+#define INITIALIZE(obj, bytes_to_extract_value, offset_value, relative_flag_value, \
+ string_convert_flag_value, base_value, endianness_value, bitmask_val_value) \
+ obj.base = base_value; \
+ obj.bitmask_val = bitmask_val_value; \
+ obj.bytes_to_extract = bytes_to_extract_value; \
+ obj.offset = offset_value; \
+ obj.endianness = endianness_value; \
+ obj.relative_flag = relative_flag_value; \
+ obj.string_convert_flag = string_convert_flag_value;
+
TEST_CASE("ips options bitmask utils")
{
// numBytesInBitmask tests
// Fill up array
int8_t ind1 = AddVarNameToList("OFFSET");
REQUIRE((ind1 == 0));
+
int8_t ind2 = AddVarNameToList("VALUE");
REQUIRE((ind2 == 1));
+
int8_t ind3 = AddVarNameToList("VAR3");
REQUIRE((ind3 == IPS_OPTIONS_NO_VAR));
REQUIRE((GetVarValueByIndex(&dst, NUM_IPS_OPTIONS_VARS) == IPS_OPTIONS_NO_VAR));
REQUIRE((SetVarValueByIndex(0, NUM_IPS_OPTIONS_VARS) == IPS_OPTIONS_NO_VAR));
}
-#endif
+TEST_CASE("set_cursor_bounds", "[byte_extraction_tests]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 010 12345 0x75";
+ p.dsize = 21;
+ Cursor c(&p);
+ const uint8_t* start = nullptr;
+ const uint8_t* ptr = nullptr;
+ const uint8_t* end = nullptr;
+
+ SECTION("4 bytes read, no offset")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ set_cursor_bounds(settings, c, start, ptr, end);
+ CHECK(start == p.data);
+ CHECK(ptr == p.data);
+ CHECK(end == p.data + 21);
+ }
+ SECTION("4 byte read, offset = 4")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 4, 4, 0, 0, 0, ENDIAN_BIG, 0);
+ set_cursor_bounds(settings, c, start, ptr, end);
+ CHECK(start == p.data);
+ CHECK(ptr == p.data + 4);
+ CHECK(end == p.data + 21);
+ }
+ SECTION("4 bytes read, cursor move without relative flag")
+ {
+ c.set_pos(3);
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ set_cursor_bounds(settings, c, start, ptr, end);
+ CHECK(start == p.data);
+ CHECK(ptr == p.data);
+ CHECK(end == p.data + 21);
+ }
+ SECTION("4 bytes read, cursor move with relative flag")
+ {
+ c.set_pos(3);
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, true, 0, 0, ENDIAN_BIG, 0);
+ set_cursor_bounds(settings, c, start, ptr, end);
+ CHECK(start == p.data);
+ CHECK(ptr == p.data + 3);
+ CHECK(end == p.data + 21);
+ }
+}
+
+TEST_CASE("extract_data valid", "[byte_extraction_tests]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 010 12345 0x75";
+ p.dsize = 21;
+ Cursor c(&p);
+ uint32_t res = 0;
+
+ SECTION("1 byte read, all - off")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 1);
+ CHECK(res == 76);
+ }
+ SECTION("2 bytes read, all - off")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 19567);
+ }
+ SECTION("3 bytes read, all - off")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 3, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 3);
+ CHECK(res == 5009266);
+ }
+ SECTION("4 bytes read, all - off")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 4);
+ CHECK(res == 1282372197);
+ }
+ SECTION("1 byte read, offset 3")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 3, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 1);
+ CHECK(res == 101);
+ }
+ SECTION("1 byte read, offset 3, relative")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 3, 1, 0, 0, ENDIAN_BIG, 0);
+ c.set_pos(3);
+ CHECK(extract_data(settings, c, &p, res) == 1);
+ CHECK(res == 48);
+ }
+ SECTION("cursor 3, 1 byte read, offset -3, relative")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, -3, 1, 0, 0, ENDIAN_BIG, 0);
+ c.set_pos(3);
+ CHECK(extract_data(settings, c, &p, res) == 1);
+ CHECK(res == 76);
+ }
+ SECTION("1 byte read, offset 6, string conversion, base 10")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 10, 0, 1, 10, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 1);
+ CHECK(res == 1);
+ }
+ SECTION("2 bytes read, offset 6, string conversion, base 8 without prefix")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 10, 0, 1, 8, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 10);
+ }
+ SECTION("2 bytes read, offset 6, string conversion, base 10")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 10, 0, 1, 10, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 12);
+ }
+ SECTION("2 bytes read, offset 6, string conversion, base 16 without prefix")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 10, 0, 1, 16, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 18);
+ }
+ SECTION("3 bytes read, offset 6, string conversion, base 8 with prefix")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 3, 6, 0, 1, 8, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 3);
+ CHECK(res == 8);
+ }
+ SECTION("4 bytes read, offset 6, string conversion, base 16 with prefix")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 4, 16, 0, 1, 16, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == 4);
+ CHECK(res == 117);
+ }
+ SECTION("2 byte read, bitmask 1100110011100011")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 0, 0, 0, 0, ENDIAN_BIG, 52451);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 19555);
+ }
+ SECTION("2 byte read, bitmask 1100110011100000")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 2, 0, 0, 0, 0, ENDIAN_BIG, 52448);
+ CHECK(extract_data(settings, c, &p, res) == 2);
+ CHECK(res == 611);
+ }
+ SECTION("4 bytes read, ENDIAN_LITTLE")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, 0, 0, 0, ENDIAN_LITTLE, 0);
+ CHECK(extract_data(settings, c, &p, res) == 4);
+ CHECK(res == 1701998412);
+ }
+ SECTION("4 bytes read, ENDIAN_FUNC, packet.endianness " \
+ "= DCERPC_BO_FLAG__LITTLE_ENDIAN")
+ {
+ DceEndianness* auto_endian = new DceEndianness();
+ auto_endian->hdr_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
+ auto_endian->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
+ p.endianness = auto_endian;
+ ByteData settings;
+ INITIALIZE(settings, 4, 0, 0, 0, 0, ENDIAN_FUNC, 0);
+ CHECK(extract_data(settings, c, &p, res) == 4);
+ CHECK(res == 1701998412);
+ }
+}
+
+TEST_CASE("extract_data invalid", "[byte_extraction_tests]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 9876";
+ p.dsize = 11;
+ Cursor c(&p);
+ uint32_t res = 0;
+
+ SECTION("packet = nullptr")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, nullptr, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("read more than 4 bytes")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 6, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("check bounds of packet, offset > packet size")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 20, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("negative offset, without relative flag")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, -20, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("negative offset, out of bounds")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, -20, 1, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("check bounds of packet, offset > packet size, empty packet")
+ {
+ p.data = (const uint8_t*)"";
+ p.dsize = 0;
+ Cursor c2(&p);
+ ByteData settings;
+ INITIALIZE(settings, 1, 20, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c2, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("check bounds of packet, read 2 bytes, empty packet")
+ {
+ p.data = (const uint8_t*)"";
+ p.dsize = 0;
+ Cursor c2(&p);
+ ByteData settings;
+ INITIALIZE(settings, 2, 0, 0, 0, 0, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c2, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("ENDIAN_FUNC, without definition of endianness in packet")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 3, 0, 0, 0, 0, ENDIAN_FUNC, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("conversion from string, decimal number, base = 8")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 3, 6, 0, 1, 8, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("conversion from string but the input is symbol")
+ {
+ ByteData settings;
+ INITIALIZE(settings, 1, 0, 0, 1, 10, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+ SECTION("cursor behind the packet size")
+ {
+ c.set_pos(15);
+ ByteData settings;
+ INITIALIZE(settings, 1, 0, 0, 1, 10, ENDIAN_BIG, 0);
+ CHECK(extract_data(settings, c, &p, res) == IpsOption::NO_MATCH);
+ }
+}
+#endif
#ifndef EXTRACT_H
#define EXTRACT_H
-#include "main/thread.h"
+#include "framework/cursor.h"
+#include "framework/endianness.h"
#include "main/snort_types.h"
+#include "main/thread.h"
+#include "protocols/packet.h"
#define ENDIAN_BIG 0x1
#define ENDIAN_LITTLE 0x2
namespace snort
{
+
+struct ByteData
+{
+ uint32_t base;
+ uint32_t bitmask_val;
+ uint32_t bytes_to_extract;
+ int32_t offset;
+ uint8_t endianness;
+ bool relative_flag;
+ bool string_convert_flag;
+};
+
SO_PUBLIC int string_extract(
int bytes_to_grab, int base, const uint8_t* ptr,
const uint8_t* start, const uint8_t* end, uint32_t* value);
int endianness, int bytes_to_grab, const uint8_t* ptr,
const uint8_t* start, const uint8_t* end, uint32_t* value);
+void set_cursor_bounds(const ByteData& settings, const Cursor& c,
+ const uint8_t*& start, const uint8_t*& ptr, const uint8_t*& end);
+
+int32_t data_extraction(const ByteData& settings, Packet* p,
+ uint32_t& result_var, const uint8_t* start,
+ const uint8_t* ptr, const uint8_t* end);
+
+int32_t extract_data(const ByteData& settings, const Cursor& c, Packet* p,
+ uint32_t& result_var);
+
SO_PUBLIC void set_byte_order(uint8_t& order, uint8_t flag, const char* opt);
SO_PUBLIC uint32_t getNumberTailingZerosInBitmask(uint32_t);
SO_PUBLIC int SetVarValueByIndex(uint32_t value, uint8_t var_number);
}
#endif
-
#include "framework/module.h"
#include "hash/hash_key_operations.h"
#include "log/messages.h"
-#include "protocols/packet.h"
#include "profiler/profiler.h"
+#include "protocols/packet.h"
#include "utils/util.h"
#include "extract.h"
+#ifdef UNIT_TEST
+#include <catch/snort_catch.h>
+#include "service_inspectors/dce_rpc/dce_common.h"
+#endif
+
using namespace snort;
static THREAD_LOCAL ProfileStats byteExtractPerfStats;
#define s_help \
"rule option to convert data to an integer variable"
-struct ByteExtractData
+struct ByteExtractData : public ByteData
{
- uint32_t bytes_to_grab;
- int32_t offset;
- uint8_t relative_flag;
- uint8_t data_string_convert_flag;
- uint8_t align;
- uint8_t endianness;
- uint32_t base;
uint32_t multiplier;
- uint32_t bitmask_val;
+ uint8_t align;
int8_t var_number;
char* name;
};
class ByteExtractOption : public IpsOption
{
public:
- ByteExtractOption(const ByteExtractData& c) : IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE)
- { config = c; }
+ ByteExtractOption(const ByteExtractData& c) :
+ IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE), config(c)
+ { }
~ByteExtractOption() override
{ snort_free(config.name); }
private:
ByteExtractData config;
+ void apply_alignment(uint32_t& value);
};
//-------------------------------------------------------------------------
uint32_t ByteExtractOption::hash() const
{
- uint32_t a = config.bytes_to_grab;
+ uint32_t a = config.bytes_to_extract;
uint32_t b = config.offset;
uint32_t c = config.base;
mix(a,b,c);
a += (config.relative_flag << 24 |
- config.data_string_convert_flag << 16 |
+ config.string_convert_flag << 16 |
config.align << 8 |
config.endianness);
b += config.multiplier;
bool ByteExtractOption::operator==(const IpsOption& ips) const
{
- if ( !IpsOption::operator==(ips) )
+ if (!IpsOption::operator==(ips))
return false;
const ByteExtractOption& rhs = (const ByteExtractOption&)ips;
const ByteExtractData* left = &config;
const ByteExtractData* right = &rhs.config;
- if ((left->bytes_to_grab == right->bytes_to_grab) &&
- (left->offset == right->offset) &&
- (left->relative_flag == right->relative_flag) &&
- (left->data_string_convert_flag == right->data_string_convert_flag) &&
- (left->align == right->align) &&
- (left->endianness == right->endianness) &&
- (left->base == right->base) &&
- (left->multiplier == right->multiplier) &&
- (left->var_number == right->var_number) &&
+ if ((left->bytes_to_extract == right->bytes_to_extract) and
+ (left->offset == right->offset) and
+ (left->relative_flag == right->relative_flag) and
+ (left->string_convert_flag == right->string_convert_flag) and
+ (left->align == right->align) and
+ (left->endianness == right->endianness) and
+ (left->base == right->base) and
+ (left->multiplier == right->multiplier) and
+ (left->var_number == right->var_number) and
(left->bitmask_val == right->bitmask_val))
{
return true;
{
RuleProfile profile(byteExtractPerfStats);
- ByteExtractData* data = &config;
+ uint32_t value = 0;
+ int bytes_read = extract_data(config, c, p, value);
- if (data == nullptr || p == nullptr)
+ if (bytes_read == NO_MATCH)
return NO_MATCH;
- const uint8_t* start = c.buffer();
- int dsize = c.size();
+ value *= config.multiplier;
- const uint8_t* ptr = data->relative_flag ? c.start() : c.buffer();
- ptr += data->offset;
+ apply_alignment(value);
- const uint8_t* end = start + dsize;
+ SetVarValueByIndex(value, config.var_number);
- // check bounds
- if (ptr < start || ptr >= end)
- return NO_MATCH;
+ c.add_pos(config.offset + bytes_read);
- uint8_t endian = data->endianness;
- if (data->endianness == ENDIAN_FUNC)
- {
- if (!p->endianness ||
- !p->endianness->get_offset_endianness(ptr - p->data, endian))
- return NO_MATCH;
- }
-
- // do the extraction
- int ret = 0;
- int bytes_read = 0;
- uint32_t value;
- if (data->data_string_convert_flag == 0)
- {
- ret = byte_extract(endian, data->bytes_to_grab, ptr, start, end, &value);
- if (ret < 0)
- return NO_MATCH;
-
- bytes_read = data->bytes_to_grab;
- }
- else
- {
- ret = string_extract(data->bytes_to_grab, data->base, ptr, start, end, &value);
- if (ret < 0)
- return NO_MATCH;
-
- bytes_read = ret;
- }
-
- if (data->bitmask_val != 0 )
- {
- uint32_t num_tailing_zeros_bitmask = getNumberTailingZerosInBitmask(data->bitmask_val);
- value = value & data->bitmask_val;
- if ( value && num_tailing_zeros_bitmask )
- {
- value = value >> num_tailing_zeros_bitmask;
- }
- }
-
- /* multiply */
- value *= data->multiplier;
+ return MATCH;
+}
- /* align to next 32-bit or 16-bit boundary */
- if ((data->align == 4) && (value % 4))
+void ByteExtractOption::apply_alignment(uint32_t& value)
+{
+ if ((config.align == 4) and (value % 4))
{
value = value + 4 - (value % 4);
}
- else if ((data->align == 2) && (value % 2))
+ else if ((config.align == 2) and (value % 2))
{
value = value + 2 - (value % 2);
}
-
- SetVarValueByIndex(value, data->var_number);
-
- /* advance cursor */
- c.add_pos(data->offset + bytes_read);
-
- /* this rule option always "matches" if the read is performed correctly */
- return MATCH;
}
+
//-------------------------------------------------------------------------
// api
//-------------------------------------------------------------------------
/* Checks a ByteExtractData instance for errors. */
static bool ByteExtractVerify(ByteExtractData* data)
{
- if (data->bytes_to_grab > MAX_BYTES_TO_GRAB && data->data_string_convert_flag == 0)
+ if (data->bytes_to_extract > MAX_BYTES_TO_GRAB and data->string_convert_flag == 0)
{
ParseError("byte_extract rule option cannot extract more than %d bytes.",
MAX_BYTES_TO_GRAB);
return false;
}
- if (data->bytes_to_grab > PARSELEN && data->data_string_convert_flag == 1)
+ if (data->bytes_to_extract > PARSELEN and data->string_convert_flag == 1)
{
ParseError("byte_extract rule cannot process more than %d bytes for "
"string extraction.", PARSELEN);
return false;
}
- if (data->align != 0 && data->align != 2 && data->align != 4)
+ if (data->align != 0 and data->align != 2 and data->align != 4)
{
ParseError("byte_extract rule option has an invalid argument "
"to 'align'. Valid arguments are '2' and '4'.");
return false;
}
- if (data->offset < 0 && data->relative_flag == 0)
+ if (data->offset < 0 and data->relative_flag == 0)
{
ParseError("byte_extract rule option has a negative offset, but does "
"not use the 'relative' option.");
return false;
}
- if (data->base && !data->data_string_convert_flag)
+ if (data->base and !data->string_convert_flag)
{
ParseError("byte_extract rule option has a string conversion type "
"(dec, hex, or oct) without the \"string\" "
return false;
}
- if (numBytesInBitmask(data->bitmask_val) > data->bytes_to_grab)
+ if (numBytesInBitmask(data->bitmask_val) > data->bytes_to_extract)
{
ParseError("Number of bytes in \"bitmask\" value is greater than bytes to extract.");
return false;
{ return DETECT; }
public:
- ByteExtractData data = {};
+ ByteExtractData data{};
};
bool ExtractModule::begin(const char*, int, SnortConfig*)
bool ExtractModule::end(const char*, int, SnortConfig*)
{
- if ( !data.endianness )
+ if (!data.endianness)
data.endianness = ENDIAN_BIG;
return ByteExtractVerify(&data);
}
bool ExtractModule::set(const char*, Value& v, SnortConfig*)
{
- if ( v.is("~count") )
- data.bytes_to_grab = v.get_uint8();
+ if (v.is("~count"))
+ data.bytes_to_extract = v.get_uint8();
- else if ( v.is("~offset") )
+ else if (v.is("~offset"))
data.offset = v.get_int32();
- else if ( v.is("~name") )
+ else if (v.is("~name"))
data.name = snort_strdup(v.get_string());
- else if ( v.is("relative") )
+ else if (v.is("relative"))
data.relative_flag = 1;
- else if ( v.is("align") )
+ else if (v.is("align"))
data.align = v.get_uint8();
- else if ( v.is("multiplier") )
+ else if (v.is("multiplier"))
data.multiplier = v.get_uint16();
- else if ( v.is("big") )
+ else if (v.is("big"))
set_byte_order(data.endianness, ENDIAN_BIG, "byte_extract");
- else if ( v.is("little") )
+ else if (v.is("little"))
set_byte_order(data.endianness, ENDIAN_LITTLE, "byte_extract");
- else if ( v.is("dce") )
+ else if (v.is("dce"))
set_byte_order(data.endianness, ENDIAN_FUNC, "byte_extract");
- else if ( v.is("string") )
+ else if (v.is("string"))
{
- data.data_string_convert_flag = 1;
+ data.string_convert_flag = 1;
data.base = 10;
}
- else if ( v.is("dec") )
+ else if (v.is("dec"))
data.base = 10;
- else if ( v.is("hex") )
+ else if (v.is("hex"))
data.base = 16;
- else if ( v.is("oct") )
+ else if (v.is("oct"))
data.base = 8;
- else if ( v.is("bitmask") )
+ else if (v.is("bitmask"))
data.bitmask_val = v.get_uint32();
else
&byte_extract_api.base,
nullptr
};
+
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
+#ifdef UNIT_TEST
+
+//-------------------------------------------------------------------------
+// helpers
+//-------------------------------------------------------------------------
+#define INITIALIZE(obj, bytes_to_extract_value, offset_value, relative_flag_value, \
+ string_convert_flag_value, align_value, endianness_value, base_value,\
+ multiplier_value, bitmask_val_value, var_number_value, name_value) \
+ obj.base = base_value; \
+ obj.bitmask_val = bitmask_val_value; \
+ obj.bytes_to_extract = bytes_to_extract_value; \
+ obj.offset = offset_value; \
+ obj.endianness = endianness_value; \
+ obj.relative_flag = relative_flag_value; \
+ obj.string_convert_flag = string_convert_flag_value; \
+ obj.multiplier = multiplier_value; \
+ obj.align = align_value; \
+ obj.var_number = var_number_value; \
+ obj.name = name_value;
+
+class ByteExtractDataMatcher
+ : public Catch::Matchers::Impl::MatcherBase<ByteExtractData>
+{
+public:
+ ByteExtractDataMatcher(const ByteExtractData& value) : m_value(value) {}
+
+ bool match(ByteExtractData const& rhs) const override
+ {
+ return ((m_value.bytes_to_extract == rhs.bytes_to_extract) and
+ (m_value.offset == rhs.offset) and
+ (m_value.relative_flag == rhs.relative_flag) and
+ (m_value.string_convert_flag == rhs.string_convert_flag) and
+ (m_value.align == rhs.align) and
+ (m_value.endianness == rhs.endianness) and
+ (m_value.base == rhs.base) and
+ (m_value.multiplier == rhs.multiplier) and
+ (m_value.var_number == rhs.var_number) and
+ (m_value.bitmask_val == rhs.bitmask_val));
+ }
+
+ std::string describe() const override
+ {
+ std::ostringstream ss;
+ ss << "settings is equals to:\n";
+ ss << "bytes_to_extract : " << m_value.bytes_to_extract << ";\n";
+ ss << "offset : " << m_value.offset << ";\n";
+ ss << "relative_flag : " << m_value.relative_flag << ";\n";
+ ss << "string_convert_flag : " << m_value.string_convert_flag << ";\n";
+ ss << "align : " << m_value.align << ";\n";
+ ss << "endianness : " << m_value.endianness << ";\n";
+ ss << "base : " << m_value.base << ";\n";
+ ss << "multiplier : " << m_value.multiplier << ";\n";
+ ss << "bitmask_val : " << m_value.bitmask_val << ";\n";
+ ss << "var_number : " << m_value.var_number << ";\n";
+ return ss.str();
+ }
+
+private:
+ ByteExtractData m_value;
+};
+
+ByteExtractDataMatcher ByteExtractDataEquals(const ByteExtractData& value)
+{
+ return {value};
+}
+
+class SetBufferOptionHelper : public IpsOption
+{
+public:
+ SetBufferOptionHelper(const char* value)
+ : IpsOption(value, RULE_OPTION_TYPE_BUFFER_SET)
+ { }
+};
+
+//-------------------------------------------------------------------------
+// option tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ByteExtractOption::operator== valid", "[ips_byte_extract]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ char* lhs_name = new char[9];
+ strcpy(lhs_name, "test_lhs");
+ ByteExtractData data_lhs;
+ INITIALIZE(data_lhs, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, lhs_name);
+ ByteExtractOption lhs(data_lhs);
+
+ char* rhs_name = new char[9];
+ strcpy(rhs_name, "test_rhs");
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, rhs_name);
+ ByteExtractOption rhs(data_rhs);
+
+ CHECK(lhs == rhs);
+}
+
+TEST_CASE("ByteExtractOption::operator== invalid", "[ips_byte_extract]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ char* lhs_name = new char[5];
+ strcpy(lhs_name, "test");
+ ByteExtractData data_lhs;
+ INITIALIZE(data_lhs, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, nullptr);
+ ByteExtractOption lhs(data_lhs);
+
+ SECTION("not equal to IpsOption object")
+ {
+ CHECK(lhs != set_buf);
+ delete[] lhs_name;
+ }
+ SECTION("all fields is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 1, 4, true, false, 2, ENDIAN_FUNC, 0, 1, 0x1, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("bytes_to_extract is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("offset is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("relative_flag is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, true, 0, 0, 0, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("string_convert_flag is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, true, 0, 0, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("align is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("endianness is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, ENDIAN_FUNC, 0, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("base is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 16, 1, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("multiplier is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("bitmask is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 0, 1, 0xFFFF, 0, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("var_number is different")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, lhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("name is different")
+ {
+ delete[] lhs_name;
+ char* rhs_name = new char[5];
+ strcpy(rhs_name, "unix");
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, rhs_name);
+ ByteExtractOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+}
+
+TEST_CASE("ByteExtractOption::hash", "[ips_byte_extract]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ ByteExtractData data_lhs;
+ INITIALIZE(data_lhs, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, nullptr);
+ ByteExtractOption lhs(data_lhs);
+
+ SECTION("hash codes of any two equal objects are equal")
+ {
+ ByteExtractData data_rhs;
+ INITIALIZE(data_rhs, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, nullptr);
+ ByteExtractOption rhs(data_rhs);
+
+ CHECK(lhs.hash() == rhs.hash());
+ }
+}
+
+TEST_CASE("ByteExtractOption::eval valid", "[ips_byte_extract]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 12345";
+ p.dsize = 11;
+ Cursor c(&p);
+
+ for (unsigned i = 0; i < NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ SetVarValueByIndex(0, i);
+ }
+ ClearIpsOptionsVars();
+
+ char* name = new char[5];
+ strcpy(name, "test");
+
+ SECTION("1 byte read, offset 6, string conversion, base 10, align 2")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 1, 6, 0, 1, 2, ENDIAN_BIG, 10, 1, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 2);
+ CHECK(c.get_pos() == 7);
+ }
+ SECTION("3 byte read, offset 6, string conversion, base 10, align 4")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 3, 6, 0, 1, 4, ENDIAN_BIG, 10, 1, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 124);
+ CHECK(c.get_pos() == 9);
+ }
+ SECTION("1 byte read, offset 1, no string conversion, align 2, multiply 3")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 1, 1, 0, 0, 2, ENDIAN_BIG, 0, 3, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 334);
+ CHECK(c.get_pos() == 2);
+ }
+ SECTION("1 byte read, offset 3, no string conversion, align 4, multiply 5")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 1, 3, 0, 0, 4, ENDIAN_BIG, 0, 5, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 508);
+ CHECK(c.get_pos() == 4);
+ }
+}
+
+TEST_CASE("ByteExtractOption::eval invalid", "[ips_byte_extract]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 9876";
+ p.dsize = 11;
+ Cursor c(&p);
+
+ for (unsigned i = 0; i < NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ SetVarValueByIndex(0, i);
+ }
+ ClearIpsOptionsVars();
+
+ char* name = new char[5];
+ strcpy(name, "test");
+
+ SECTION("align value to 1")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 1, 0, 0, 0, 1, ENDIAN_BIG, 0, 1, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 76);
+ CHECK(c.get_pos() == 1);
+ }
+ SECTION("align value to 6")
+ {
+ ByteExtractData data;
+ INITIALIZE(data, 1, 0, 0, 0, 6, ENDIAN_BIG, 0, 1, 0, 0, name);
+ ByteExtractOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 76);
+ CHECK(c.get_pos() == 1);
+ }
+}
+
+//-------------------------------------------------------------------------
+// module tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ExtractModule lifecycle", "[ips_byte_extract]")
+{
+ ExtractModule obj;
+
+ SECTION("test of constructor")
+ {
+ CHECK(obj.data.multiplier == 1);
+ }
+ SECTION("test of \"begin\" method")
+ {
+ CHECK(obj.begin(nullptr, 0, nullptr));
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("test of \"end\" method")
+ {
+ obj.begin(nullptr, 0, nullptr);
+
+ Value v_name("test");
+ Parameter p_name{
+ "~name", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable that will be used in other rule options"};
+ v_name.set(&p_name);
+ obj.set(nullptr, v_name, nullptr);
+
+ Value v_bytes(4.0);
+ Parameter p_bytes{
+ "~count", Parameter::PT_INT, "1:10", nullptr,
+ "number of bytes to pick up from the buffer"};
+ v_bytes.set(&p_bytes);
+ obj.set(nullptr, v_bytes, nullptr);
+
+ CHECK(obj.end(nullptr, 0, nullptr));
+
+ char* name = new char[5];
+ strcpy(name, "test");
+ ByteExtractData expected;
+ INITIALIZE(expected, 4, 0, 0, 0, 0, ENDIAN_BIG, 0, 1, 0, 0, name);
+
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+
+ delete[] name;
+ delete[] obj.data.name;
+ }
+}
+
+TEST_CASE("Test of byte_extract_ctor", "[ips_byte_extract]")
+{
+ ClearIpsOptionsVars();
+
+ std::string name = "test";
+ for (unsigned i = 0; i <= NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ ExtractModule obj;
+ obj.begin(nullptr, 0, nullptr);
+ Value v((name + std::to_string(i)).c_str());
+ Parameter p{
+ "~name", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable that will be used in other rule options"};
+ v.set(&p);
+ obj.set(nullptr, v, nullptr);
+
+ if (i < NUM_IPS_OPTIONS_VARS)
+ {
+ IpsOption* res = byte_extract_ctor(&obj, nullptr);
+ delete res;
+ }
+ else
+ {
+ IpsOption* res_null = byte_extract_ctor(&obj, nullptr);
+ CHECK(res_null == nullptr);
+ delete[] obj.data.name;
+ }
+ }
+}
+
+TEST_CASE("ExtractModule::set", "[ips_byte_extract]")
+{
+ ExtractModule obj;
+ obj.begin(nullptr, 0, nullptr);
+
+ SECTION("set bytes_to_extract")
+ {
+ Value v(4.0);
+ Parameter p{
+ "~count", Parameter::PT_INT, "1:10", nullptr,
+ "number of bytes to pick up from the buffer"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 4, 0, 0, 0, 0, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set offset")
+ {
+ Value v(7.0);
+ Parameter p{
+ "~offset", Parameter::PT_INT, "-65535:65535", nullptr,
+ "number of bytes into the buffer to start processing"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 7, 0, 0, 0, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set name")
+ {
+ Value v("test_name");
+ Parameter p{
+ "~name", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable that will be used in other rule options"};
+ v.set(&p);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data.name, Catch::Matchers::Equals("test_name"));
+ }
+ SECTION("set relative")
+ {
+ Value v(true);
+ Parameter p{
+ "relative", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "offset from cursor instead of start of buffer"};
+ v.set(&p);
+ obj.set(nullptr, v, nullptr);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set multiplier")
+ {
+ Value v(6.0);
+ Parameter p{
+ "multiplier", Parameter::PT_INT, "1:65535", "1",
+ "scale extracted value by given amount"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set align")
+ {
+ Value v(2.0);
+ Parameter p{
+ "align", Parameter::PT_INT, "0:4", "0",
+ "round the number of converted bytes up to the next 2- "
+ "or 4-byte boundary"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set endianness")
+ {
+ Value v_big((double)ENDIAN_BIG);
+ Parameter p_big{"big", Parameter::PT_IMPLIED, nullptr, nullptr, "big endian"};
+ v_big.set(&p_big);
+ obj.set(nullptr, v_big, nullptr);
+ ByteExtractData expected_big;
+ INITIALIZE(expected_big, 0, 0, 0, 0, 0, ENDIAN_BIG, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v_big, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected_big));
+
+ Value v_lit((double)ENDIAN_LITTLE);
+ Parameter p_lit{"little", Parameter::PT_IMPLIED, nullptr, nullptr, "little endian"};
+ v_lit.set(&p_lit);
+ ByteExtractData expected_lit;
+ INITIALIZE(expected_lit, 0, 0, 0, 0, 0, ENDIAN_LITTLE, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v_lit, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected_lit));
+
+ Value v_dce((double)ENDIAN_FUNC);
+ Parameter p_dce{"dce", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "dcerpc2 determines endianness"};
+ v_dce.set(&p_dce);
+ ByteExtractData expected_dce;
+ INITIALIZE(expected_dce, 0, 0, 0, 0, 0, ENDIAN_FUNC, 0, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v_dce, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected_dce));
+ }
+ SECTION("set string")
+ {
+ Value v(true);
+ Parameter p{
+ "string", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "convert from string"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 1, 0, 0, 10, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set hex")
+ {
+ Value v(true);
+ Parameter p{
+ "hex", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "convert from hex string"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 16, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set oct")
+ {
+ Value v(true);
+ Parameter p{
+ "oct", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "convert from octal string"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set dec")
+ {
+ Value v(true);
+ Parameter p{
+ "dec", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "convert from decimal string"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 10, 1, 0, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("set bitmask")
+ {
+ Value v(1023.0);
+ Parameter p{
+ "bitmask", Parameter::PT_INT, "0x1:0xFFFFFFFF", nullptr,
+ "applies as an AND to the extracted value before "
+ "storage in 'name'"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 0, 1, 1023, 0, nullptr);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+ SECTION("invalid set")
+ {
+ Value v(1023.0);
+ Parameter p{
+ "error", Parameter::PT_INT, "nan", nullptr,
+ "not an option"};
+ v.set(&p);
+ ByteExtractData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, nullptr);
+
+ CHECK(!obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteExtractDataEquals(expected));
+ }
+
+ delete[] obj.data.name;
+}
+
+//-------------------------------------------------------------------------
+// api tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ByteExtractVerify_valid", "[ips_byte_extract]")
+{
+ ByteExtractData obj;
+ char name[] = "test";
+
+ SECTION("Minimum values, no string conversion")
+ {
+ INITIALIZE(obj, 1, -65535, 1, 0, 0, ENDIAN_FUNC, 0, 1, 0x1, 0, name);
+ CHECK(ByteExtractVerify(&obj));
+ }
+ SECTION("Maximum values, no string conversion")
+ {
+ INITIALIZE(obj, MAX_BYTES_TO_GRAB, 65535, 1, 0, 4, ENDIAN_FUNC,
+ 0, 65535, 0xFFFFFFFF, 0, name);
+ CHECK(ByteExtractVerify(&obj));
+ }
+ SECTION("Minimum values, with string conversion")
+ {
+ INITIALIZE(obj, 1, -65535, 1, 1, 0, ENDIAN_FUNC, 8, 1, 0x1, 0, name);
+ CHECK(ByteExtractVerify(&obj));
+ }
+ SECTION("Maximum values, with string conversion")
+ {
+ INITIALIZE(obj, PARSELEN, 65535, 1, 1, 4, ENDIAN_FUNC, 16, 65535, 0xFFFFFFFF, 0, name);
+ CHECK(ByteExtractVerify(&obj));
+ }
+}
+
+TEST_CASE("ByteExtractVerify_invalid", "[ips_byte_extract]")
+{
+ char* name = new char[5];
+ strcpy(name, "test");
+ ByteExtractData obj;
+ INITIALIZE(obj, 2, 7, 0, 0, 2, ENDIAN_FUNC, 0, 1, 0, 0, name);
+
+ SECTION("bytes_to_extract checks")
+ {
+ obj.bytes_to_extract = MAX_BYTES_TO_GRAB + 1;
+ CHECK((!ByteExtractVerify(&obj)));
+
+ obj.string_convert_flag = true;
+ obj.bytes_to_extract = PARSELEN + 1;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ SECTION("align checks")
+ {
+ obj.align = 1;
+ CHECK((!ByteExtractVerify(&obj)));
+
+ obj.align = 6;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ SECTION("offset checks")
+ {
+ obj.offset = -5;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ SECTION("name checks")
+ {
+ delete[] name;
+ obj.name = nullptr;
+ CHECK((!ByteExtractVerify(&obj)));
+
+ name = new char[6];
+ strcpy(name, "64bit");
+ obj.name = name;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ SECTION("base checks")
+ {
+ obj.base = 16;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ SECTION("bitmask checks")
+ {
+ obj.bytes_to_extract = 2;
+ obj.bitmask_val = 1048575;
+ CHECK((!ByteExtractVerify(&obj)));
+ }
+ delete[] name;
+}
+
+#endif
*
* Arguments:
* Required:
- * <bytes_to_grab>: number of bytes to pick up from the packet
+ * <bytes_to_extract>: number of bytes to pick up from the packet
* <offset>: number of bytes into the payload to grab the bytes
* Optional:
* ["relative"]: offset relative to last pattern match
#define s_name "byte_jump"
-typedef struct _ByteJumpData
+struct ByteJumpData : public ByteData
{
- uint32_t bytes_to_grab;
- int32_t offset;
- uint8_t relative_flag;
- uint8_t data_string_convert_flag;
- uint8_t from_beginning_flag;
- uint8_t align_flag;
- uint8_t endianness;
- uint32_t base;
uint32_t multiplier;
int32_t post_offset;
- uint32_t bitmask_val;
int8_t offset_var;
- uint8_t from_end_flag;
int8_t post_offset_var;
-} ByteJumpData;
+ bool align_flag;
+ bool from_beginning_flag;
+ bool from_end_flag;
+};
class ByteJumpOption : public IpsOption
{
public:
- ByteJumpOption(const ByteJumpData& c) : IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE)
- { config = c; }
-
+ ByteJumpOption(const ByteJumpData& c) : IpsOption(s_name,
+ RULE_OPTION_TYPE_BUFFER_USE), config(c)
+ { }
uint32_t hash() const override;
bool operator==(const IpsOption&) const override;
uint32_t ByteJumpOption::hash() const
{
- uint32_t a = config.bytes_to_grab;
+ uint32_t a = config.bytes_to_extract;
uint32_t b = config.offset;
uint32_t c = config.base;
mix(a,b,c);
a += (config.relative_flag << 24 |
- config.data_string_convert_flag << 16 |
+ config.string_convert_flag << 16 |
config.from_beginning_flag << 8 |
config.align_flag);
b += config.endianness;
mix(a,b,c);
a += config.post_offset;
- b += config.from_end_flag << 16 | (uint32_t) config.offset_var << 8 | config.post_offset_var;
+ b += config.from_end_flag << 16 |
+ (uint32_t) config.offset_var << 8 |
+ config.post_offset_var;
c += config.bitmask_val;
mix(a,b,c);
bool ByteJumpOption::operator==(const IpsOption& ips) const
{
- if ( !IpsOption::operator==(ips) )
+ if (!IpsOption::operator==(ips))
return false;
const ByteJumpOption& rhs = (const ByteJumpOption&)ips;
const ByteJumpData* left = &config;
const ByteJumpData* right = &rhs.config;
- if (( left->bytes_to_grab == right->bytes_to_grab) &&
- ( left->offset == right->offset) &&
- ( left->offset_var == right->offset_var) &&
- ( left->relative_flag == right->relative_flag) &&
- ( left->data_string_convert_flag == right->data_string_convert_flag) &&
- ( left->from_beginning_flag == right->from_beginning_flag) &&
- ( left->align_flag == right->align_flag) &&
- ( left->endianness == right->endianness) &&
- ( left->base == right->base) &&
- ( left->multiplier == right->multiplier) &&
- ( left->post_offset == right->post_offset) &&
- ( left->bitmask_val == right->bitmask_val) &&
- ( left->from_end_flag == right->from_end_flag) &&
+ if (( left->bytes_to_extract == right->bytes_to_extract) and
+ ( left->offset == right->offset) and
+ ( left->offset_var == right->offset_var) and
+ ( left->relative_flag == right->relative_flag) and
+ ( left->string_convert_flag == right->string_convert_flag) and
+ ( left->from_beginning_flag == right->from_beginning_flag) and
+ ( left->align_flag == right->align_flag) and
+ ( left->endianness == right->endianness) and
+ ( left->base == right->base) and
+ ( left->multiplier == right->multiplier) and
+ ( left->post_offset == right->post_offset) and
+ ( left->bitmask_val == right->bitmask_val) and
+ ( left->from_end_flag == right->from_end_flag) and
( left->post_offset_var == right->post_offset_var))
{
return true;
int32_t post_offset = 0;
// Get values from byte_extract variables, if present.
- if (bjd->offset_var >= 0 && bjd->offset_var < NUM_IPS_OPTIONS_VARS)
+ if (bjd->offset_var >= 0 and bjd->offset_var < NUM_IPS_OPTIONS_VARS)
{
uint32_t extract_offset;
GetVarValueByIndex(&extract_offset, bjd->offset_var);
{
offset = bjd->offset;
}
- if (bjd->post_offset_var >= 0 && bjd->post_offset_var < NUM_IPS_OPTIONS_VARS)
+ if (bjd->post_offset_var >= 0 and
+ bjd->post_offset_var < NUM_IPS_OPTIONS_VARS)
{
uint32_t extract_post_offset;
GetVarValueByIndex(&extract_post_offset, bjd->post_offset_var);
{
post_offset = bjd->post_offset;
}
-
- const uint8_t* const start_ptr = c.buffer();
- const uint8_t* const end_ptr = start_ptr + c.size();
- const uint8_t* const base_ptr = offset +
- ((bjd->relative_flag) ? c.start() : start_ptr);
+ ByteJumpData extract_config = config;
+ extract_config.offset = offset;
+ const uint8_t *start_ptr, *end_ptr, *base_ptr;
+ set_cursor_bounds(extract_config, c, start_ptr, base_ptr, end_ptr);
uint32_t jump = 0;
- uint32_t payload_bytes_grabbed = 0;
- uint8_t endian = bjd->endianness;
-
- if (endian == ENDIAN_FUNC)
- {
- if (!p->endianness ||
- !p->endianness->get_offset_endianness(base_ptr - p->data, endian))
- return NO_MATCH;
- }
+ int32_t payload_bytes_grabbed = 0;
- // Both of the extraction functions contain checks to ensure the data
- // is inbounds and will return no match if it isn't
- if (bjd->bytes_to_grab)
+ if (bjd->bytes_to_extract)
{
- if ( !bjd->data_string_convert_flag )
- {
- if ( byte_extract(
- endian, bjd->bytes_to_grab,
- base_ptr, start_ptr, end_ptr, &jump) )
- return NO_MATCH;
-
- payload_bytes_grabbed = bjd->bytes_to_grab;
- }
- else
- {
- int32_t tmp = string_extract(
- bjd->bytes_to_grab, bjd->base,
- base_ptr, start_ptr, end_ptr, &jump);
-
- if (tmp < 0)
- return NO_MATCH;
-
- payload_bytes_grabbed = tmp;
- }
-
- // Negative offsets that put us outside the buffer should have been caught
- // in the extraction routines
- assert(base_ptr >= c.buffer());
+ payload_bytes_grabbed = data_extraction(extract_config, p,
+ jump, start_ptr, base_ptr, end_ptr);
- if (bjd->bitmask_val != 0 )
+ if (NO_MATCH == payload_bytes_grabbed)
{
- uint32_t num_tailing_zeros_bitmask = getNumberTailingZerosInBitmask(bjd->bitmask_val);
- jump = jump & bjd->bitmask_val;
- if (jump && num_tailing_zeros_bitmask )
- {
- jump = jump >> num_tailing_zeros_bitmask;
- }
+ return NO_MATCH;
}
if (bjd->multiplier)
}
}
- uint32_t pos;
+ uint32_t pos = 0;
- if ( bjd->from_beginning_flag )
+ if (bjd->from_beginning_flag)
pos = 0;
- else if ( bjd->from_end_flag )
+ else if (bjd->from_end_flag)
pos = c.size();
else
pos += jump + post_offset;
- if ( !c.set_pos(pos) )
+ if (!c.set_pos(pos))
return NO_MATCH;
return MATCH;
"round the number of converted bytes up to the next 2- or 4-byte boundary" },
{ "post_offset", Parameter::PT_STRING, nullptr, nullptr,
- "skip forward or backward (positive or negative value) by variable name or number of " \
+ "skip forward or backward (positive or negative value) " \
+ "by variable name or number of " \
"bytes after the other jump options have been applied" },
{ "big", Parameter::PT_IMPLIED, nullptr, nullptr,
class ByteJumpModule : public Module
{
public:
- ByteJumpModule() : Module(s_name, s_help, s_params) { data.multiplier = 1; }
+ ByteJumpModule() : Module(s_name, s_help, s_params)
+ { data.multiplier = 1; }
bool begin(const char*, int, SnortConfig*) override;
bool end(const char*, int, SnortConfig*) override;
bool ByteJumpModule::end(const char*, int, SnortConfig*)
{
- if ( var.empty() )
+ if (var.empty())
data.offset_var = IPS_OPTIONS_NO_VAR;
else
{
return false;
}
}
- if ( post_var.empty() )
+ if (post_var.empty())
data.post_offset_var = IPS_OPTIONS_NO_VAR;
else
{
return false;
}
}
- if ( !data.endianness )
+ if (!data.endianness)
data.endianness = ENDIAN_BIG;
- if (data.from_beginning_flag && data.from_end_flag)
+ if (data.from_beginning_flag and data.from_end_flag)
{
ParseError("from_beginning and from_end options together in a rule\n");
return false;
}
- if ( data.bitmask_val && (numBytesInBitmask(data.bitmask_val) > data.bytes_to_grab))
+ if (data.bitmask_val and
+ (numBytesInBitmask(data.bitmask_val) > data.bytes_to_extract))
{
- ParseError("Number of bytes in \"bitmask\" value is greater than bytes to extract.");
+ ParseError("Number of bytes in \"bitmask\" value " \
+ "is greater than bytes to extract.");
return false;
}
- if ((data.bytes_to_grab > MAX_BYTES_TO_GRAB) && !data.data_string_convert_flag)
+ if ((data.bytes_to_extract > MAX_BYTES_TO_GRAB) and
+ !data.string_convert_flag)
{
ParseError(
- "byte_jump rule option cannot extract more than %d bytes without valid string prefix.",
- MAX_BYTES_TO_GRAB);
+ "byte_jump rule option cannot extract more than %d bytes without "\
+ "valid string prefix.", MAX_BYTES_TO_GRAB);
return false;
}
bool ByteJumpModule::set(const char*, Value& v, SnortConfig*)
{
- if ( v.is("~count") )
- data.bytes_to_grab = v.get_uint8();
+ if (v.is("~count"))
+ data.bytes_to_extract = v.get_uint8();
- else if ( v.is("~offset") )
+ else if (v.is("~offset"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
data.offset = n;
else
var = v.get_string();
}
- else if ( v.is("relative") )
+ else if (v.is("relative"))
data.relative_flag = 1;
- else if ( v.is("from_beginning") )
+ else if (v.is("from_beginning"))
data.from_beginning_flag = 1;
- else if ( v.is("align") )
+ else if (v.is("align"))
data.align_flag = 1;
- else if ( v.is("multiplier") )
+ else if (v.is("multiplier"))
data.multiplier = v.get_uint16();
- else if ( v.is("post_offset") )
+ else if (v.is("post_offset"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
data.post_offset = n;
else
post_var = v.get_string();
}
- else if ( v.is("big") )
+ else if (v.is("big"))
set_byte_order(data.endianness, ENDIAN_BIG, "byte_jump");
- else if ( v.is("little") )
+ else if (v.is("little"))
set_byte_order(data.endianness, ENDIAN_LITTLE, "byte_jump");
- else if ( v.is("dce") )
+ else if (v.is("dce"))
set_byte_order(data.endianness, ENDIAN_FUNC, "byte_jump");
- else if ( v.is("string") )
+ else if (v.is("string"))
{
- data.data_string_convert_flag = 1;
+ data.string_convert_flag = 1;
data.base = 10;
}
- else if ( v.is("dec") )
+ else if (v.is("dec"))
data.base = 10;
- else if ( v.is("hex") )
+ else if (v.is("hex"))
data.base = 16;
- else if ( v.is("oct") )
+ else if (v.is("oct"))
data.base = 8;
- else if ( v.is("from_end") )
+ else if (v.is("from_end"))
data.from_end_flag = 1;
- else if ( v.is("bitmask") )
+ else if (v.is("bitmask"))
data.bitmask_val = v.get_uint32();
else
nullptr
};
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
+#ifdef UNIT_TEST
+#include <iostream>
+#include <climits>
+
+#include "framework/value.h"
+#include "framework/parameter.h"
+
+#include "catch/snort_catch.h"
+
+#define NO_MATCH snort::IpsOption::EvalStatus::NO_MATCH
+#define MATCH snort::IpsOption::EvalStatus::MATCH
+
+void SetByteJumpData(ByteJumpData &byte_jump, int value)
+{
+ byte_jump.bytes_to_extract = value;
+ byte_jump.offset = value;
+ byte_jump.relative_flag = value;
+ byte_jump.string_convert_flag = value;
+ byte_jump.from_beginning_flag = value;
+ byte_jump.align_flag = value;
+ byte_jump.endianness = value;
+ byte_jump.base = value;
+ byte_jump.multiplier = value;
+ byte_jump.post_offset = value;
+ byte_jump.bitmask_val = value;
+ byte_jump.offset_var = value;
+ byte_jump.from_end_flag = value;
+ byte_jump.post_offset_var = value;
+}
+
+void SetByteJumpMaxValue(ByteJumpData &byte_jump)
+{
+ byte_jump.bytes_to_extract = UINT_MAX;
+ byte_jump.offset = INT_MAX;
+ byte_jump.relative_flag = UCHAR_MAX;
+ byte_jump.string_convert_flag = UCHAR_MAX;
+ byte_jump.from_beginning_flag = UCHAR_MAX;
+ byte_jump.align_flag = UCHAR_MAX;
+ byte_jump.endianness = UCHAR_MAX;
+ byte_jump.base = UINT_MAX;
+ byte_jump.multiplier = UINT_MAX;
+ byte_jump.post_offset = INT_MAX;
+ byte_jump.bitmask_val = UINT_MAX;
+ byte_jump.offset_var = SCHAR_MAX;
+ byte_jump.from_end_flag = UCHAR_MAX;
+ byte_jump.post_offset_var = SCHAR_MAX;
+}
+
+class StubIpsOption : public IpsOption
+{
+public:
+ StubIpsOption(const char* name, option_type_t option_type) :
+ IpsOption(name, option_type)
+ { }
+};
+
+class StubEndianness : public Endianness
+{
+public:
+ StubEndianness() = default;
+ virtual bool get_offset_endianness(int32_t, uint8_t&) override
+ { return false; }
+};
+
+TEST_CASE("ByteJumpOption test", "[ips_byte_jump]")
+{
+ ByteJumpData byte_jump;
+ SetByteJumpData(byte_jump, 1);
+ snort::IpsOption::set_buffer("hello_world");
+
+ SECTION("method hash")
+ {
+ ByteJumpOption hash_test(byte_jump);
+ ByteJumpOption hash_test_equal(byte_jump);
+
+ SECTION("Testing hash with very low values")
+ {
+ SECTION("Hash has same source")
+ {
+ REQUIRE(hash_test.hash() == hash_test_equal.hash());
+ }
+
+ SECTION("Compare hash from different source")
+ {
+ SetByteJumpData(byte_jump, 4);
+ ByteJumpOption hash_test_diff(byte_jump);
+ CHECK(hash_test.hash() != hash_test_diff.hash());
+ }
+ }
+
+ SECTION("Testing hash with maximum values")
+ {
+ SetByteJumpMaxValue(byte_jump);
+ ByteJumpOption hash_test_max(byte_jump);
+ ByteJumpOption hash_test_equal_max(byte_jump);
+
+ SECTION("Hash has same source")
+ {
+ CHECK(hash_test_max.hash() == hash_test_equal_max.hash());
+ }
+
+ SECTION("Compare hash from different source")
+ {
+ SetByteJumpMaxValue(byte_jump);
+ ByteJumpOption hash_test_max(byte_jump);
+ CHECK(hash_test.hash() != hash_test_max.hash());
+ }
+ }
+ }
+
+ SECTION("operator ==")
+ {
+ ByteJumpOption jump(byte_jump);
+
+ SECTION("Compare IpsOptions with different names")
+ {
+ StubIpsOption case_diff_name("not_hello_world",
+ option_type_t::RULE_OPTION_TYPE_BUFFER_USE);
+ REQUIRE(jump != case_diff_name);
+ }
+
+ ByteJumpData byte_jump2;
+ SetByteJumpData(byte_jump2, 1);
+
+ SECTION("Compare between equals objects")
+ {
+ ByteJumpOption jump_1(byte_jump);
+ REQUIRE(jump == jump_1);
+ }
+
+ SECTION("bytes_to_extract is different")
+ {
+ byte_jump2.bytes_to_extract = 2;
+ ByteJumpOption jump_2_1(byte_jump2);
+ REQUIRE(jump != jump_2_1);
+ }
+
+ SECTION("offset is different")
+ {
+ byte_jump2.offset = 2;
+ ByteJumpOption jump_2_2(byte_jump2);
+ REQUIRE(jump != jump_2_2);
+ }
+
+ SECTION("relative_flag is different")
+ {
+ byte_jump2.relative_flag = 0;
+ ByteJumpOption jump_2_3(byte_jump2);
+ REQUIRE(jump != jump_2_3);
+ }
+
+ SECTION("string_convert_flag is different")
+ {
+ byte_jump2.string_convert_flag = 0;
+ ByteJumpOption jump_2_4(byte_jump2);
+ REQUIRE(jump != jump_2_4);
+ }
+
+ SECTION("from_beginning_flag is different")
+ {
+ byte_jump2.from_beginning_flag = 0;
+ ByteJumpOption jump_2_5(byte_jump2);
+ REQUIRE(jump != jump_2_5);
+ }
+
+ SECTION("align_flag is different")
+ {
+ byte_jump2.align_flag = 0;
+ ByteJumpOption jump_2_6(byte_jump2);
+ REQUIRE(jump != jump_2_6);
+ }
+
+ SECTION("endianness is different")
+ {
+ byte_jump2.endianness = 0;
+ ByteJumpOption jump_2_7(byte_jump2);
+ REQUIRE(jump != jump_2_7);
+ }
+
+ SECTION("base is different")
+ {
+ byte_jump2.base = 2;
+ ByteJumpOption jump_2_8(byte_jump2);
+ REQUIRE(jump != jump_2_8);
+ }
+
+ SECTION("multiplier is different")
+ {
+ byte_jump2.multiplier = 2;
+ ByteJumpOption jump_2_9(byte_jump2);
+ REQUIRE(jump != jump_2_9);
+ }
+
+ SECTION("post_offset is different")
+ {
+ byte_jump2.post_offset = 2;
+ ByteJumpOption jump_2_10(byte_jump2);
+ REQUIRE(jump != jump_2_10);
+ }
+
+ SECTION("bitmask_val is different")
+ {
+ byte_jump2.bitmask_val = 2;
+ ByteJumpOption jump_2_11(byte_jump2);
+ REQUIRE(jump != jump_2_11);
+ }
+
+ SECTION("offset_var is different")
+ {
+ byte_jump2.offset_var = 0;
+ ByteJumpOption jump_2_12(byte_jump2);
+ REQUIRE(jump != jump_2_12);
+ }
+
+ SECTION("from_end_flag is different")
+ {
+ byte_jump2.from_end_flag = 0;
+ ByteJumpOption jump_2_13(byte_jump2);
+ REQUIRE(jump != jump_2_13);
+ }
+
+ SECTION("post_offset_var is different")
+ {
+ byte_jump2.post_offset_var = 0;
+ ByteJumpOption jump_2_14(byte_jump2);
+ REQUIRE(jump != jump_2_14);
+ }
+ }
+
+ SECTION("method eval")
+ {
+ Packet test_packet;
+ Cursor current_cursor;
+ SetByteJumpData(byte_jump, 1);
+
+ SECTION("Cursor not set correct for string_extract")
+ {
+ ByteJumpOption test_2(byte_jump);
+ REQUIRE((test_2.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Extract too much (1000000) bytes from in byte_extract")
+ {
+ uint8_t buff = 0;
+ byte_jump.string_convert_flag = 0;
+ byte_jump.bytes_to_extract = 1000000;
+ current_cursor.set("hello_world_long_name", &buff, 50);
+ ByteJumpOption test_3(byte_jump);
+ REQUIRE((test_3.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Cursor not set correct")
+ {
+ uint8_t buff = 0;
+ current_cursor.set("hello_world_long_name", &buff, 1);
+ byte_jump.string_convert_flag = 0;
+ byte_jump.from_beginning_flag = 0;
+ byte_jump.from_end_flag = 1;
+ byte_jump.bytes_to_extract = 1;
+ byte_jump.post_offset_var = 12;
+ ByteJumpOption test_4(byte_jump);
+ REQUIRE((test_4.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Match")
+ {
+ uint8_t buff = 0;
+ byte_jump.string_convert_flag = 0;
+ byte_jump.from_beginning_flag = 0;
+ byte_jump.from_end_flag = 0;
+ current_cursor.set("hello_world_long_name", &buff, 50);
+ ByteJumpOption test_5(byte_jump);
+ REQUIRE((test_5.eval(current_cursor, &test_packet)) == MATCH);
+
+ current_cursor.set("hello_world_long_name", (const uint8_t*)"hello", 5);
+ byte_jump.from_beginning_flag = 1;
+ byte_jump.bitmask_val = 2;
+ ByteJumpOption test_5_1(byte_jump);
+ REQUIRE((test_5_1.eval(current_cursor, &test_packet)) == MATCH);
+ }
+ }
+}
+
+TEST_CASE("ByteJumpModule test", "[ips_byte_jump]")
+{
+ ByteJumpModule module_jump;
+ ByteJumpData byte_jump;
+ SetByteJumpData(byte_jump, 1);
+
+ SECTION("method end")
+ {
+ std::string buff = "tmp";
+
+ SECTION("Undefined rule option for var")
+ {
+ module_jump.var = buff;
+ byte_jump.offset_var = -1;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Undefined rule option for offset_var")
+ {
+ module_jump.var.clear();
+ module_jump.post_var = buff;
+ byte_jump.post_offset_var = -1;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("From_beginning and from_end options together")
+ {
+ byte_jump.endianness = 0;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Number of bytes in \"bitmask\" value is greater than bytes to extract")
+ {
+ byte_jump.from_beginning_flag = 0;
+ byte_jump.bytes_to_extract = 0;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("byte_jump rule option cannot extract more \
+ than %d bytes without valid string prefix")
+ {
+ byte_jump.from_beginning_flag = 0;
+ byte_jump.bytes_to_extract = 5;
+ byte_jump.string_convert_flag = 0;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Case with returned value true")
+ {
+ byte_jump.from_beginning_flag = 0;
+ module_jump.data = byte_jump;
+ REQUIRE(module_jump.end("tmp", 0, nullptr) == true);
+ }
+ }
+
+ SECTION("method set")
+ {
+ Value value(false);
+
+ SECTION("All params incorrect")
+ {
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == false);
+ }
+
+ SECTION("Case param \"~count\"")
+ {
+ Parameter param("~count", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"~offset\"")
+ {
+ SECTION("Value doesn't have a str")
+ {
+ Parameter param("~offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("When value has a str")
+ {
+ Value value_tmp("123");
+ Parameter param("~offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value_tmp.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value_tmp, nullptr) == true);
+ }
+ }
+
+ SECTION("Case param \"relative\"")
+ {
+ Parameter param("relative", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Param \"from_beginning\" correct")
+ {
+ Parameter param("from_beginning", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"from_end\"")
+ {
+ Parameter param("from_end", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"align\"")
+ {
+ Parameter param("align", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"multiplier\"")
+ {
+ Parameter param("multiplier", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"post_offset\"")
+ {
+ SECTION("Value doesn't have a str")
+ {
+ Parameter param("post_offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("When value has a str")
+ {
+ Value value_tmp("123");
+ Parameter param("post_offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value_tmp.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value_tmp, nullptr) == true);
+ }
+ }
+
+ SECTION("Case param \"big\"")
+ {
+ Parameter param("big", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"little\"")
+ {
+ Parameter param("little", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"dce\"")
+ {
+ Parameter param("dce", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"string\"")
+ {
+ Parameter param("string", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"dec\"")
+ {
+ Parameter param("dec", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"hex\"")
+ {
+ Parameter param("hex", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"oct\"")
+ {
+ Parameter param("oct", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"bitmask\"")
+ {
+ Parameter param("bitmask", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_jump.set(nullptr, value, nullptr) == true);
+ }
+ }
+}
+
+#endif
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
-#include "profiler/profiler.h"
#include "utils/util.h"
#include "extract.h"
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#include "service_inspectors/dce_rpc/dce_common.h"
+#endif
+
using namespace snort;
using namespace std;
static THREAD_LOCAL ProfileStats byteMathPerfStats;
-struct ByteMathData
+struct ByteMathData : public ByteData
{
- uint32_t bytes_to_extract;
uint32_t rvalue;
- int32_t offset;
- uint32_t bitmask_val;
- char* result_name;
BM_Oper oper;
- bool relative_flag;
- bool string_convert_flag;
- uint8_t base;
- uint8_t endianess;
+ int8_t offset_var;
int8_t result_var;
int8_t rvalue_var;
- int8_t offset_var;
+ char* result_name;
};
class ByteMathOption : public IpsOption
{
public:
- ByteMathOption(const ByteMathData& c) : IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE),
- config(c) { }
+ ByteMathOption(const ByteMathData& c) :
+ IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE), config(c)
+ { }
~ByteMathOption() override
{ snort_free(config.result_name); }
private:
const ByteMathData config;
+ int calc(uint32_t& value, const uint32_t rvalue);
};
uint32_t ByteMathOption::hash() const
b += ((uint32_t) config.rvalue_var << 24 |
(uint32_t) config.offset_var << 16 |
(uint32_t) config.result_var << 8 |
- config.endianess);
+ config.endianness);
c += config.base;
mix(a,b,c);
bool ByteMathOption::operator==(const IpsOption& ips) const
{
- if ( !IpsOption::operator==(ips) )
+ if (!IpsOption::operator==(ips))
return false;
const ByteMathOption& rhs = (const ByteMathOption&)ips;
const ByteMathData* left = &config;
const ByteMathData* right = &rhs.config;
- if (( left->bytes_to_extract == right->bytes_to_extract) &&
- ( left->rvalue == right->rvalue) &&
- ( left->oper == right->oper) &&
- ( left->offset == right->offset) &&
- ( left->relative_flag == right->relative_flag) &&
- ( left->string_convert_flag == right->string_convert_flag) &&
- ( left->endianess == right->endianess) &&
- ( left->base == right->base) &&
- ( left->bitmask_val == right->bitmask_val) &&
- ( left->rvalue_var == right->rvalue_var) &&
- ( left->offset_var == right->offset_var) &&
+ if (( left->bytes_to_extract == right->bytes_to_extract) and
+ ( left->rvalue == right->rvalue) and
+ ( left->oper == right->oper) and
+ ( left->offset == right->offset) and
+ ( left->relative_flag == right->relative_flag) and
+ ( left->string_convert_flag == right->string_convert_flag) and
+ ( left->endianness == right->endianness) and
+ ( left->base == right->base) and
+ ( left->bitmask_val == right->bitmask_val) and
+ ( left->rvalue_var == right->rvalue_var) and
+ ( left->offset_var == right->offset_var) and
( left->result_var == right->result_var))
{
return true;
{
RuleProfile profile(byteMathPerfStats);
- if (p == nullptr)
- return NO_MATCH;
-
- const uint8_t* start = c.buffer();
- int dsize = c.size();
-
- const uint8_t* ptr = config.relative_flag ? c.start() : c.buffer();
- const uint8_t* end = start + dsize;
-
/* Get values from ips options variables, if present. */
uint32_t rvalue;
- if (config.rvalue_var >= 0 && config.rvalue_var < NUM_IPS_OPTIONS_VARS)
+ if (config.rvalue_var >= 0 and config.rvalue_var < NUM_IPS_OPTIONS_VARS)
{
GetVarValueByIndex(&rvalue, config.rvalue_var);
if (rvalue == 0 and config.oper == BM_DIVIDE)
rvalue = config.rvalue;
int32_t offset;
- if (config.offset_var >= 0 && config.offset_var < NUM_IPS_OPTIONS_VARS)
+ if (config.offset_var >= 0 and config.offset_var < NUM_IPS_OPTIONS_VARS)
{
// Rule options variables are kept as uint32_t,
// in order to support full range for unsigned options.
// Signed options do a cast to int32_t after getting the value.
- // The range limitation should be taken into consideration when writing a rule
- // with an option that is read from a variable.
+ // The range limitation should be taken into consideration when writing
+ // a rule with an option that is read from a variable.
uint32_t extract_offset;
GetVarValueByIndex(&extract_offset, config.offset_var);
offset = (int32_t)extract_offset;
else
offset = config.offset;
- ptr += offset;
+ ByteMathData extract_config = config;
+ extract_config.offset = offset;
- // check bounds
- if (ptr < start || ptr >= end)
- return NO_MATCH;
+ uint32_t value = 0;
+ int bytes_read = extract_data(extract_config, c, p, value);
- uint8_t endian = config.endianess;
- if (config.endianess == ENDIAN_FUNC)
- {
- if (!p->endianness ||
- !p->endianness->get_offset_endianness(ptr - p->data, endian))
- return NO_MATCH;
- }
+ if (bytes_read == NO_MATCH)
+ return NO_MATCH;
- // do the extraction
- uint32_t value;
+ if (calc(value, rvalue) == NO_MATCH)
+ return NO_MATCH;
- if (!config.string_convert_flag)
- {
- if (byte_extract(endian, config.bytes_to_extract, ptr, start, end, &value) < 0)
- return NO_MATCH;
- }
- else
- {
- if (string_extract(config.bytes_to_extract, config.base, ptr, start, end, &value) < 0)
- return NO_MATCH;
- }
+ SetVarValueByIndex(value, config.result_var);
- if (config.bitmask_val != 0)
- {
- uint32_t num_tailing_zeros_bitmask = getNumberTailingZerosInBitmask(config.bitmask_val);
- value = value & config.bitmask_val;
- if ( value && num_tailing_zeros_bitmask )
- {
- value = value >> num_tailing_zeros_bitmask;
- }
- }
+ return MATCH;
+}
+int ByteMathOption::calc(uint32_t& value, const uint32_t rvalue)
+{
// Note: all of the operations are done on uint32_t.
// If the rule isn't written correctly, there is a risk for wrap around.
switch (config.oper)
{
case BM_PLUS:
- if( value + rvalue < value )
+ if (value + rvalue < value)
{
return NO_MATCH;
}
break;
}
case BM_MINUS:
- if( value < rvalue )
+ if (value < rvalue)
{
return NO_MATCH;
}
break;
}
case BM_MULTIPLY:
- if ( value != 0 and rvalue != 0 and (((value * rvalue) / rvalue) != value) )
+ if (value != 0 and rvalue != 0 and
+ (((value * rvalue) / rvalue) != value))
{
return NO_MATCH;
}
value *= rvalue;
break;
}
- case BM_DIVIDE: value /= rvalue;
- break;
+ case BM_DIVIDE:
+ value /= rvalue;
+ break;
- case BM_LEFT_SHIFT: value <<= rvalue;
- break;
+ case BM_LEFT_SHIFT:
+ value <<= rvalue;
+ break;
- case BM_RIGHT_SHIFT: value >>= rvalue;
- break;
+ case BM_RIGHT_SHIFT:
+ value >>= rvalue;
+ break;
}
-
- SetVarValueByIndex(value, config.result_var);
-
return MATCH;
}
{
assert(value <= 1);
int endian[] = { ENDIAN_BIG, ENDIAN_LITTLE };
- set_byte_order(idx.endianess, endian[value], "byte_math");
+ set_byte_order(idx.endianness, endian[value], "byte_math");
}
//-------------------------------------------------------------------------
{ return DETECT; }
public:
- ByteMathData data = {};
+ ByteMathData data{};
string rvalue_var;
string off_var;
};
bool ByteMathModule::set(const char*, Value& v, SnortConfig*)
{
- if ( v.is("bytes") )
+ if (v.is("bytes"))
data.bytes_to_extract = v.get_uint8();
- else if ( v.is("oper") )
+ else if (v.is("oper"))
data.oper = (BM_Oper)v.get_uint8();
- else if ( v.is("rvalue") )
+ else if (v.is("rvalue"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
{
if (n == 0)
return false;
else
rvalue_var = v.get_string();
}
- else if ( v.is("offset") )
+ else if (v.is("offset"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
data.offset = n;
else
off_var = v.get_string();
}
- else if ( v.is("relative") )
+ else if (v.is("relative"))
data.relative_flag = true;
- else if ( v.is("dce") )
- set_byte_order(data.endianess, ENDIAN_FUNC, "byte_math");
+ else if (v.is("dce"))
+ set_byte_order(data.endianness, ENDIAN_FUNC, "byte_math");
- else if ( v.is("string") )
+ else if (v.is("string"))
{
data.string_convert_flag = true;
parse_base(v.get_uint8(), data);
}
- else if ( v.is("endian") )
+ else if (v.is("endian"))
parse_endian(v.get_uint8(), data);
- else if ( v.is("bitmask") )
+ else if (v.is("bitmask"))
data.bitmask_val = v.get_uint32();
- else if ( v.is("result") )
+ else if (v.is("result"))
data.result_name = snort_strdup(v.get_string());
else
return false;
}
- if ( ((data->oper == BM_LEFT_SHIFT) || (data->oper == BM_RIGHT_SHIFT)) &&
+ if (((data->oper == BM_LEFT_SHIFT) or (data->oper == BM_RIGHT_SHIFT)) and
(data->rvalue > 32))
{
ParseError("Number of bits in rvalue input [%u] should be less than 32 "
return false;
}
- if (((data->oper == BM_LEFT_SHIFT) || (data->oper == BM_RIGHT_SHIFT)) &&
+ if (((data->oper == BM_LEFT_SHIFT) or (data->oper == BM_RIGHT_SHIFT)) and
(data->bytes_to_extract > 4))
{
ParseError("for operators << and >> valid bytes_to_extract input range is"
return false;
}
- if (data->bytes_to_extract > MAX_BYTES_TO_GRAB && !data->string_convert_flag)
+ if (data->bytes_to_extract > MAX_BYTES_TO_GRAB and !data->string_convert_flag)
{
ParseError("byte_math rule option cannot extract more than %d bytes without valid"
" string prefix.", MAX_BYTES_TO_GRAB);
bool ByteMathModule::end(const char*, int, SnortConfig*)
{
- if ( rvalue_var.empty() )
+ if (rvalue_var.empty())
data.rvalue_var = IPS_OPTIONS_NO_VAR;
else
{
}
}
- if ( off_var.empty() )
+ if (off_var.empty())
data.offset_var = IPS_OPTIONS_NO_VAR;
else
{
}
}
- if ( !data.endianess )
- data.endianess = ENDIAN_BIG;
+ if (!data.endianness )
+ data.endianness = ENDIAN_BIG;
return ByteMathVerify(&data);
}
&byte_math_api.base,
nullptr
};
+
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
+#ifdef UNIT_TEST
+
+//-------------------------------------------------------------------------
+// helpers
+//-------------------------------------------------------------------------
+#define INITIALIZE(obj, bytes_to_extract_value, rvalue_value, offset_value, bitmask_val_value, \
+ result_name_value, oper_value, relative_flag_value, string_convert_flag_value, base_value, \
+ endianness_value, result_var_value, rvalue_var_value, offset_var_value) \
+ obj.base = base_value; \
+ obj.bitmask_val = bitmask_val_value; \
+ obj.bytes_to_extract = bytes_to_extract_value; \
+ obj.offset = offset_value; \
+ obj.endianness = endianness_value; \
+ obj.relative_flag = relative_flag_value; \
+ obj.string_convert_flag = string_convert_flag_value; \
+ obj.rvalue = rvalue_value; \
+ obj.oper = oper_value; \
+ obj.offset_var = offset_var_value; \
+ obj.result_var = result_var_value; \
+ obj.rvalue_var = rvalue_var_value; \
+ obj.result_name = result_name_value;
+
+class ByteMathDataMatcher
+ : public Catch::Matchers::Impl::MatcherBase<ByteMathData>
+{
+public:
+ ByteMathDataMatcher(const ByteMathData& value) : m_value(value) {}
+
+ bool match(ByteMathData const& rhs) const override
+ {
+ return ((m_value.bytes_to_extract == rhs.bytes_to_extract) and
+ (m_value.rvalue == rhs.rvalue) and (m_value.oper == rhs.oper) and
+ (m_value.offset == rhs.offset) and
+ (m_value.relative_flag == rhs.relative_flag) and
+ (m_value.string_convert_flag == rhs.string_convert_flag) and
+ (m_value.endianness == rhs.endianness) and
+ (m_value.base == rhs.base) and
+ (m_value.bitmask_val == rhs.bitmask_val) and
+ (m_value.rvalue_var == rhs.rvalue_var) and
+ (m_value.offset_var == rhs.offset_var) and
+ (m_value.result_var == rhs.result_var));
+ }
+
+ std::string describe() const override
+ {
+ std::ostringstream ss;
+ ss << "settings is equals to:\n";
+ ss << "bytes_to_extract : " << m_value.bytes_to_extract << ";\n";
+ ss << "rvalue : " << m_value.rvalue << ";\n";
+ ss << "oper : " << m_value.oper << ";\n";
+ ss << "offset : " << m_value.offset << ";\n";
+ ss << "relative_flag : " << m_value.relative_flag << ";\n";
+ ss << "string_convert_flag : " << m_value.string_convert_flag << ";\n";
+ ss << "endianness : " << m_value.endianness << ";\n";
+ ss << "base : " << m_value.base << ";\n";
+ ss << "bitmask_val : " << m_value.bitmask_val << ";\n";
+ ss << "rvalue_var : " << m_value.rvalue_var << ";\n";
+ ss << "offset_var : " << m_value.offset_var << ";\n";
+ ss << "result_var : " << m_value.result_var << ";\n";
+ return ss.str();
+ }
+
+private:
+ ByteMathData m_value;
+};
+
+ByteMathDataMatcher ByteMathDataEquals(const ByteMathData& value)
+{
+ return {value};
+}
+
+class SetBufferOptionHelper : public IpsOption
+{
+public:
+ SetBufferOptionHelper(const char* value)
+ : IpsOption(value, RULE_OPTION_TYPE_BUFFER_SET)
+ { }
+};
+
+//-------------------------------------------------------------------------
+// option tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ByteMathOption::operator== valid", "[ips_byte_math]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ char* lhs_name = new char[9];
+ strcpy(lhs_name, "test_lhs");
+ ByteMathData data_lhs;
+ INITIALIZE(data_lhs, 0, 25, 0, 0, lhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption lhs(data_lhs);
+
+ char* rhs_name = new char[9];
+ strcpy(rhs_name, "test_rhs");
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+
+ CHECK(lhs == rhs);
+}
+
+TEST_CASE("ByteMathOption::operator== invalid", "[ips_byte_math]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ char* lhs_name = new char[5];
+ strcpy(lhs_name, "test");
+ ByteMathData data_lhs;
+ INITIALIZE(data_lhs, 0, 25, 0, 0, lhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption lhs(data_lhs);
+
+ char* rhs_name = new char[5];
+ strcpy(rhs_name, "test");
+
+ SECTION("not equal to IpsOption object")
+ {
+ CHECK(lhs != set_buf);
+ delete[] rhs_name;
+ }
+ SECTION("all fields is different")
+ {
+ delete[] rhs_name;
+ rhs_name = new char[5];
+ strcpy(rhs_name, "unix");
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 2, 25, 2, 255, rhs_name, BM_MULTIPLY, 1,
+ 1, 8, ENDIAN_LITTLE, 1, 1, 1);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("bytes_to_grab is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 2, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("rvalue is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 15, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("offset is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 3, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("bitmask is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 255, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("result_name is different")
+ {
+ delete[] rhs_name;
+ rhs_name = new char[5];
+ strcpy(rhs_name, "unix");
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 255, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("operation is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_DIVIDE, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("relative_flag is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 1, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("string_convert_flag is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 1, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("base is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 8, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("endianness is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_LITTLE,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("result_var is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("rvalue_var is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, 1, IPS_OPTIONS_NO_VAR);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+ SECTION("offset_var is different")
+ {
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 0, 25, 0, 0, rhs_name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR, 0);
+ ByteMathOption rhs(data_rhs);
+ CHECK(lhs != rhs);
+ }
+}
+
+TEST_CASE("ByteMathOption::hash", "[ips_byte_math]")
+{
+ SetBufferOptionHelper set_buf("test");
+
+ char* lhs_name = new char[5];
+ strcpy(lhs_name, "test");
+ ByteMathData data_lhs;
+ INITIALIZE(data_lhs, 2, 25, 2, 255, lhs_name, BM_MULTIPLY, 1, 1, 8,
+ ENDIAN_LITTLE, 1, 1, 1);
+ ByteMathOption lhs(data_lhs);
+
+ SECTION("hash codes of any two equal objects are equal")
+ {
+ char* rhs_name = new char[5];
+ strcpy(rhs_name, "test");
+ ByteMathData data_rhs;
+ INITIALIZE(data_rhs, 2, 25, 2, 255, rhs_name, BM_MULTIPLY, 1, 1, 8,
+ ENDIAN_LITTLE, 1, 1, 1);
+ ByteMathOption rhs(data_rhs);
+
+ CHECK(lhs.hash() == rhs.hash());
+ }
+}
+
+TEST_CASE("ByteMathOption::eval valid", "[ips_byte_math]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 12345";
+ p.dsize = 11;
+ Cursor c(&p);
+
+ for (unsigned i = 0; i < NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ SetVarValueByIndex(0, i);
+ }
+ ClearIpsOptionsVars();
+
+ char* name = new char[5];
+ strcpy(name, "test");
+
+ SECTION("1 byte read, all off, operation \"+\", rvalue 1")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 0, 0, name, BM_PLUS, 0, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 77);
+ }
+ SECTION("1 byte read, offset 3, operation \"*\", rvalue 2")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 2, 3, 0, name, BM_MULTIPLY, 0, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 202);
+ }
+ SECTION("1 byte read, offset 3, relative, cursor 3, operation \"-\", rvalue 3")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 3, 3, 0, name, BM_MINUS, 1, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ c.set_pos(3);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 46);
+ }
+ SECTION("cursor 3, 1 byte read, offset -3, relative, operation \"/\", rvalue 4")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 4, -3, 0, name, BM_DIVIDE, 1, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ c.set_pos(3);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 19);
+ }
+ SECTION("1 byte read, offset 6, string conversion, base 10, operation \"+\", rvalue 4")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 4, 6, 0, name, BM_PLUS, 0, 1, 10,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 5);
+ }
+ SECTION("1 byte read, offset 6, string conversion, base 10, operation \"<<\", rvalue 2")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 2, 6, 0, name, BM_LEFT_SHIFT, 0, 1, 10,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 4);
+ }
+ SECTION("3 byte read, offset 6, string conversion, base 10, operation \">>\", rvalue 1")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 3, 1, 6, 0, name, BM_RIGHT_SHIFT, 0, 1, 10,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 61);
+ }
+ SECTION("2 bytes read, operation \">>\", result_var = 0, rvalue_var = 1")
+ {
+ SetVarValueByIndex(3, 1);
+ ByteMathData data;
+ INITIALIZE(data, 2, 0, 0, 0, name, BM_RIGHT_SHIFT, 0,
+ 0, 0, ENDIAN_BIG, 0, 1, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 2445);
+ }
+ SECTION("1 byte read, operation \"<<\", offset_var = 0, result_var = 1")
+ {
+ SetVarValueByIndex(1, 0);
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 0, 0, name, BM_LEFT_SHIFT,
+ 0, 0, 0, ENDIAN_BIG, 1, IPS_OPTIONS_NO_VAR, 0);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 1);
+ CHECK(res == 222);
+ }
+}
+
+TEST_CASE("ByteMathOption::eval invalid", "[ips_byte_math]")
+{
+ Packet p;
+ p.data = (const uint8_t*)"Lorem 9876";
+ p.dsize = 11;
+ Cursor c(&p);
+
+ for (unsigned i = 0; i < NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ SetVarValueByIndex(0, i);
+ }
+ ClearIpsOptionsVars();
+
+ char* name = new char[5];
+ strcpy(name, "test");
+
+ SECTION("rvalue_variable didn't exist")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 0, 0, name, BM_PLUS, 0,
+ 0, 0, ENDIAN_BIG, 0, 1, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 76);
+ }
+ SECTION("offset_variable didn't exist")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 1, 0, name, BM_PLUS, 0,
+ 0, 0, ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, 1);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 77);
+ }
+ SECTION("rvalue_variable_index > NUM_IPS_OPTIONS_VARS")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 0, 0, name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ 0, NUM_IPS_OPTIONS_VARS + 1, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 77);
+ }
+ SECTION("offset_variable_index > NUM_IPS_OPTIONS_VARS")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 1, 1, 0, name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ 0, IPS_OPTIONS_NO_VAR, NUM_IPS_OPTIONS_VARS + 1);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::MATCH);
+ uint32_t res = 0;
+ GetVarValueByIndex(&res, 0);
+ CHECK(res == 112);
+ }
+ SECTION("get negative number with MINUS")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 256, 0, 0, name, BM_MINUS, 0, 0, 0, ENDIAN_BIG,
+ 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::NO_MATCH);
+ }
+ SECTION("out of bounds of uint32_t, PLUS")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 4294967295, 0, 0, name, BM_PLUS, 0, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::NO_MATCH);
+ }
+ SECTION("out of bounds of uint32_t, MULTIPLY")
+ {
+ ByteMathData data;
+ INITIALIZE(data, 1, 2147483647, 0, 0, name, BM_MULTIPLY, 0, 0, 0,
+ ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::NO_MATCH);
+ }
+ SECTION("dividing on zero in rvalue_var")
+ {
+ SetVarValueByIndex(1, 0);
+ ByteMathData data;
+ INITIALIZE(data, 1, 0, 0, 0, name, BM_DIVIDE, 0,
+ 0, 0, ENDIAN_BIG, 0, 1, IPS_OPTIONS_NO_VAR);
+ ByteMathOption opt(data);
+ CHECK(opt.eval(c, &p) == IpsOption::NO_MATCH);
+ }
+}
+
+//-------------------------------------------------------------------------
+// module tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ByteMathModule::begin", "[ips_byte_math]")
+{
+ ByteMathModule obj;
+ SECTION("test of \"begin\" method")
+ {
+ CHECK(obj.begin(nullptr, 0, nullptr));
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+}
+
+TEST_CASE("ByteMathModule::end", "[ips_byte_math]")
+{
+ ByteMathModule obj;
+
+ obj.begin(nullptr, 0, nullptr);
+
+ Value v_name("test");
+ Parameter p_name{"result", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable to store the result"};
+ v_name.set(&p_name);
+ obj.set(nullptr, v_name, nullptr);
+
+ Value v_bytes(4.0);
+ Parameter p_bytes{"bytes", Parameter::PT_INT, "1:10", nullptr,
+ "number of bytes to pick up from the buffer"};
+ v_bytes.set(&p_bytes);
+ obj.set(nullptr, v_bytes, nullptr);
+
+ Value v_operation(0.0);
+ Parameter p_operation{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>",
+ nullptr, "mathematical operation to perform"};
+ v_operation.set(&p_operation);
+ obj.set(nullptr, v_operation, nullptr);
+
+ char* name = new char[5];
+ strcpy(name, "test");
+
+ SECTION("without variables")
+ {
+ Value v_rvalue("7");
+ Parameter p_rvalue{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v_rvalue.set(&p_rvalue);
+ obj.set(nullptr, v_rvalue, nullptr);
+
+ CHECK(obj.end(nullptr, 0, nullptr));
+
+ ByteMathData expected;
+ INITIALIZE(expected, 4, 7, 0, 0, name, BM_PLUS, 0, 0, 0, ENDIAN_BIG,
+ 0, IPS_OPTIONS_NO_VAR, IPS_OPTIONS_NO_VAR);
+
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("with rvalue var")
+ {
+ ClearIpsOptionsVars();
+ int8_t var_idx = AddVarNameToList("rvalue_test_var");
+ SetVarValueByIndex(3, var_idx);
+
+ Value v_rvalue("rvalue_test_var");
+ Parameter p_rvalue{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v_rvalue.set(&p_rvalue);
+ obj.set(nullptr, v_rvalue, nullptr);
+
+ CHECK(obj.end(nullptr, 0, nullptr));
+
+ ByteMathData expected;
+ INITIALIZE(expected, 4, 0, 0, 0, name, BM_PLUS, 0, 0, 0,
+ ENDIAN_BIG, 0, var_idx, IPS_OPTIONS_NO_VAR);
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("with offset variable")
+ {
+ ClearIpsOptionsVars();
+ int8_t var_idx = AddVarNameToList("offset_test_var");
+ SetVarValueByIndex(3, var_idx);
+
+ Value v_offvalue("offset_test_var");
+ Parameter p_offvalue{"offset", Parameter::PT_STRING, nullptr, nullptr,
+ "number of bytes into the buffer to start processing"};
+ v_offvalue.set(&p_offvalue);
+ obj.set(nullptr, v_offvalue, nullptr);
+
+ CHECK(obj.end(nullptr, 0, nullptr));
+
+ ByteMathData expected;
+ INITIALIZE(expected, 4, 0, 0, 0, name, BM_PLUS, 0, 0,
+ 0, ENDIAN_BIG, 0, IPS_OPTIONS_NO_VAR, var_idx);
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("rvalue var doesn't exist")
+ {
+ ClearIpsOptionsVars();
+ Value v_rvalue("rvalue_test_var");
+ Parameter p_rvalue{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v_rvalue.set(&p_rvalue);
+ obj.set(nullptr, v_rvalue, nullptr);
+
+ CHECK(!(obj.end(nullptr, 0, nullptr)));
+ }
+ SECTION("offset var doesn't exist")
+ {
+ ClearIpsOptionsVars();
+ Value v_offvalue("offset_test_var");
+ Parameter p_offvalue{"offset", Parameter::PT_STRING, nullptr, nullptr,
+ "number of bytes into the buffer to start processing"};
+ v_offvalue.set(&p_offvalue);
+ obj.set(nullptr, v_offvalue, nullptr);
+
+ CHECK(!(obj.end(nullptr, 0, nullptr)));
+ }
+
+ delete[] obj.data.result_name;
+ delete[] name;
+}
+
+TEST_CASE("Test of byte_math_ctor", "[ips_byte_math]")
+{
+ ClearIpsOptionsVars();
+
+ std::string name = "test";
+ for (unsigned i = 0; i <= NUM_IPS_OPTIONS_VARS; ++i)
+ {
+ ByteMathModule obj;
+ obj.begin(nullptr, 0, nullptr);
+ Value v((name + std::to_string(i)).c_str());
+ Parameter p{"result", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable to store the result"};
+ v.set(&p);
+ obj.set(nullptr, v, nullptr);
+ if (i < NUM_IPS_OPTIONS_VARS)
+ {
+ IpsOption* res = byte_math_ctor(&obj, nullptr);
+ delete res;
+ }
+ else
+ {
+ IpsOption* res_null = byte_math_ctor(&obj, nullptr);
+ CHECK(res_null == nullptr);
+ delete[] obj.data.result_name;
+ }
+ }
+}
+
+TEST_CASE("ByteMathModule::set valid", "[ips_byte_math]")
+{
+ ByteMathModule obj;
+ obj.begin(nullptr, 0, nullptr);
+
+ SECTION("set bytes")
+ {
+ Value v(4.0);
+ Parameter p{"bytes", Parameter::PT_INT, "1:10", nullptr,
+ "number of bytes to pick up from the buffer"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 4, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set offset")
+ {
+ Value v("3");
+ Parameter p{"offset", Parameter::PT_STRING, nullptr, nullptr,
+ "number of bytes into the buffer to start processing"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 3, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \"+\"")
+ {
+ Value v(0.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \"-\"")
+ {
+ Value v(1.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_MINUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \"*\"")
+ {
+ Value v(2.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_MULTIPLY, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \"/\"")
+ {
+ Value v(3.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_DIVIDE, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \"<<\"")
+ {
+ Value v(4.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_LEFT_SHIFT, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set option \">>\"")
+ {
+ Value v(5.0);
+ Parameter p{"oper", Parameter::PT_ENUM, "+|-|*|/|<<|>>", nullptr,
+ "mathematical operation to perform"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_RIGHT_SHIFT, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set rvalue num")
+ {
+ Value v("21");
+ Parameter p{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 21, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set result")
+ {
+ Value v("res_name");
+ Parameter p{"result", Parameter::PT_STRING, nullptr, nullptr,
+ "name of the variable to store the result"};
+ v.set(&p);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data.result_name, Catch::Matchers::Equals("res_name"));
+ }
+ SECTION("set relative")
+ {
+ Value v(true);
+ Parameter p{"relative", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "offset from cursor instead of start of buffer"};
+ v.set(&p);
+ obj.set(nullptr, v, nullptr);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 1, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set endianness \"big\"")
+ {
+ Value v(0.0);
+ Parameter p{"endian", Parameter::PT_ENUM, "big|little", nullptr,
+ "specify big/little endian"};
+ v.set(&p);
+ obj.set(nullptr, v, nullptr);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, ENDIAN_BIG, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set endianness \"little\"")
+ {
+ Value v(1.0);
+ Parameter p{"endian", Parameter::PT_ENUM, "big|little", nullptr,
+ "specify big/little endian"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, ENDIAN_LITTLE, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set dce")
+ {
+ Value v(true);
+ Parameter p{"dce", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "dcerpc2 determines endianness"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, ENDIAN_FUNC, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set string, hex base")
+ {
+ Value v(0.0);
+ Parameter p{"string", Parameter::PT_ENUM, "hex|dec|oct", nullptr,
+ "convert extracted string to dec/hex/oct"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 1, 16, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set string, dec base")
+ {
+ Value v(1.0);
+ Parameter p{"string", Parameter::PT_ENUM, "hex|dec|oct", nullptr,
+ "convert extracted string to dec/hex/oct"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 1, 10, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set string, oct base")
+ {
+ Value v(2.0);
+ Parameter p{"string", Parameter::PT_ENUM, "hex|dec|oct", nullptr,
+ "convert extracted string to dec/hex/oct"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 1, 8, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("set bitmask")
+ {
+ Value v(1023.0);
+ Parameter p{"bitmask", Parameter::PT_INT, "0x1:0xFFFFFFFF", nullptr,
+ "applies as bitwise AND to the extracted value before storage in 'name'"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 1023, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("rvalue as variable")
+ {
+ Value v("r_test_var");
+ Parameter p{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v.set(&p);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK(strcmp(obj.rvalue_var.c_str(), "r_test_var") == 0);
+ }
+ SECTION("offset as variable")
+ {
+ Value v("off_test_var");
+ Parameter p{"offset", Parameter::PT_STRING, nullptr, nullptr,
+ "number of bytes into the buffer to start processing"};
+ v.set(&p);
+
+ CHECK(obj.set(nullptr, v, nullptr));
+ CHECK(strcmp(obj.off_var.c_str(), "off_test_var") == 0);
+ }
+
+ delete[] obj.data.result_name;
+}
+
+TEST_CASE("ByteMathModule::set invalid", "[ips_byte_math]")
+{
+ ByteMathModule obj;
+ obj.begin(nullptr, 0, nullptr);
+
+ SECTION("invalid parameter")
+ {
+ Value v(1023.0);
+ Parameter p{"error", Parameter::PT_INT, "nan", nullptr,
+ "not an option"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(!obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+ SECTION("rvalue = 0")
+ {
+ Value v("0");
+ Parameter p{"rvalue", Parameter::PT_STRING, nullptr, nullptr,
+ "value to use mathematical operation against"};
+ v.set(&p);
+ ByteMathData expected;
+ INITIALIZE(expected, 0, 0, 0, 0, 0, BM_PLUS, 0, 0, 0, 0, 0, 0, 0);
+
+ CHECK(!obj.set(nullptr, v, nullptr));
+ CHECK_THAT(obj.data, ByteMathDataEquals(expected));
+ }
+}
+//-------------------------------------------------------------------------
+// api tests
+//-------------------------------------------------------------------------
+
+TEST_CASE("ByteMathVerify valid", "[ips_byte_math]")
+{
+ ByteMathData obj;
+ char name[] = "test";
+
+ SECTION("Minimum values, no string conversion")
+ {
+ INITIALIZE(obj, 1, 0, -65535, 0, name, BM_PLUS, 1, 0, 0, 0, 0, 0, 0);
+ CHECK(ByteMathVerify(&obj));
+ }
+ SECTION("Maximum values, no string conversion")
+ {
+ INITIALIZE(obj, MAX_BYTES_TO_GRAB, 2147483647, 65535, 0xFFFFFFFF, name, BM_PLUS, 1, 0,
+ 0, ENDIAN_FUNC, NUM_IPS_OPTIONS_VARS, NUM_IPS_OPTIONS_VARS, NUM_IPS_OPTIONS_VARS);
+ CHECK(ByteMathVerify(&obj));
+ }
+ SECTION("Minimum values, with string conversion")
+ {
+ INITIALIZE(obj, 1, 0, -65535, 0, name, BM_PLUS, 1, 1, 8, 0, 0, 0, 0);
+ CHECK(ByteMathVerify(&obj));
+ }
+ SECTION("Maximum values, with string conversion")
+ {
+ INITIALIZE(obj, PARSELEN, 2147483647, 65535, 0xFFFFFFFF, name, BM_PLUS, 1, 1, 16,
+ ENDIAN_FUNC, NUM_IPS_OPTIONS_VARS, NUM_IPS_OPTIONS_VARS, NUM_IPS_OPTIONS_VARS);
+ CHECK(ByteMathVerify(&obj));
+ }
+}
+
+TEST_CASE("ByteMathVerify invalid", "[ips_byte_math]")
+{
+ char* name = new char[5];
+ strcpy(name, "test");
+ ByteMathData obj;
+ INITIALIZE(obj, 1, 9, 25, 1023, name, BM_PLUS, 1, 0, 0, 0, 0, 0, 0);
+
+ SECTION("name existence check")
+ {
+ obj.result_name = nullptr;
+
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ SECTION("name not numeric check")
+ {
+ delete[] name;
+ name = new char[5];
+ strcpy(name, "6in4");
+ obj.result_name = name;
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ SECTION("shift > 32 checks")
+ {
+ obj.rvalue = 33;
+
+ obj.oper = BM_LEFT_SHIFT;
+ CHECK((!ByteMathVerify(&obj)));
+
+ obj.oper = BM_RIGHT_SHIFT;
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ SECTION("shift and bytes_to_extract > 4 checks")
+ {
+ obj.bytes_to_extract = MAX_BYTES_TO_GRAB + 1;
+
+ obj.oper = BM_LEFT_SHIFT;
+ CHECK((!ByteMathVerify(&obj)));
+
+ obj.oper = BM_RIGHT_SHIFT;
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ SECTION("no string conversion and bytes_to_extract > 4 checks")
+ {
+ obj.bytes_to_extract = MAX_BYTES_TO_GRAB + 1;
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ SECTION("bitmask checks")
+ {
+ obj.bytes_to_extract = 2;
+ obj.bitmask_val = 1048575;
+ CHECK((!ByteMathVerify(&obj)));
+ }
+ delete[] name;
+}
+
+#endif
#include "protocols/packet.h"
#include "utils/util.h"
+#ifdef UNIT_TEST
+#include <climits>
+#include "catch/snort_catch.h"
+#endif
+
#include "extract.h"
using namespace snort;
CHECK_XOR
};
-struct ByteTestData
+struct ByteTestData : public ByteData
{
- uint32_t bytes_to_compare;
uint32_t cmp_value;
ByteTestOper opcode;
- int32_t offset;
bool not_flag;
- bool relative_flag;
- bool data_string_convert_flag;
- uint8_t endianness;
- uint32_t base;
- uint32_t bitmask_val;
int8_t cmp_value_var;
int8_t offset_var;
};
// static functions
// -----------------------------------------------------------------------------
-static inline bool byte_test_check(ByteTestOper op, uint32_t val, uint32_t cmp, bool not_flag)
+static inline bool byte_test_check(ByteTestOper op, uint32_t val, uint32_t cmp,
+ bool not_flag)
{
bool success = false;
switch ( op )
{
- case CHECK_LT:
- success = (val < cmp);
- break;
-
case CHECK_EQ:
success = (val == cmp);
break;
- case CHECK_GT:
- success = (val > cmp);
+ case CHECK_LT:
+ success = (val < cmp);
break;
- case CHECK_AND:
- success = ((val & cmp) > 0);
+ case CHECK_GT:
+ success = (val > cmp);
break;
- case CHECK_XOR:
- success = ((val ^ cmp) > 0);
+ case CHECK_LTE:
+ success = (val <= cmp);
break;
case CHECK_GTE:
success = (val >= cmp);
break;
- case CHECK_LTE:
- success = (val <= cmp);
+ case CHECK_AND:
+ success = ((val & cmp) > 0);
+ break;
+
+ case CHECK_XOR:
+ success = ((val ^ cmp) > 0);
break;
}
- if ( not_flag )
+ if (not_flag)
{
success = !success;
}
class ByteTestOption : public IpsOption
{
public:
- ByteTestOption(const ByteTestData& c) : IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE)
- { config = c; }
+ ByteTestOption(const ByteTestData& c) : IpsOption(s_name,
+ RULE_OPTION_TYPE_BUFFER_USE), config(c)
+ { }
uint32_t hash() const override;
bool operator==(const IpsOption&) const override;
uint32_t ByteTestOption::hash() const
{
- uint32_t a = config.bytes_to_compare;
+ uint32_t a = config.bytes_to_extract;
uint32_t b = config.cmp_value;
uint32_t c = config.opcode;
a += config.offset;
b += config.not_flag ? (1 << 24) : 0;
b += config.relative_flag ? (1 << 16) : 0;
- b += config.data_string_convert_flag ? (1 << 8) : 0;
+ b += config.string_convert_flag ? (1 << 8) : 0;
b += config.endianness;
c += config.base;
bool ByteTestOption::operator==(const IpsOption& ips) const
{
- if ( !IpsOption::operator==(ips) )
+ if (!IpsOption::operator==(ips))
return false;
const ByteTestOption& rhs = (const ByteTestOption&)ips;
const ByteTestData* left = &config;
const ByteTestData* right = &rhs.config;
- if (( left->bytes_to_compare == right->bytes_to_compare) &&
- ( left->cmp_value == right->cmp_value) &&
- ( left->opcode == right->opcode) &&
- ( left->offset == right->offset) &&
- ( left->not_flag == right->not_flag) &&
- ( left->relative_flag == right->relative_flag) &&
- ( left->data_string_convert_flag == right->data_string_convert_flag) &&
- ( left->endianness == right->endianness) &&
- ( left->base == right->base) &&
- ( left->cmp_value_var == right->cmp_value_var) &&
- ( left->offset_var == right->offset_var) &&
+ if (( left->bytes_to_extract == right->bytes_to_extract) and
+ ( left->cmp_value == right->cmp_value) and
+ ( left->opcode == right->opcode) and
+ ( left->offset == right->offset) and
+ ( left->not_flag == right->not_flag) and
+ ( left->relative_flag == right->relative_flag) and
+ ( left->string_convert_flag == right->string_convert_flag) and
+ ( left->endianness == right->endianness) and
+ ( left->base == right->base) and
+ ( left->cmp_value_var == right->cmp_value_var) and
+ ( left->offset_var == right->offset_var) and
( left->bitmask_val == right->bitmask_val))
{
return true;
uint32_t cmp_value = 0;
// Get values from byte_extract variables, if present.
- if (btd->cmp_value_var >= 0 && btd->cmp_value_var < NUM_IPS_OPTIONS_VARS)
+ if (btd->cmp_value_var >= 0 and btd->cmp_value_var < NUM_IPS_OPTIONS_VARS)
{
uint32_t val;
GetVarValueByIndex(&val, btd->cmp_value_var);
int offset = 0;
- if (btd->offset_var >= 0 && btd->offset_var < NUM_IPS_OPTIONS_VARS)
+ if (btd->offset_var >= 0 and btd->offset_var < NUM_IPS_OPTIONS_VARS)
{
uint32_t val;
GetVarValueByIndex(&val, btd->offset_var);
else
offset = btd->offset;
- const uint8_t* start_ptr = btd->relative_flag ? c.start() : c.buffer();
- start_ptr += offset;
+ unsigned len = btd->relative_flag ? c.length() : c.size();
+ if (len > btd->bytes_to_extract)
+ len = btd->bytes_to_extract;
- uint8_t endian = btd->endianness;
- if (endian == ENDIAN_FUNC)
- {
- if (!p->endianness ||
- !p->endianness->get_offset_endianness(start_ptr - p->data, endian))
- return NO_MATCH;
- }
+ ByteTestData extract_config = *btd;
+ extract_config.bytes_to_extract = len;
+ extract_config.offset = offset;
uint32_t value = 0;
+ int32_t payload_bytes_grabbed = extract_data(extract_config, c, p, value);
- if (!btd->data_string_convert_flag)
- {
- if ( byte_extract(
- endian, btd->bytes_to_compare,
- start_ptr, c.buffer(), c.endo(), &value))
- return NO_MATCH;
- }
- else
- {
- unsigned len = btd->relative_flag ? c.length() : c.size();
-
- if ( len > btd->bytes_to_compare )
- len = btd->bytes_to_compare;
+ if (payload_bytes_grabbed == NO_MATCH)
+ return NO_MATCH;
- int payload_bytes_grabbed = string_extract(
- len, btd->base, start_ptr, c.buffer(), c.endo(), &value);
-
- if ( payload_bytes_grabbed < 0 )
- {
- return NO_MATCH;
- }
- }
-
- if (btd->bitmask_val != 0 )
- {
- uint32_t num_tailing_zeros_bitmask = getNumberTailingZerosInBitmask(btd->bitmask_val);
- value = value & btd->bitmask_val;
- if ( value && num_tailing_zeros_bitmask )
- {
- value = value >> num_tailing_zeros_bitmask;
- }
- }
-
- if ( byte_test_check(btd->opcode, value, cmp_value, btd->not_flag) )
+ if (byte_test_check(btd->opcode, value, cmp_value, btd->not_flag))
return MATCH;
return NO_MATCH;
cptr++;
}
- if (idx.not_flag && strlen(cptr) == 0)
+ if (idx.not_flag and strlen(cptr) == 0)
{
idx.opcode = CHECK_EQ;
}
"variable name or value to test the converted result against" },
{ "~offset", Parameter::PT_STRING, nullptr, nullptr,
- "variable name or number of bytes into the payload to start processing" },
+ "variable name or number of bytes into the payload to start processing"},
{ "relative", Parameter::PT_IMPLIED, nullptr, nullptr,
"offset from cursor instead of start of buffer" },
bool ByteTestModule::end(const char*, int, SnortConfig*)
{
- if ( off_var.empty() )
+ if (off_var.empty())
data.offset_var = IPS_OPTIONS_NO_VAR;
else
{
return false;
}
}
- if ( cmp_var.empty() )
+ if (cmp_var.empty())
data.cmp_value_var = IPS_OPTIONS_NO_VAR;
else
{
return false;
}
}
- if ( !data.endianness )
+ if (!data.endianness)
data.endianness = ENDIAN_BIG;
- if (numBytesInBitmask(data.bitmask_val) > data.bytes_to_compare)
+ if (numBytesInBitmask(data.bitmask_val) > data.bytes_to_extract)
{
- ParseError("Number of bytes in \"bitmask\" value is greater than bytes to extract.");
+ ParseError("Number of bytes in \"bitmask\" value is greater " \
+ "than bytes to extract.");
return false;
}
bool ByteTestModule::set(const char*, Value& v, SnortConfig*)
{
- if ( v.is("~count") )
- data.bytes_to_compare = v.get_uint8();
+ if (v.is("~count"))
+ data.bytes_to_extract = v.get_uint8();
- else if ( v.is("~operator") )
+ else if (v.is("~operator"))
parse_operator(v.get_string(), data);
- else if ( v.is("~compare") )
+ else if (v.is("~compare"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
data.cmp_value = n;
else
cmp_var = v.get_string();
}
- else if ( v.is("~offset") )
+ else if (v.is("~offset"))
{
long n;
- if ( v.strtol(n) )
+ if (v.strtol(n))
data.offset = n;
else
off_var = v.get_string();
}
- else if ( v.is("relative") )
+ else if (v.is("relative"))
data.relative_flag = true;
- else if ( v.is("big") )
+ else if (v.is("big"))
set_byte_order(data.endianness, ENDIAN_BIG, "byte_test");
- else if ( v.is("little") )
+ else if (v.is("little"))
set_byte_order(data.endianness, ENDIAN_LITTLE, "byte_test");
- else if ( v.is("dce") )
+ else if (v.is("dce"))
set_byte_order(data.endianness, ENDIAN_FUNC, "byte_test");
- else if ( v.is("string") )
+ else if (v.is("string"))
{
- data.data_string_convert_flag = true;
+ data.string_convert_flag = true;
data.base = 10;
}
- else if ( v.is("dec") )
+ else if (v.is("dec"))
data.base = 10;
- else if ( v.is("hex") )
+ else if (v.is("hex"))
data.base = 16;
- else if ( v.is("oct") )
+ else if (v.is("oct"))
data.base = 8;
- else if ( v.is("bitmask") )
+ else if (v.is("bitmask"))
data.bitmask_val = v.get_uint32();
else
nullptr
};
+//-------------------------------------------------------------------------
+// UNIT TESTS
+//-------------------------------------------------------------------------
+#ifdef UNIT_TEST
+#include <climits>
+
+#include "catch/snort_catch.h"
+
+#define NO_MATCH snort::IpsOption::EvalStatus::NO_MATCH
+#define MATCH snort::IpsOption::EvalStatus::MATCH
+
+void SetByteTestData(ByteTestData &byte_test, int value, ByteTestOper code = CHECK_EQ)
+{
+ byte_test.bytes_to_extract = value;
+ byte_test.cmp_value = value;
+ byte_test.opcode = code;
+ byte_test.offset = value;
+ byte_test.not_flag = value;
+ byte_test.relative_flag = value;
+ byte_test.string_convert_flag = value;
+ byte_test.endianness = value;
+ byte_test.base = value;
+ byte_test.bitmask_val = value;
+ byte_test.cmp_value_var = value;
+ byte_test.offset_var = value;
+}
+
+void SetByteTestDataMax(ByteTestData& byte_test)
+{
+ byte_test.bytes_to_extract = UINT_MAX;
+ byte_test.cmp_value = UINT_MAX;
+ byte_test.opcode = CHECK_XOR;
+ byte_test.offset = INT_MAX;
+ byte_test.not_flag = true;
+ byte_test.relative_flag = true;
+ byte_test.string_convert_flag = true;
+ byte_test.endianness = UCHAR_MAX;
+ byte_test.base = UINT_MAX;
+ byte_test.bitmask_val = UINT_MAX;
+ byte_test.cmp_value_var = CHAR_MAX;
+ byte_test.offset_var = CHAR_MAX;
+}
+
+class StubIpsOption : public IpsOption
+{
+public:
+ StubIpsOption(const char* name, option_type_t option_type) :
+ IpsOption(name, option_type)
+ { };
+
+};
+
+class StubEndianness : public Endianness
+{
+public:
+ StubEndianness() = default;
+ virtual bool get_offset_endianness(int32_t, uint8_t& ) override
+ { return false; }
+};
+
+TEST_CASE("byte_test_check test", "[ips_byte_test]")
+{
+ SECTION("Incorrect ByteTestOper, other data correct")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(7), 1, 1, 0) == false);
+ }
+
+ SECTION("Incorrect ByteTestOper, true not_flag")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(7), 1, 1, 1) == true);
+ }
+
+ SECTION("CHECK_EQ both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(0), 1, 1, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(0), 1, 2, 0) == false);
+ }
+
+ SECTION("CHECK_LT both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(1), 1, 2, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(1), 4, 1, 0) == false);
+ }
+
+ SECTION("CHECK_GT both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(2), 2, 1, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(2), 1, 4, 0) == false);
+ }
+
+ SECTION("CHECK_LTE both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(3), 0, 1, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(3), 4, 1, 0) == false);
+ }
+
+ SECTION("CHECK_GTE both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(4), 1, 0, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(4), 0, 4, 0) == false);
+ }
+
+ SECTION("CHECK_AND for bites both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(5), 1, 1, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(5), 1, 0, 0) == false);
+ }
+
+ SECTION("CHECK_XOR for bites both true && false situation")
+ {
+ REQUIRE(byte_test_check(ByteTestOper(6), 1, 0, 0) == true);
+ REQUIRE(byte_test_check(ByteTestOper(6), 1, 1, 0) == false);
+ }
+}
+
+TEST_CASE("ByteTestOption test", "[ips_byte_test]")
+{
+ ByteTestData byte_test;
+ SetByteTestData(byte_test, 1);
+ snort::IpsOption::set_buffer("hello_world");
+
+ SECTION("method hash")
+ {
+ ByteTestOption hash_test(byte_test);
+ ByteTestOption hash_test_equal(byte_test);
+
+ SECTION("Testing hash with very low values")
+ {
+ SECTION("Hash has same source")
+ {
+ CHECK(hash_test.hash() == hash_test_equal.hash());
+ }
+
+ SECTION("Compare hash from different source")
+ {
+ SetByteTestData(byte_test, 4);
+ ByteTestOption hash_test_diff(byte_test);
+ CHECK(hash_test.hash() != hash_test_diff.hash());
+ }
+ }
+
+ SECTION("Testing hash with maximum values")
+ {
+ SetByteTestDataMax(byte_test);
+ ByteTestOption hash_test_max(byte_test);
+ ByteTestOption hash_test_equal_max(byte_test);
+
+ SECTION("Hash has same source")
+ {
+ CHECK(hash_test_max.hash() == hash_test_equal_max.hash());
+ }
+
+ SECTION("Testing hash with maximum values from different source")
+ {
+ SetByteTestDataMax(byte_test);
+ ByteTestOption hash_test_max(byte_test);
+ CHECK(hash_test.hash() != hash_test_max.hash());
+ }
+ }
+ }
+
+ SECTION("operator ==")
+ {
+ ByteTestOption test(byte_test);
+
+ SECTION("Compare IpsOptions with different names")
+ {
+ StubIpsOption case_diff_name("not_hello_world",
+ option_type_t::RULE_OPTION_TYPE_BUFFER_USE);
+ REQUIRE(test != case_diff_name);
+ }
+
+ SECTION("Compare between equals objects")
+ {
+ ByteTestOption test_1(byte_test);
+ REQUIRE(test == test_1);
+ }
+
+ SECTION("byte_to_compare is different")
+ {
+ byte_test.bytes_to_extract = 2;
+ ByteTestOption test_2_1(byte_test);
+ REQUIRE(test != test_2_1);
+ }
+
+ SECTION("cmp_value is different")
+ {
+ byte_test.cmp_value = 2;
+ ByteTestOption test_2_2(byte_test);
+ REQUIRE(test != test_2_2);
+ }
+
+ SECTION("cmp_value is different")
+ {
+ byte_test.opcode = CHECK_LT;
+ ByteTestOption test_2_3(byte_test);
+ REQUIRE(test != test_2_3);
+ }
+
+ SECTION("offset is different")
+ {
+ byte_test.offset = 2;
+ ByteTestOption test_2_4(byte_test);
+ REQUIRE(test != test_2_4);
+ }
+
+ SECTION("not_flag is different")
+ {
+ byte_test.not_flag = 0;
+ ByteTestOption test_2_5(byte_test);
+ REQUIRE(test != test_2_5);
+ }
+
+ SECTION("relative_flag is different")
+ {
+ byte_test.relative_flag = 0;
+ ByteTestOption test_2_6(byte_test);
+ REQUIRE(test != test_2_6);
+ }
+
+ SECTION("string_convert_flag is different")
+ {
+ byte_test.string_convert_flag = 0;
+ ByteTestOption test_2_7(byte_test);
+ REQUIRE(test != test_2_7);
+ }
+
+ SECTION("endianness is different")
+ {
+ byte_test.endianness = 0;
+ ByteTestOption test_2_8(byte_test);
+ REQUIRE(test != test_2_8);
+ }
+
+ SECTION("base is different")
+ {
+ byte_test.base = 2;
+ ByteTestOption test_2_9(byte_test);
+ REQUIRE(test != test_2_9);
+ }
+
+ SECTION("bitmask_val is different")
+ {
+ byte_test.bitmask_val = 2;
+ ByteTestOption test_2_10(byte_test);
+ REQUIRE(test != test_2_10);
+ }
+
+ SECTION("cmp_value_var is different")
+ {
+ byte_test.cmp_value_var = 0;
+ ByteTestOption test_2_13(byte_test);
+ REQUIRE(test != test_2_13);
+ }
+
+ SECTION("cmp_value_var is different")
+ {
+ byte_test.offset_var = 0;
+ ByteTestOption test_2_12(byte_test);
+ REQUIRE(test != test_2_12);
+ }
+ }
+
+ SECTION("method eval")
+ {
+ Packet test_packet;
+ Cursor current_cursor;
+ SetByteTestData(byte_test, 1);
+
+ SECTION("Cursor not set correct for byte_extract")
+ {
+ byte_test.cmp_value_var = 3;
+ byte_test.offset_var = 3;
+ byte_test.string_convert_flag = 0;
+ ByteTestOption test_2(byte_test);
+ REQUIRE((test_2.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Byte_to_compare set to zero for string_extract")
+ {
+ byte_test.string_convert_flag = 1;
+ byte_test.bytes_to_extract = 0;
+ ByteTestOption test_3(byte_test);
+ uint8_t buff = 0;
+ current_cursor.set("hello_world_long_name", &buff, 50);
+ REQUIRE((test_3.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Byte_test_check with extract value not equal to need one")
+ {
+ byte_test.string_convert_flag = 0;
+ byte_test.relative_flag = 0;
+ uint8_t buff = 0;
+ current_cursor.set("hello_world_long_name", &buff, 50);
+ ByteTestOption test_4(byte_test);
+ REQUIRE((test_4.eval(current_cursor, &test_packet)) == NO_MATCH);
+ }
+
+ SECTION("Correct match")
+ {
+ byte_test.string_convert_flag = 0;
+ byte_test.relative_flag = 0;
+ byte_test.opcode = ByteTestOper(7);
+ byte_test.not_flag = 1;
+ uint8_t buff = 0;
+ current_cursor.set("hello_world_long_name", &buff, 50);
+ ByteTestOption test_5(byte_test);
+ REQUIRE((test_5.eval(current_cursor, &test_packet)) == MATCH);
+ }
+ }
+
+}
+
+TEST_CASE("ByteTestModule test", "[ips_byte_test]")
+{
+ ByteTestModule module_test;
+ ByteTestData byte_test;
+ SetByteTestData(byte_test, 1);
+
+ SECTION("method end")
+ {
+ std::string buff = "tmp";
+
+ SECTION("Undefined rule option for var")
+ {
+ module_test.cmp_var = buff;
+ module_test.data = byte_test;
+ REQUIRE(module_test.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Undefined rule option for offset_var")
+ {
+ module_test.cmp_var.clear();
+ module_test.off_var = buff;
+ module_test.data = byte_test;
+ REQUIRE(module_test.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Number of bytes in \"bitmask\" value is greater than bytes to extract")
+ {
+ byte_test.endianness = 0;
+ byte_test.bytes_to_extract = 0;
+ module_test.data = byte_test;
+ REQUIRE(module_test.end("tmp", 0, nullptr) == false);
+ }
+
+ SECTION("Case with returned value true")
+ {
+ module_test.data = byte_test;
+ REQUIRE(module_test.end("tmp", 0, nullptr) == true);
+ }
+ }
+
+ SECTION("method set")
+ {
+ Value value(false);
+
+ SECTION("All params incorrect")
+ {
+ REQUIRE(module_test.set(nullptr, value, nullptr) == false);
+ }
+
+ SECTION("Case param \"~count\"")
+ {
+ Parameter param("~count", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Param \"~operator\" correct")
+ {
+ Parameter param("~operator", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"~compare\"")
+ {
+ SECTION("Value doesn't have a str")
+ {
+ Parameter param("~compare", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("When value has a str")
+ {
+ Value value_tmp("123");
+ Parameter param("~compare", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value_tmp.set(¶m);
+ REQUIRE(module_test.set(nullptr, value_tmp, nullptr) == true);
+ }
+ }
+
+ SECTION("Case param \"~offset\"")
+ {
+ SECTION("Value doesn't have a str")
+ {
+ Parameter param("~offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("When value has a str")
+ {
+ Value value_tmp("123");
+ Parameter param("~offset", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value_tmp.set(¶m);
+ REQUIRE(module_test.set(nullptr, value_tmp, nullptr) == true);
+ }
+ }
+
+ SECTION("Case param \"relative\"")
+ {
+ Parameter param("relative", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"big\"")
+ {
+ Parameter param("big", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"little\"")
+ {
+ Parameter param("little", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"dce\"")
+ {
+ Parameter param("dce", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"string\"")
+ {
+ Parameter param("string", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"dec\"")
+ {
+ Parameter param("dec", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"hex\"")
+ {
+ Parameter param("hex", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"oct\"")
+ {
+ Parameter param("oct", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+
+ SECTION("Case param \"bitmask\"")
+ {
+ Parameter param("bitmask", snort::Parameter::Type::PT_BOOL,
+ nullptr, "default", "help");
+ value.set(¶m);
+ REQUIRE(module_test.set(nullptr, value, nullptr) == true);
+ }
+ }
+}
+
+#endif