suricat主要分为引擎和规则。对于引擎功能比较多,包括协议的解码,规则的加载和解析,以及规则的识别。总的来检测的成功率主要体现在规则上。规则会随着需求的增加而增长,当规则很多的时候,识别的效率就会非常的突出。如果让每一个报文去匹配所有的规则,那么识别的效率是远远满足不了实际的要求的。因此为了提高检测效率,suricata引入了prefilter机制。
每一条规则有很多的匹配条件,只取该条规则中的其中一个匹配条件放入prefilter中,在该规则匹配命中之后,才会在后续的流程继续匹配剩下的条件;如果没有命中,则该条规则后续的条件也不会再去匹配检测,从而节省匹配的时间。
选择一条规则中哪一个匹配条件放入prefilter中,suricata提供了两种方式:
1,显示的指定匹配条件放入prefilter。针对字符串的匹配,即使用MPM多模匹配算法的匹配,使用fast_pattern关键字进行指定。例如,content:"IMSTSWebProxy"; nocase; fast_pattern;
则content:"IMSTSWebProxy";
会被方放入prefilter中。针对于非字符串的匹配,即non-MPM的匹配,使用prefilter关键字,例如,ttl:123; prefilter;
,则ttl:123;
会被方放入prefilter中。
2,使用默认的方式。如果没有像1中的显示指定prefilter,则suricata会根据自己内部的一套方法来选择默认的prefilter。如下:
1,总的原则是肯定的匹配条件优先级高于否定的匹配条件(否定的匹配指的是条件中带有!的匹配)。
2,suricata内部定义了一些关键字buffer的优先级,例如http_method优先级高于http_cookie,在http_method和http_cookie条件同时存在的时候,优先选择http_method作为prefilter。关于这些关键字buffer的优先级,这里。
3,对于像http_uri以及http_host这样具有相同优先级的buffer,则使用匹配条件中长度较长的content作为prefilter.
4,如果第三步中仍然无法确认prefilter,则根据匹配条件content的字符复杂程度进行判断,更为复杂的字符作为prefilter。关于字符串复杂度的判断算法,这里。
5,第4步中仍然无法确认的话,则根据buffer注册的id来进行判断,注册顺序建步骤2的链接。在源码中,buffer部分注册的如下:
/* NOTE: the order of these currently affects inspect
* engine registration order and ultimately the order
* of inspect engines in the rule. Which in turn affects
* state keeping */
DetectHttpUriRegister();
DetectHttpRequestLineRegister();
DetectHttpClientBodyRegister();
DetectHttpResponseLineRegister();
DetectHttpServerBodyRegister();
DetectHttpHeaderRegister();
DetectHttpHeaderNamesRegister();
DetectHttpHeadersRegister();
DetectHttpProtocolRegister();
DetectHttpStartRegister();
DetectHttpRawHeaderRegister();
DetectHttpMethodRegister();
DetectHttpCookieRegister();
DetectHttpRawUriRegister();
6,如果第五步还未能选出prefilter,则根据规则编写的顺序进行判断,即一条规则中先出现的作为prefilter,即左侧的规则优先作为prefilter。
根据上述的1,2,3,4,5,6个步骤依次进行判定,直至选出最终的prefilter。prefilter的处理方式也是引擎加规则,只是引擎是prefilter的引擎,如上所述,总计分为三中类别的prefilter引擎,如下:
typedef struct SigGroupHeadInitData_ {
MpmStore mpm_store[MPMB_MAX];
uint8_t *sig_array; /**< bit array of sig nums (internal id's) */
uint32_t sig_size; /**< size in bytes */
uint8_t protos[256]; /**< proto(s) this sgh is for */
uint32_t direction; /**< set to SIG_FLAG_TOSERVER, SIG_FLAG_TOCLIENT or both */
int whitelist; /**< try to make this group a unique one */
MpmCtx **app_mpms;
PrefilterEngineList *pkt_engines;//传输层以下的packet prefilter
PrefilterEngineList *payload_engines;//其他的传输层以上的载payload prefilter
PrefilterEngineList *tx_engines;//TLS,HTTP,DNS等应用层事物的 tx prefilter
/* port ptr */
struct DetectPort_ *port;
} SigGroupHeadInitData;
而规则则是每一条规则中抽取的一个匹配条件形成的规则库。
1,pkt_engines 即传输层以下的packet prefilter。这类匹配主要针对的是传输层及以下的支持prefilter的协议字段,例如前面提到的TTL以及TCP的ACK等二进制协议字段,通常都是进行数字的比较。在suricata中,packet prefilter的匹配都会调用PrefilterSetupPacketHeader
函数进行prefilter的注册,例如TCP ACK规则解析中prefilter就是调用了PrefilterSetupTcpAck
进行prefilter的注册,具体的用于ACK字段的匹配函数为PrefilterPacketAckMatch
。PrefilterPacketAckMatch
主要做了两件事情,其一就是直接将报文中的ACK字段和prefilter中的ACK进行比较,其二就是将成功匹配条件对应的rule id通过PrefilterAddSids
函数加入到det_ctx->pmq中,供后续的该条规则对应的其他匹配条件进行匹配。TTL等字段同理,可以看到这类的prefilter并不是很多,有13个规则关键字支持这类的prefilter。
当然这些prefilter的规则都会通过PrefilterAppendEngine
函数加入到PrefilterEngineList *pkt_engines中。在PrefilterAppendEngine
函数中可以看到对于每个prefilter条件规则都生成了一个prefilter引擎,通过sgh->init->pkt_engines链表进行组织。在实际匹配的时候,会去逐个遍历匹配每个prefilter引擎中的规则。在规则检测部分Prefilter
函数中,可以看到会去逐个两类prefilter引擎中所有的引擎,即packet引擎以及payload引擎(见第二部分所述)。
由于suricata针对传输层以下的协议也是提供了tcp.hdr以及udp.hdr等用于字符串比较的规则关键字,因此packet prefilter除了数字的直接比较方式,也是提供了字符串的匹配方式。例如PrefilterGenericMpmPktRegister函数就是注册这类的packet prefilter引擎。其匹配函数为PrefilterMpmPkt
,和所有的多模匹配一样,就是用prefilter中的规则去匹配解码后的buffer。除了tcp.hdr buffer以及udp.hdr buffer之外,调用PrefilterGenericMpmPktRegister
的还包括ipv4.hdr以及ipv6.hdr的buffer,都是这类的多模匹配。
2,payload_engines即传输层以上的载payload prefilter。suricata将一些应用层协议和无法解析的应用层协议的载荷匹配对应的prefilter称之为payload prefilter。
该种类型的prefilter通过PrefilterAppendPayloadEngine
函数注册到payload_engines中。PrefilterAppendPayloadEngine
的调用者保包括PrefilterPktStreamRegister
以及PrefilterPktPayloadRegister
。事实上从字面意思还是很容易明白的PrefilterPktStreamRegister
使用与传输层是TCP的ayload prefilter的注册,PrefilterPktPayloadRegister
既可以用TCP也可以用于UDP,这在更上层的调用函数PatternMatchPrepareGroup
中有所体现。当然上述两者的区别在于他们的匹配函数,分别为PrefilterPktStream以及PrefilterPktPayload。PrefilterPktPayload函数就是简单的单包匹配,当一个报文送来的时候,将对应prefilter中的规则和报文的载荷进行多模匹配。具体使用哪些Prefilter呢?其实可以看到调用关系SigAddressPrepareStage4->PrefilterSetupRuleGroup->PatternMatchPrepareGroup->PrefilterPktPayloadRegister
中payload prefilter的注册其实是按照规则分组建立的。也就是说有多个个规则的分组group,就有多少个payload prefilter。因此具体的packet会根据packet所属的分组去匹配该分组的prefilter引擎下所有的规则(该分组每个规则取其中一个匹配条件形成的)。这里面可以看到分组能够减少无用的prefilter匹配,通常来说分组越多,匹配效率越高。
PrefilterPktPayloadRegister
指的是单包的匹配,而PrefilterPktStreamRegister指的是传输层为TCP协议,有的规则针对的载荷不再是简单的单包匹配而是需要将将TCP报文重组之后(例如stream_size关键字),形成完整的内容在进行匹配。cong其PrefilterPktStream
匹配函数中可以看到,是先进行重组内容的匹配,即StreamReassembleRaw
函数中的StreamMpmFunc
回调对重组后的数据进行匹配。如果改包不是重组包,即if ((p->flags & (PKT_NOPAYLOAD_INSPECTION|PKT_STREAM_ADD)) == 0)
,则进行单包匹配。
当然无论对于的是单包还是tcp reassemble之后的匹配,注册的prefilter引擎都会挂到sgh->init->payload_engines
结构下,在检测阶段Prefilter函数中进行调用。可以看到Prefilter函数中只有两种类型的引擎,即packet prefilter以及payload prefilter。因为事物de prefilter是在DetectRunPrefilterTx中进行调用。由于tx_engines涉及到事物的概念,将在介绍事物概念之后再进行介绍。
本文为CSDN村中少年原创文章,未经允许不得转载,博主链接这里。