其实规则的匹配流程和加载流程是强相关的,你如何组织规则那么就会采用该种数据结构去匹配,例如你用radix tree组织海量ip规则,那么匹配的时候也是采用bit test确定前缀节点,然后逐一左右子树查询,Suricata也是如此,让我给大家简单介绍一下匹配流程。
1. run the IPonly engine
运行纯ip规则引擎匹配,由于是纯ip规则,所以只要目的ip和源ip可以匹配,我们就可以认为这条纯ip规则是可以命中的,我们把命中的规则sid加入到alert array中作为输出
2.get our rule group
这一步是获取该报文属于哪个规则分组,在规则加载中我们分别将规则的上下行端口进行分组,例如:
alert ip 1.1.1.1 80 -> 1.1.1.2 80:90(sid:1;),
alert ip 1.1.1.1 80 -> 1.1.1.2 80:85(sid:2;)
这两条规则就会以上行80:85新建一个分组包含sid为1和2,86:90再新建一个分组包含sid为1。那么这个函数的目的就会根据上下行,用当前报文的目的端口号或源端口号去匹配得到规则分组。
3.run the prefilters for packets
这一步是进行prefilter规则过滤,我们在第2步的时候获取分组sgh,这个分组包含了很多prefilter引擎列表,例如content prefilter, 它是同属该组的规则的content会以hyperscan的多模数据库的形式组织,这样运行content prefilter时可快速得到匹配到候选者。
SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, strlen(buf));
4. inspect the rules against the packet
经过第3步一系列的prefilter过滤,会得到很少量的候选者sid,这个时候我们需要逐一遍历这些规则看看是否可以真正命中。首先会匹配五元组及协议,然后会对规则option一一匹配,例如pcre,threshold等等,如果这些option全部命中,我们才认为这个报文命中了该条规则,并会产生相关告警和日志。
/* if prefilter didn't already run, we need to consider transformations */
const DetectEngineTransforms *transforms = NULL;
if (!engine->mpm) {transforms = engine->v2.transforms;
}
//->
const InspectionBuffer *buffer = engine->v2.GetData(det_ctx, transforms,f, flags, txv, list_id);
/* Inspect all the uricontents fetched on each
* transaction at the app layer */
int r = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd,NULL, f,(uint8_t *)data, data_len, offset, ci_flags,DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
//->
(void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
5. append a signature match to a packet
记录规则匹配结果