Snort 入侵检测系统规则解析部分分析

Snort规则实际采用三维链表结构形式,数据结构RuleListNode如下:
typedef struct_RuleListNode
{
ListHead *RuList; /* The rule list associated with this node*/
int mode; /* The rule mode */
int rval; /* 0—no detection , 1 –detection event */
int evalIndex ; /* eval index for this rule set */
char *name; /* Name of this rule list */
struct_RuleListNode Next; /* Rhe next RuleListNode */
} RuleListNode;

在parse.c文件可以看到:
/* Rule list order keywords
* This is separate from keywords because activation was used
* instead of activate */
下面是规则的说明关键字如”alert””log”等。
#define RULE_LIST_TYPE__ACTIVATION “activation”
#define RULE_LIST_TYPE__ALERT “alert”
#define RULE_LIST_TYPE__DROP “drop”
#define RULE_LIST_TYPE__DYNAMIC “dynamic”
#define RULE_LIST_TYPE__LOG “log”
#define RULE_LIST_TYPE__PASS “pass”
#define RULE_LIST_TYPE__REJECT “reject”
#define RULE_LIST_TYPE__SDROP “sdrop”

#define RULE_PROTO_OPT__IP “ip”
#define RULE_PROTO_OPT__TCP “tcp”
#define RULE_PROTO_OPT__UDP “udp”
#define RULE_PROTO_OPT__ICMP “icmp”

#define RULE_DIR_OPT__DIRECTIONAL “->”
#define RULE_DIR_OPT__BIDIRECTIONAL “<>”
看一下结构:
typedef struct _SnortConfig
{
RunMode run_mode;
int run_mode_flags;
int run_flags;
int output_flags;
int logging_flags;
int log_tcpdump;
int no_log;
int no_alert;
int dirty_pig;

//used for processing command line arguments, checksum configuration
//in conf files is maintained at policy level
int checksum_flags; /* -k */
int checksum_flags_modified;
int checksum_drop_flags;
int checksum_drop_flags_modified;

uint32_t event_log_id; /* -G */
int pkt_snaplen;
int64_t pkt_cnt; /* -n */

char *dynamic_rules_path; /* –dump-dynamic-rules */

#ifdef DYNAMIC_PLUGIN
/* –dynamic-engine-lib
* –dynamic-engine-lib-dir
* –dynamic-detection-lib
* –dynamic-detection-lib-dir
* –dynamic-preprocessor-lib
* –dynamic-preprocessor-lib-dir
*
* See below for struct type
*/
DynamicLibInfo *dyn_engines;
DynamicLibInfo *dyn_rules;
DynamicLibInfo *dyn_preprocs;
#endif

char pid_path[STD_BUF]; /* –pid-path or config pidpath */

#ifdef EXIT_CHECK
uint64_t exit_check; /* –exit-check */
#endif

/* -h and -B */
#ifdef SUP_IP6
sfip_t homenet;
sfip_t obfuscation_net;
#else
uint32_t homenet;
uint32_t netmask;
uint32_t obfuscation_net;
uint32_t obfuscation_mask;
#endif

/* config disable_decode_alerts
* config enable_decode_oversized_alerts
* config enable_decode_oversized_drops
* config enable_decode_drops
* config disable_decode_drops
* config disable_tcpopt_experimental_alerts
* config enable_tcpopt_experimental_drops
* config disable_tcpopt_experimental_drops
* config disable_tcpopt_obsolete_alerts
* config enable_tcpopt_obsolete_drops
* config disable_tcpopt_obsolete_drops
* config disable_ttcp_alerts, config disable_tcpopt_ttcp_alerts
* config enable_ttcp_drops, config enable_tcpopt_ttcp_drops
* config disable_ttcp_drops
* config disable_tcpopt_alerts
* config enable_tcpopt_drops
* config disable_tcpopt_drops
* config disable_ipopt_alerts
* config enable_ipopt_drops
* config disable_ipopt_drops
* config ipv6_frag:
* bsd_icmp_frag_alert
* bad_ipv6_frag_alert
* frag_timeout – not in DecoderFlags
* max_frag_sessions – not in DecoderFlags
* drop_bad_ipv6_frag
*/
uint32_t ipv6_frag_timeout;
uint32_t ipv6_max_frag_sessions;

uint8_t flowbit_size;

char pid_filename[STD_BUF]; /* used with pid_path */
char pidfile_suffix[MAX_PIDFILE_SUFFIX + 1]; /* -R */
char *log_dir; /* -l or config log_dir */
char *orig_log_dir; /* set in case of chroot */
char *interface; /* -i or config interface */
char *bpf_file; /* -F or config bpf_file */
char *pcap_log_file; /* -L */
char *chroot_dir; /* -t or config chroot */
char *alert_file;
char *perf_file; /* -Z */
char *bpf_filter; /* last command line arguments */
char *pcap_file; /* config read_bin_file */
char* daq_type; /* –daq or config daq */
char* daq_mode; /* –daq-mode or config daq_mode */
void* daq_vars; /* –daq-var or config daq_var */
void* daq_dirs; /* –daq-dir or config daq_dir */

int thiszone;

#ifdef WIN32
char syslog_server[STD_BUF];
int syslog_server_port;
# ifdef ENABLE_WIN32_SERVICE
int terminate_service_flag;
int pause_service_flag;
# endif
#endif

uint8_t ignore_ports[UINT16_MAX]; /* config ignore_ports */
long int tagged_packet_limit; /* config tagged_packet_limit */
long int pcre_match_limit; /* config pcre_match_limit */
long int pcre_match_limit_recursion; /* config pcre_match_limit_recursion */

#ifdef PERF_PROFILING
ProfileConfig profile_rules; /* config profile_rules */
ProfileConfig profile_preprocs; /* config profile_preprocs */
#endif

int user_id;
int group_id;

mode_t file_mask;

#ifdef MPLS
uint8_t mpls_payload_type; /* –mpls_payload_type */
long int mpls_stack_depth; /* –max_mpls_labelchain_len */
#endif

int default_rule_state; /* config default_rule_state */

char* react_page; /* config react */

#ifdef ACTIVE_RESPONSE
uint8_t respond_attempts; /* config respond */
char* respond_device;
#endif

#ifdef TARGET_BASED
uint32_t max_attribute_hosts; /* config max_attribute_hosts */
uint32_t max_metadata_services; /* config max_metadata_services */
#endif

OutputConfig *output_configs;
OutputConfig *rule_type_output_configs;
SFGHASH *config_table; /* table of config keywords and arguments */
int asn1_mem;

int active_dynamic_nodes;

RuleState *rule_state_list;
ClassType *classifications;
ReferenceSystemNode *references;
SFGHASH *so_rule_otn_map;
SFGHASH *otn_map;

FastPatternConfig *fast_pattern_config;
EventQueueConfig *event_queue_config;

PreprocPostConfigFuncNode *preproc_post_config_funcs;
PreprocCheckConfigFuncNode *preproc_config_check_funcs;
#ifdef SNORT_RELOAD
PreprocReloadVerifyFuncNode *preproc_reload_verify_funcs;
#endif

/* XXX XXX policy specific? */
ThresholdConfig *threshold_config;
RateFilterConfig *rate_filter_config;
DetectionFilterConfig *detection_filter_config;

SF_EVENTQ *event_queue[NUM_EVENT_QUEUES];

SF_LIST **ip_proto_only_lists;
uint8_t ip_proto_array[NUM_IP_PROTOS];

int num_rule_types;
RuleListNode *rule_lists;

ListHead Alert; /* Alert Block Header */
ListHead Log; /* Log Block Header */
ListHead Pass; /* Pass Block Header */
ListHead Activation; /* Activation Block Header */
ListHead Dynamic; /* Dynamic Block Header */
ListHead Drop;
ListHead SDrop;
ListHead Reject;

PluginSignalFuncNode *plugin_post_config_funcs;

OTNX_MATCH_DATA *omd;

/* Pattern matcher queue statistics */
unsigned int max_inq;
uint64_t tot_inq_flush;
uint64_t tot_inq_inserts;
uint64_t tot_inq_uinserts;

/* master port list table */
rule_port_tables_t *port_tables;

#ifdef PPM_MGR
ppm_cfg_t ppm_cfg;
#endif

/* The port-rule-maps map the src-dst ports to rules for
* udp and tcp, for Ip we map the dst port as the protocol,
* and for Icmp we map the dst port to the Icmp type. This
* allows us to use the decode packet information to in O(1)
* select a group of rules to apply to the packet. These
* rules may have uricontent, content, or they may be no content
* rules, or any combination. We process the uricontent 1st,
* then the content, and then the no content rules for udp/tcp
* and icmp, than we process the ip rules. */
PORT_RULE_MAP *prmIpRTNX;
PORT_RULE_MAP *prmTcpRTNX;
PORT_RULE_MAP *prmUdpRTNX;
PORT_RULE_MAP *prmIcmpRTNX;

#ifdef TARGET_BASED
srmm_table_t *srmmTable; /* srvc rule map master table */
srmm_table_t *spgmmTable; /* srvc port_group map master table */
sopg_table_t *sopgTable; /* service-oridnal to port_group table */
#endif

SFXHASH *detection_option_hash_table;
SFXHASH *detection_option_tree_hash_table;

tSfPolicyConfig *policy_config;
SnortPolicy **targeted_policies;
unsigned int num_policies_allocated;

char *base_version;

uint8_t enable_teredo; /* config enable_deep_teredo_inspection */

uint32_t so_rule_memcap;
} SnortConfig;

看一下解析函数,功能还是显而易见:
static void ParseActivate(SnortConfig *, SnortPolicy *, char *);
static void ParseAlert(SnortConfig *, SnortPolicy *, char *);
static void ParseDrop(SnortConfig *, SnortPolicy *, char *);
static void ParseDynamic(SnortConfig *, SnortPolicy *, char *);
static void ParseLog(SnortConfig *, SnortPolicy *, char *);
static void ParsePass(SnortConfig *, SnortPolicy *, char *);
static void ParseReject(SnortConfig *, SnortPolicy *, char *);
static void ParseSdrop(SnortConfig *, SnortPolicy *, char *);
。。。。。

ParseSnortConf()
读规则文件一次一行并且发送每个规则到规则解析。

/*
* Parse a port string as a port var, and create or find a port object for it,
* and add it to the port var table. These are used by the rtn’s
* as src and dst port lists for final rtn/otn processing.
*
* These should not be confused with the port objects used to merge ports and rules
* to build PORT_GROUP objects. Those are generated after the otn processing.
*
*/
static PortObject * ParsePortListTcpUdpPort(PortVarTable *pvt,
PortTable *noname, char *port_str)
{
PortObject * portobject;
//PortObject * pox;
char * errstr=0;
POParser poparser;

if ((pvt == NULL) || (noname == NULL) || (port_str == NULL))
return NULL;

/* 1st – check if we have an any port */
if( strcasecmp(port_str,”any”)== 0 )
{
portobject = PortVarTableFind(pvt, “any”);
if (portobject == NULL)
ParseError(“PortVarTable missing an ‘any’ variable.”);

return portobject;
}

/* 2nd – check if we have a PortVar */
else if( port_str[0]==’$’ )
{
/*||isalpha(port_str[0])*/ /*TODO: interferes with protocol names for ports*/
char * name = port_str;

name++; /* advance past ‘$’ */

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,”PortVarTableFind: finding ‘%s’\n”, port_str););

/* look it up in the port var table */
portobject = PortVarTableFind(pvt, name);
if (portobject == NULL)
ParseError(“***PortVar Lookup failed on ‘%s’.”, port_str);

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,”PortVarTableFind: ‘%s’ found!\n”, port_str););
}

/* 3rd – and finally process a raw port list */
else
{
/* port list = [p,p,p:p,p,…] or p or p:p , no embedded spaces due to tokenizer */
PortObject * pox;

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
“parser.c->PortObjectParseString: parsing ‘%s’\n”,port_str););

portobject = PortObjectParseString(pvt, &poparser, 0, port_str, 0);

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
“parser.c->PortObjectParseString: ‘%s’ done.\n”,port_str););

if( !portobject )
{
errstr = PortObjectParseError( &poparser );
ParseError(“***Rule–PortVar Parse error: (pos=%d,error=%s)\n>>%s\n>>%*s\n”,
poparser.pos,errstr,port_str,poparser.pos,”^”);
}

/* check if we already have this port object in the un-named port var table … */
pox = PortTableFindInputPortObjectPorts(noname, portobject);
if( pox )
{
DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
“parser.c: already have ‘%s’ as a PortObject – ”
“calling PortObjectFree(portbject) line=%d\n”,port_str,__LINE__ ););
PortObjectFree( portobject );
portobject = pox;
}
else
{
DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
“parser.c: adding ‘%s’ as a PortObject line=%d\n”,port_str,__LINE__ ););
/* Add to the un-named port var table */
if (PortTableAddObject(noname, portobject))
{
FatalError(“%s(%d) Unable to add raw port object to unnamed ”
“port var table, out of memory!\n”, __FILE__, __LINE__);
}
}
}

return portobject;
}

ParsePreprocessor():
保存预处理配置为以后load。

解析规则函数:ParseRule();
static void ParseRule(SnortConfig *sc, SnortPolicy *p, char *args,
RuleType rule_type, ListHead *list)
{
char **toks = NULL;
int num_toks = 0;
int protocol = 0;
RuleTreeNode test_rtn;
RuleTreeNode *rtn;
OptTreeNode *otn;
char *roptions = NULL;
port_entry_t pe;
PortVarTable *portVarTable = p->portVarTable;
PortTable *nonamePortVarTable = p->nonamePortVarTable;

if ((sc == NULL) || (args == NULL))
return;

memset(&test_rtn, 0, sizeof(RuleTreeNode));

memset(&pe, 0, sizeof(pe));

DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,”[*] Rule start\n”););

/* We have a preproc or decoder rule – we assume a header of ‘tcp any any -> any any ‘ */
if (*args == ‘(‘)
{
test_rtn.flags |= ANY_DST_PORT;
test_rtn.flags |= ANY_SRC_PORT;
test_rtn.flags |= ANY_DST_IP;
test_rtn.flags |= ANY_SRC_IP;
test_rtn.flags |= BIDIRECTIONAL;
test_rtn.type = rule_type;
protocol = IPPROTO_TCP;

roptions = args;

DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, “Preprocessor Rule detected\n”););
}
else
{
/* proto ip port dir ip port r*/
toks = mSplit(args, ” \t”, 7, &num_toks, ‘\\’);

/* A rule might not have rule options */
if (num_toks < 6)
{
ParseError("Bad rule in rules file: %s", args);
}

if (num_toks == 7)
roptions = toks[6];

test_rtn.type = rule_type;

DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Non-Preprocessor Rule detected\n"););

/* Set the rule protocol - fatal errors if protocol not found */
protocol = GetRuleProtocol(toks[0]);
test_rtn.proto = protocol;

switch (protocol)
{
case IPPROTO_TCP:
sc->ip_proto_array[IPPROTO_TCP] = 1;
break;
case IPPROTO_UDP:
sc->ip_proto_array[IPPROTO_UDP] = 1;
break;
case IPPROTO_ICMP:
sc->ip_proto_array[IPPROTO_ICMP] = 1;
sc->ip_proto_array[IPPROTO_ICMPV6] = 1;

if ( rule_type == RULE_TYPE__REJECT )
test_rtn.type = rule_type = RULE_TYPE__ALERT;
break;
case ETHERNET_TYPE_IP:
/* This will be set via ip_protos */
break;
default:
ParseError(“Bad protocol: %s”, toks[0]);
break;
}

/* Process the IP address and CIDR netmask – changed version 1.2.1
* “any” IP’s are now set to addr 0, netmask 0, and the normal rules are
* applied instead of checking the flag if we see a “!” we
* need to set a flag so that we can properly deal with it when we are
* processing packets. */
ProcessIP(sc, toks[1], &test_rtn, SRC, 0);

/* Check to make sure that the user entered port numbers.
* Sometimes they forget/don’t know that ICMP rules need them */
if ((strcasecmp(toks[2], RULE_DIR_OPT__DIRECTIONAL) == 0) ||
(strcasecmp(toks[2], RULE_DIR_OPT__BIDIRECTIONAL) == 0))
{
ParseError(“Port value missing in rule!”);
}

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,”Src-Port: %s\n”,toks[2]););

if (ParsePortList(&test_rtn, portVarTable, nonamePortVarTable,
toks[2], protocol, 0 /* =src port */ ))
{
ParseError(“Bad source port: ‘%s'”, toks[2]);
}

/* changed version 1.8.4
* Die when someone has tried to define a rule character other
* than -> or <> */
if ((strcmp(toks[3], RULE_DIR_OPT__DIRECTIONAL) != 0) &&
(strcmp(toks[3], RULE_DIR_OPT__BIDIRECTIONAL) != 0))
{
ParseError(“Illegal direction specifier: %s”, toks[3]);
}

/* New in version 1.3: support for bidirectional rules
* This checks the rule “direction” token and sets the bidirectional
* flag if the token = ‘<>‘ */
if (strcmp(toks[3], RULE_DIR_OPT__BIDIRECTIONAL) == 0)
{
DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,”Bidirectional rule!\n”););
test_rtn.flags |= BIDIRECTIONAL;
}

/* changed version 1.2.1
* “any” IP’s are now set to addr 0, netmask 0, and the normal rules are
* applied instead of checking the flag
* If we see a “!” we need to set a flag so that we can
* properly deal with it when we are processing packets */
ProcessIP(sc, toks[4], &test_rtn, DST, 0);

DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,”Dst-Port: %s\n”, toks[5]););

if (ParsePortList(&test_rtn, portVarTable, nonamePortVarTable,
toks[5], protocol, 1 /* =dst port */ ))
{
ParseError(“Bad destination port: ‘%s'”, toks[5]);
}
}

DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,”test_rtn.flags = 0x%X\n”, test_rtn.flags););
DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,”Processing Head Node….\n”););

test_rtn.listhead = list;

rtn = ProcessHeadNode(sc, &test_rtn, list);
/* The IPs in the test node get free’d in ProcessHeadNode if there is
* already a matching RTN. The portobjects will get free’d when the
* port var table is free’d */

DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,”Parsing Rule Options…\n”););

otn = ParseRuleOptions(sc, rtn, roptions, rule_type, protocol);
if (otn == NULL)
{
/* This otn is a dup and we’re choosing to keep the old one */
mSplitFree(&toks, num_toks);
return;
}

rule_count++;

/* Get rule option info */
pe.gid = otn->sigInfo.generator;
pe.sid = otn->sigInfo.id;

/* Have to have at least 6 toks */
if (num_toks != 0)
{
pe.protocol = SnortStrdup(toks[0]);
pe.src_port = SnortStrdup(toks[2]);
pe.dst_port = SnortStrdup(toks[5]);
}

/* See what kind of content is going in the fast pattern matcher */
#ifdef DYNAMIC_PLUGIN
if (otn->ds_list[PLUGIN_DYNAMIC] != NULL)
{
DynamicData *dd = (DynamicData *)otn->ds_list[PLUGIN_DYNAMIC];
if (dd->contentFlags & CONTENT_HTTP)
pe.uricontent = 1;
else if (dd->contentFlags & CONTENT_NORMAL)
pe.content = 1;
}
else
#endif
{
/* Since http_cookie content is not used in fast pattern matcher,
* need to iterate the entire list */
if (otn->ds_list[PLUGIN_PATTERN_MATCH_URI] != NULL)
{
PatternMatchData *pmd = otn->ds_list[PLUGIN_PATTERN_MATCH_URI];
for (; pmd != NULL; pmd = pmd->next)
{
if((pmd->uri_buffer) && IsHttpBufFpEligible(pmd->uri_buffer))
{
pe.uricontent = 1;
break;
}
}
}

if (!pe.uricontent && ((otn->ds_list[PLUGIN_PATTERN_MATCH] != NULL)
|| (otn->ds_list[PLUGIN_PATTERN_MATCH_OR] != NULL)))
{
pe.content = 1;
}
}

if (rtn->flags & BIDIRECTIONAL)
pe.dir = 1;

pe.proto = protocol;
pe.rule_type = rule_type;

port_list_add_entry(&port_list, &pe);

/*
* The src/dst port parsing must be done before the Head Nodes are processed, since they must
* compare the ports/port_objects to find the right rtn list to add the otn rule to.
*
* After otn processing we can finalize port object processing for this rule
*/
if (FinishPortListRule(sc->port_tables, rtn, otn, protocol, &pe, sc->fast_pattern_config))
ParseError(“Failed to finish a port list rule.”);

mSplitFree(&toks, num_toks);
}

在SnortMain()函数中,主要涉及调用了建立和初始化规则优化和快速匹配数据结构的类型。fpCreateFastPacketDetection()
是建立快速匹配引擎的主要接口函数,是读入有规则解析模块建立的规则链表中的所有规则链表头(RuleTreeNode类型)和规则选项节点(OptTreeNode类型),一边快速匹配之用。

发表评论