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村中少年原創文章,未經允許不得轉載,博主鏈接這裏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章