This section provides a simple example of a dynamic preprocessor and a dynamic rule.
The following is an example of a simple preprocessor. This preprocessor always alerts on a packet if the TCP port matches the one configured.
The following code is defined in spp_example.c and is compiled together with libsf_dynamic_preproc.a, using pkg-config, into lib_sfdynamic_preprocessor_example.so.
Define the required meta data variables.
#define GENERATOR_EXAMPLE 256 extern DynamicPreprocessorData _dpd; const int MAJOR_VERSION = 1; const int MINOR_VERSION = 0; const int BUILD_VERSION = 0; const char *PREPROC_NAME = "SF_Dynamic_Example_Preprocessor"; #define ExampleSetup DYNAMIC_PREPROC_SETUP
Define the Setup function to register the initialization function.
void ExampleInit(unsigned char *); void ExampleProcess(void *, void *); void ExampleSetup() { _dpd.registerPreproc("dynamic_example", ExampleInit); DEBUG_WRAP(_dpd.debugMsg(DEBUG_PLUGIN, "Preprocessor: Example is setup\n");); }
The initialization function to parse the keywords from snort.conf.
u_int16_t portToCheck; void ExampleInit(unsigned char *args) { char *arg; char *argEnd; unsigned long port; _dpd.logMsg("Example dynamic preprocessor configuration\n"); arg = strtok(args, " \t\n\r"); if(!strcasecmp("port", arg)) { arg = strtok(NULL, "\t\n\r"); if (!arg) { _dpd.fatalMsg("ExamplePreproc: Missing port\n"); } port = strtoul(arg, &argEnd, 10); if (port < 0 || port > 65535) { _dpd.fatalMsg("ExamplePreproc: Invalid port %d\n", port); } portToCheck = port; _dpd.logMsg(" Port: %d\n", portToCheck); } else { _dpd.fatalMsg("ExamplePreproc: Invalid option %s\n", arg); } /* Register the preprocessor function, Transport layer, ID 10000 */ _dpd.addPreproc(ExampleProcess, PRIORITY_TRANSPORT, 10000); DEBUG_WRAP(_dpd.debugMsg(DEBUG_PLUGIN, "Preprocessor: Example is initialized\n");); }
The function to process the packet and log an alert if the either port matches.
#define SRC_PORT_MATCH 1 #define SRC_PORT_MATCH_STR "example_preprocessor: src port match" #define DST_PORT_MATCH 2 #define DST_PORT_MATCH_STR "example_preprocessor: dest port match" void ExampleProcess(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket *)pkt; if (!p->ip4_header || p->ip4_header->proto != IPPROTO_TCP || !p->tcp_header) { /* Not for me, return */ return; } if (p->src_port == portToCheck) { /* Source port matched, log alert */ _dpd.alertAdd(GENERATOR_EXAMPLE, SRC_PORT_MATCH, 1, 0, 3, SRC_PORT_MATCH_STR, 0); return; } if (p->dst_port == portToCheck) { /* Destination port matched, log alert */ _dpd.alertAdd(GENERATOR_EXAMPLE, DST_PORT_MATCH, 1, 0, 3, DST_PORT_MATCH_STR, 0); return; } }
The following is an example of a simple rule, take from the current rule set, SID 109. It is implemented to work with the detection engine provided with snort.
The snort rule in normal format:
alert tcp $HOME_NET 12345:12346 -> $EXTERNAL_NET any \ (msg:"BACKDOOR netbus active"; flow:from_server,established; \ content:"NetBus"; reference:arachnids,401; classtype:misc-activity; \ sid:109; rev:5;)
This is the metadata for this rule library, defined in detection_lib_meta.h.
/* Version for this rule library */ #define DETECTION_LIB_MAJOR_VERSION 1 #define DETECTION_LIB_MINOR_VERSION 0 #define DETECTION_LIB_BUILD_VERSION 1 #define DETECTION_LIB_NAME "Snort_Dynamic_Rule_Example" /* Required version and name of the engine */ #define REQ_ENGINE_LIB_MAJOR_VERSION 1 #define REQ_ENGINE_LIB_MINOR_VERSION 0 #define REQ_ENGINE_LIB_NAME "SF_SNORT_DETECTION_ENGINE"
The definition of each data structure for this rule is in sid109.c.
Declaration of the data structures.
Define the FlowFlags structure and its corresponding RuleOption. Per the text version, flow is from_server,established.
static FlowFlags sid109flow = { FLOW_ESTABLISHED|FLOW_TO_CLIENT }; static RuleOption sid109option1 = { OPTION_TYPE_FLOWFLAGS, { &sid109flow } };
Define the ContentInfo structure and its corresponding RuleOption. Per the text version, content is "NetBus", no depth or offset, case sensitive, and non-relative. Search on the normalized buffer by default. NOTE: This content will be used for the fast pattern matcher since it is the longest content option for this rule and no contents have a flag of CONTENT_FAST_PATTERN.
static ContentInfo sid109content = { "NetBus", /* pattern to search for */ 0, /* depth */ 0, /* offset */ CONTENT_BUF_NORMALIZED, /* flags */ NULL, /* holder for boyer/moore info */ NULL, /* holder for byte representation of "NetBus" */ 0, /* holder for length of byte representation */ 0 /* holder for increment length */ }; static RuleOption sid109option2 = { OPTION_TYPE_CONTENT, { &sid109content } };
Define the references.
static RuleReference sid109ref_arachnids = { "arachnids", /* Type */ "401" /* value */ }; static RuleReference *sid109refs[] = { &sid109ref_arachnids, NULL };
The list of rule options. Rule options are evaluated in the order specified.
RuleOption *sid109options[] = { &sid109option1, &sid109option2, NULL };
The rule itself, with the protocol header, meta data (sid, classification, message, etc).
Rule sid109 = { /* protocol header, akin to => tcp any any -> any any */ { IPPROTO_TCP, /* proto */ HOME_NET, /* source IP */ "12345:12346", /* source port(s) */ 0, /* Direction */ EXTERNAL_NET, /* destination IP */ ANY_PORT, /* destination port */ }, /* metadata */ { 3, /* genid -- use 3 to distinguish a C rule */ 109, /* sigid */ 5, /* revision */ "misc-activity", /* classification */ 0, /* priority */ "BACKDOOR netbus active", /* message */ sid109refs /* ptr to references */ }, sid109options, /* ptr to rule options */ NULL, /* Use internal eval func */ 0, /* Holder, not yet initialized, used internally */ 0, /* Holder, option count, used internally */ 0, /* Holder, no alert, used internally for flowbits */ NULL /* Holder, rule data, used internally */
The NULL terminated list of rules. The InitializeDetection iterates through each Rule in the list and initializes the content, flowbits, pcre, etc.
extern Rule sid109; extern Rule sid637; Rule *rules[] = { &sid109, &sid637, NULL };