suricata源码中的packet prefilter 以及payload prefilter

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字段的匹配函数为PrefilterPacketAckMatchPrefilterPacketAckMatch主要做了两件事情,其一就是直接将报文中的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村中少年原创文章,未经允许不得转载,博主链接这里

猜你喜欢

转载自blog.csdn.net/javajiawei/article/details/106598715