本文共 14781 字,大约阅读时间需要 49 分钟。
前面介绍了规则头数据结构分析、解析和匹配过程。接下来分析规则选项的数据结构和解析、匹配过程。
/* * 规则匹选项配数据结构 */typedef struct _OptFpList{ /* context data for this test */ /* 匹配时传入的参数option_data, 例如 PatternMatchData*/ void *context; /* 匹配函数指针*/ int (*OptTestFunc)(void *option_data, Packet *p); /* 解析规则选项时,使用next指针将所有的规则选项的匹配对象链在一起*/ struct _OptFpList *next; unsigned char isRelative; option_type_t type;} OptFpList;/* * 规则选项 */typedef struct _OptTreeNode{ /* plugin/detection functions go here */ /* 检测链表 */ OptFpList *opt_func; /* 响应链表, 比如重置tcp连接*/ RspFpList *rsp_func; /* response functions */ /* 日志输出链表*/ OutputFuncNode *outputFuncs; /* per sid enabled output functions */ /* the ds_list is absolutely essential for the plugin system to work, it allows the plugin authors to associate "dynamic" data structures with the rule system, letting them link anything they can come up with to the rules list */ /* 这个结构非常重要,这是规则选项匹配的核心结构, 里面每一种类型的元素是一个链表,链表的元素是每个规则选项字段匹配时需要用到的数据结构, 例如content字段对应于PatternMatchData的对象*/ void *ds_list[PLUGIN_MAX]; /* list of plugin data struct pointers */ /* 规则选项的计数索引*/ int chain_node_number; int evalIndex; /* where this rule sits in the evaluation sets */ /* 协议类型 tcp、udp*/ int proto; /* protocol, added for integrity checks during rule parsing */ int session_flag; /* record session data */ char *logto; /* log file in which to write packets which match this rule*/ /* metadata about signature */ /* metadata 相关的数据,存在这个结构中, 还有 msg的调试也存储在这当中*/ SigInfo sigInfo; uint8_t stateless; /* this rule can fire regardless of session state */ uint8_t established; /* this rule can only fire if it is established */ uint8_t unestablished; Event event_data; void* detection_filter; /* if present, evaluated last, after header checks */ TagData *tag; /* stuff for dynamic rules activation/deactivation */ int active_flag; int activation_counter; int countdown; int activates; int activated_by; struct _OptTreeNode *OTN_activation_ptr; struct _RuleTreeNode *RTN_activation_ptr; struct _OptTreeNode *next; struct _OptTreeNode *nextSoid; /* ptr to list of RTNs (head part) */ struct _RuleTreeNode **proto_nodes; /**number of proto_nodes. */ unsigned short proto_node_num; uint8_t failedCheckBits; char generated; uint16_t longestPatternLen; int rule_state; /* Enabled or Disabled */ /* 性能统计 */#ifdef PERF_PROFILING uint64_t ticks; uint64_t ticks_match; uint64_t ticks_no_match; uint64_t checks; uint64_t matches; uint64_t alerts; uint8_t noalerts;#endif /* 性能统计 */ int pcre_flag; /* PPM */ uint64_t ppm_suspend_time; /* PPM */ uint64_t ppm_disable_cnt; /*PPM */ uint32_t num_detection_opts; /**unique index generated in ruleIndexMap. */ /* 在选择对应端口的规则时使用PortObject, 对规则进行分类*/ int ruleIndex; /* List of preprocessor registered fast pattern contents */ void *preproc_fp_list;} OptTreeNode;
对数据结构中的主要字段进行解释。接下来看如何解析赋值。
同样是从ParseRule中开始解析,内部调用ParseRuleOptions进行解析
/* 规则选项解析函数*/OptTreeNode * ParseRuleOptions(SnortConfig *sc, RuleTreeNode *rtn, char *rule_opts, RuleType rule_type, int protocol){ OptTreeNode *otn; RuleOptOtnHandler otn_handler = NULL; int num_detection_opts = 0; char *dopt_keyword = NULL; OptFpList *fpl = NULL; int got_sid = 0; /* 分配内存 */ otn = (OptTreeNode *)SnortAlloc(sizeof(OptTreeNode)); /* 设置默认值、统计计数等*/ otn->chain_node_number = otn_count; otn->proto = protocol; otn->event_data.sig_generator = GENERATOR_SNORT_ENGINE; otn->sigInfo.generator = GENERATOR_SNORT_ENGINE; otn->sigInfo.rule_type = SI_RULE_TYPE_DETECT; /* standard rule */ otn->sigInfo.rule_flushing = SI_RULE_FLUSHING_ON; /* usually just standard rules cause a flush*/ /* Set the default rule state */ otn->rule_state = ScDefaultRuleState(); /* 只有规则头的情况*/ if (rule_opts == NULL) { ... if (ScRequireRuleSid()) ParseError("Each rule must contain a Rule-sid."); /* 规则选项与规则头关联,里面涉及到policy相关的知识,后面会有博客进行分析*/ addRtnToOtn(otn, getParserPolicy(sc), rtn); otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap, otn->sigInfo.generator, otn->sigInfo.id); } else { char **toks; int num_toks; /* 这个数组用来控制某些选项的配置方式,比如在一条规则中只能出现一次msg*/ char configured[sizeof(rule_options) / sizeof(RuleOptFunc)]; int i; OptTreeNode *otn_dup; /* 规则选项的格式限制, 必须'c'开始 ')'结尾*/ if ((rule_opts[0] != '(') || (rule_opts[strlen(rule_opts) - 1] != ')')) { ParseError("Rule options must be enclosed in '(' and ')'."); } /* Move past '(' and zero out ')' */ /* 解析时, 剔除 '('、')'*/ rule_opts++; rule_opts[strlen(rule_opts) - 1] = '\0'; /* Used to determine if a rule option has already been configured * in the rule. Some can only be configured once */ memset(configured, 0, sizeof(configured)); /* 规则选项都是以 ';'分隔, 所以使用它进行切分*/ toks = mSplit(rule_opts, ";", 0, &num_toks, '\\'); /* 对切分结果进行解析*/ for (i = 0; i < num_toks; i++) { char **opts; int num_opts; char *option_args = NULL; int j; ... /* break out the option name from its data */ /* 基本上每一项都是以 a:b (key:value)进行编写, 所以需要再次切分*/ opts = mSplit(toks[i], ":", 2, &num_opts, '\\'); ... if (num_opts == 2) { /* 获取key */ option_args = opts[1]; } /* 普通规则选项集合, 顺序遍历进行搜索匹配key,如果存在调用key对应的解析函数进行解 析处理,并设置已经配置过,再次出现报错。如果没有搜索到,继续往下匹配 */ for (j = 0; rule_options[j].name != NULL; j++) { if (strcasecmp(opts[0], rule_options[j].name) == 0) { if (configured[j] && rule_options[j].only_once) { ParseError("Only one '%s' rule option per rule.", opts[0]); } if ((option_args == NULL) && rule_options[j].args_required) { ParseError("No argument passed to keyword \"%s\". " "Make sure you didn't forget a ':' or the " "argument to this keyword.\n", opts[0]); } rule_options[j].parse_func(sc, rtn, otn, rule_type, option_args); configured[j] = 1; break; } } /* Because we actually allow an sid of 0 */ if ((rule_options[j].name != NULL) && (strcasecmp(rule_options[j].name, RULE_OPT__SID) == 0)) { got_sid = 1; } /* It's possibly a detection option plugin */ /* 出现的规则字段如果是检测引擎的需要使用的字段,特殊处理,这些回调函数是在snort启动 的时候注册的一些解析函数, 通过RegisterRuleOptions注册生成解析回调链表, 里面会 设置好每个选项对应的匹配回调函数 */ if (rule_options[j].name == NULL) { RuleOptConfigFuncNode *dopt = rule_opt_config_funcs; for (; dopt != NULL; dopt = dopt->next) { if (strcasecmp(opts[0], dopt->keyword) == 0) { /* 将value =>option_args传入回调函数中进行解析处理*/ dopt->func(sc, option_args, otn, protocol); /* If this option contains an OTN handler, save it for use after the rule is done parsing. */ if (dopt->otn_handler != NULL) otn_handler = dopt->otn_handler; /* This is done so if we have a preprocessor/decoder * rule, we can tell the user that detection options * are not supported with those types of rules, and * what the detection option is */ if ((dopt_keyword == NULL) && (dopt->type == OPT_TYPE_DETECTION)) { dopt_keyword = SnortStrdup(opts[0]); } break; } } /* 如果上面的两个都没匹配上, 再在预处理回调解析函数链表中进行搜索匹配, 动作和上面一致*/ if (dopt == NULL) { /* Maybe it's a preprocessor rule option */ PreprocOptionInit initFunc = NULL; PreprocOptionEval evalFunc = NULL; PreprocOptionFastPatternFunc fpFunc = NULL; PreprocOptionOtnHandler preprocOtnHandler = NULL; PreprocOptionCleanup cleanupFunc = NULL; void *opt_data = NULL; int ret = GetPreprocessorRuleOptionFuncs (sc, opts[0], &initFunc, &evalFunc, &preprocOtnHandler, &fpFunc, &cleanupFunc); if (ret && (initFunc != NULL)) { initFunc(sc, opts[0], option_args, &opt_data); AddPreprocessorRuleOption(sc, opts[0], otn, opt_data, evalFunc); if (preprocOtnHandler != NULL) otn_handler = (RuleOptOtnHandler)preprocOtnHandler; ... } else { /* Unrecognized rule option */ ParseError("Unknown rule option: '%s'.", opts[0]); } } if (dopt_keyword == NULL) dopt_keyword = SnortStrdup(opts[0]); num_detection_opts++; } mSplitFree(&opts, num_opts); } if ((dopt_keyword != NULL) && (otn->sigInfo.rule_type != SI_RULE_TYPE_DETECT)) { /* Preprocessor and decoder rules can not have * detection options */ ParseError("Preprocessor and decoder rules do not support " "detection options: %s.", dopt_keyword); } if (dopt_keyword != NULL) free(dopt_keyword); if (!got_sid && !ScTestMode()) ParseError("Each rule must contain a rule sid."); ... /* 关联 规则选项与规则头*/ addRtnToOtn(otn, getParserPolicy(sc), rtn); /* Check for duplicate SID */ /* 通过gid 和sid 检索是否存在重复的规则,存在进行合并 */ otn_dup = OtnLookup(sc->otn_map, otn->sigInfo.generator, otn->sigInfo.id); if (otn_dup != NULL) { otn->ruleIndex = otn_dup->ruleIndex; if (mergeDuplicateOtn(sc, otn_dup, otn, rtn) == 0) { /* We are keeping the old/dup OTN and trashing the new one * we just created - it's free'd in the remove dup function */ mSplitFree(&toks, num_toks); return NULL; } } /* 不存在, 添加到map中*/ else { otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap, otn->sigInfo.generator, otn->sigInfo.id); } mSplitFree(&toks, num_toks); } otn->num_detection_opts += num_detection_opts; otn_count++; /* snort规则种类很多 通过rule_type 进行区分, 并对每种类型进行计数统计*/ if (otn->sigInfo.rule_type == SI_RULE_TYPE_DETECT) { detect_rule_count++; } else if (otn->sigInfo.rule_type == SI_RULE_TYPE_DECODE) { //Set the bit if the decoder rule is enabled in the policies UpdateDecodeRulesArray(otn->sigInfo.id, ENABLE_RULE, ENABLE_ONE_RULE); decode_rule_count++; } else if (otn->sigInfo.rule_type == SI_RULE_TYPE_PREPROC) { preproc_rule_count++; } /* 与规则头类似, 设置一个终止条件的回调函数*/ fpl = AddOptFuncToList(OptListEnd, otn); fpl->type = RULE_OPTION_TYPE_LEAF_NODE; if (otn_handler != NULL) { otn_handler(sc, otn); } /* 检查规则选项的合法性*/ FinalizeContentUniqueness(sc, otn); ValidateFastPattern(otn); if ((thdx_tmp != NULL) && (otn->detection_filter != NULL)) { ParseError("The \"detection_filter\" rule option and the \"threshold\" " "rule option cannot be used in the same rule.\n"); } if (thdx_tmp != NULL) { int rstat; thdx_tmp->sig_id = otn->sigInfo.id; thdx_tmp->gen_id = otn->sigInfo.generator; rstat = sfthreshold_create(sc, sc->threshold_config, thdx_tmp); if (rstat) { if (rstat == THD_TOO_MANY_THDOBJ) { ParseError("threshold (in rule): could not create threshold - " "only one per sig_id=%u.", thdx_tmp->sig_id); } else { ParseError("threshold (in rule): could not add threshold " "for sig_id=%u!\n", thdx_tmp->sig_id); } } thdx_tmp = NULL; } /* setup gid,sid->otn mapping */ SoRuleOtnLookupAdd(sc->so_rule_otn_map, otn); OtnLookupAdd(sc->otn_map, otn); return otn;}
以上规则解析函数的分析,然后下面分析规则检测中content字段的具体解析过程
RegisterRuleOptions=> SetupPatternMatch=>PayloadSearchInit
这个是初始化的注册过程。
解析时,遇到content会调用PayloadSearchInit 进行处理。
/* * data : 实际的规则选项的内容, 例如 content:snort , data为snort */static void PayloadSearchInit(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol){ OptFpList *fpl; PatternMatchData *pmd; char *data_end; char *data_dup; char *opt_data; int opt_len = 0; char *next_opt; ... /* whack a new node onto the list */ /* 以为content涉及到字符串匹配, 默认内部使用bm算法, 所以需要创建bm需要的数据结构*/ pmd = NewNode(otn, PLUGIN_PATTERN_MATCH); lastType = PLUGIN_PATTERN_MATCH; if (!data) ParseError("No Content Pattern specified!"); ... data_dup = SnortStrdup(data); data_end = data_dup + strlen(data_dup); opt_data = PayloadExtractParameter(data_dup, &opt_len); /* 解析模式串, 并构建bm算法的坏字表和好后缀表*/ ParsePattern(opt_data, otn, PLUGIN_PATTERN_MATCH); ... /* 设置bm算法的匹配函数CheckANDPatternMatch, 并将fpl链到otn->opt_func中*/ fpl = AddOptFuncToList(CheckANDPatternMatch, otn); fpl->type = RULE_OPTION_TYPE_CONTENT; pmd->buffer_func = CHECK_AND_PATTERN_MATCH; /* 匹配时需要的参数*/ fpl->context = pmd; pmd->fpl = fpl; // if content is followed by any comma separated options, // we have to parse them here. content related options // separated by semicolons go straight to the callbacks. ...}
解析完毕需要构建模式匹配引擎,这个比较复杂,需要单独一个章节进行分析,匹配过程也会使用一个章节进行分析。
转载地址:http://rvtmd.baihongyu.com/