suricata UT測試用例中使用的幾個重要的輔助函數

對於suricata UT用例來說,很多時候需要構造報文作爲輸入,有的時候還要構造流作爲輸入,獲取報文匹配結果。爲此在suricata中使用util-unittest-helper.c文件來組織這些用於構造UT用例的輔助函數。

unittest-helper.c文件是由編譯宏控制編譯的,即#ifdef UNITTESTS,在正式的發佈版本中不會被編進入執行程序中,但是在運行UT的時候只要定義UNITTESTS,即可以運行,如圖1:
在這裏插入圖片描述
圖1

util-unittest-helper.c文件主要分爲兩大類,即輔助的構造函數以及針對這次構造函數的測試用例。輔助構造函數又分爲如下幾類:

1,構造報文相關
構造報文最基本的是UTHBuildPacketReal函數,該函數的作用是構造特定IP以及特定端口的TCP,UDP和ICMP報文,該函數的入參就是報文payload數據及其長度,以及五元組。該函數內部的實現也是很明確,即申請Packet這樣幹一個內存空間,將入參的值賦上後即完成了報文的創建。因爲在外部傳遞到引擎的報文最終都會轉化爲Packet結構體進行處理。可以看到很多的測試用例都會使用該函數進行報文的創建,例如DetectDnsQueryTest04。當然此函數針構造的是IPV4報文,如果想要構造IPV6報文,則可以使用UTHBuildPacketIPV6Real函數,由此可見suricata內置是支持IPV6的。

在一條用例執行完畢之後,需要對於申請的報文空間進行釋放,就需要使用UTHFreePacket進行內存的釋放。

UTHBuildPacketReal爲最原始的構造報文函數,還有一些函數封裝了此函數提供了更多差異化的函數構造功能,如下:

UTHBuildPacket使用默認的IP和端口進行報文構造,使用者只需要傳輸payload內容,長度和協議三個參數即可。

UTHBuildPacketSrcDst使用了默認端口,需要payload內容,長度,協議以及目的源IP五個參數即可。

UTHBuildPacketSrcDstPorts使用了默認IP,需要payload內容,長度,協議以及目的源端口五個參數即可。因爲對於有的用例來說需要關注端口而不關注IP,則使用UTHBuildPacketSrcDstPorts即可,其他同理。

上述的函數是構造單個報文,如果想要構造多個報文的話使用一組循環即可。上述構造報文的函數還有一個問題在於,其入參關注的是五元組,那麼除了五元組之外的,比如TCP的ACK字段是沒有構造的,對於這種情況該怎麼辦呢?可以使用UTHBuildPacketFromEth,對於構造單個報文來說,比較簡潔的辦法是使用一些真實的報文數據進行導入。可以看到函數的入參便是原始的報文數據,以及數據大小還有報文個數。其本質是通過引擎的解碼函數DecodeEthernet來填充Packet結構體的內容。正常來說引擎的工作模式也是對於接收到的數據,送入DecodeEthernet進行解碼,然後填充Packet結構體各個字段的內容,對於IP,TCP的各個字段在Packet結構體中就會有所體現。由此你也可以看出引擎是從DecodeEthernet開始處理的,當然該函數還是要被wrapper的,因爲外層還有一些邏輯。對於構造多個詳細報文來說,使用UTHBuildPacketArrayFromEth函數即可。釋放多個報文的函數爲UTHFreePackets。

2,構造流相關

引擎中使用流表來管理流,如果想要創建單條流,使用UTHBuildFlow函數。同報文創建相同,該函數爲單條流申請內存,空間並將四元組以及IPV4或者IPV6對應的標誌位賦值即生成流。

由於特定的報文屬於某一個流,因此實用 UTHAssignFlow函數指定報文的歸屬,其實內容很簡單就是將報文結構體中的flow和標誌位關聯即可。其實可以想象當引擎收到一個報文的時候,會通過四元組去查找流表,如果流表中流已經存在,將報文進行關聯,如果不存在,新建流在關聯。

釋放流內存的函數爲UTHFreeFlow。

另外還發現存在UTHAddStreamToFlow,UTHAddSessionToFlow等函數。首先flow,session,stream這幾個概念如下:

flow:通常所說的流,包括TCP流以及UDP流,由五元組唯一確定的通信雙方的虛擬鏈接。
stream:這個就不像flow是一個比較通用的概念,stream是suricata中特指TCP的一條流的上行鏈接或者下行鏈接。通常來說flow包含了雙向的報文,而一條stream特指TCP的上行報文或者下行報文內容。
session:session也是suricata中爲了處理TCP的內容而設置的概念,可以說session和tcp 的flow總體上是指的一回事,當然兩個結構體並不相同。由於flow除了表示TCP流之外,還能夠表示UDP流,因此flow一個比較通用的結構體。由CP流的處理遠比UDP複雜,包括各種報文重組等等,因此使用了session來表示獨立於flow之外的一些功能,比如報文重組,因此可以簡單的理解session就是一個TCP流的處理結構。

由於TCP的處理比之UDP要複雜許多,爲此suricata提供了stream 表示單向的流內容,使用session來進行額外的流處理。

UTHAddSessionToFlow即申請一個session的結構空間,和flow進行關聯。由於在session結構中存在了client以及server的stream,因此在UTHAddStreamToFlow的主要工作就是將接受到的數據加入到上下行stream的buffer中。詳細使用可以參考DetectUriSigTest06用例。UTHRemoveSessionFromFlow表示從flow中去除session。

3,構造匹配規則相關
UTHAppendSigs向引擎上下文加入規則。通常來說規則文件是以.rules文件提供的,文件中每一行就是一條規則,通過文件加載形式賦值給引擎。但是在UT測試的時候,需要時用UTHAppendSigs函數給引擎上下文添加規則,其中入參包括引擎上下文結構,規則數組,即一行行規則的字符串數組,還有規則個數。其本質也是調用引擎的DetectEngineAppendSig函數完成具體任務,這個函數也是規則加載過程中非常重要的函數。可以參考SCSigOrderingTest12用例中的使用方式。

UTHMatchPackets的入參是引擎上下文,以及若干個報文,目的是讓報文去匹配規則。在函數中封裝了引擎最爲重要的一些函數,包括SigGroupBuild將解析的規則轉換爲內部的結構;DetectEngineThreadCtxInit初始化引擎上下文;SigMatchSignatures->DetectRun規則檢測等等。構造UTHMatchPackets的目的在於作爲用例使用的公共部分(但是實際使用的並不多),更多的用例會單獨的使用SigGroupBuild以及DetectEngineThreadCtxInit等函數構造測試邏輯。但是通過UTHMatchPackets可以瞭解到報文匹配規則前後的一些設置,執行的邏輯,這是很重要的。

UTHCheckPacketMatchResults檢查報文匹配的結果是否正確,調用PacketAlertCheck進行檢查。通過PacketAlertCheck也可以得知,匹配的結果存儲在p->alerts.alerts[i].s->id中。

用例SCSigOrderingTest12中正是體現了上述的內容,先是示給引擎上下文添加規則UTHAppendSigs;然後通過報文進行規則的匹配UTHMatchPackets;最後檢查匹配結果UTHCheckPacketMatchResults。

UTHPacketMatchSig中使用報文去匹配引擎上下文中的規則,UTHPacketMatchSig的入參爲報文和規則,其目的是直接使用報文匹配指定的規則。對比UTHMatchPackets函數,他們用到的執行邏輯都是一致的,即SigGroupBuild將解析的規則轉換爲內部的結構;DetectEngineThreadCtxInit初始化引擎上下文;SigMatchSignatures->DetectRun規則檢測等等。UTHPacketMatchSigMpm比之UTHPacketMatchSig更近一步,增加了多模匹配算法的類型這個入參。

UTHParseSignature測試規則解析是否正確。

UTHGenericTest對前面介紹的函數又進行了封裝,即直接扔進去報文,規則,預期結果,然後給出檢測結果是否符合預期。

明白了上述幾類輔助函數,相信對於該文件中剩下的用例也是很容易理解的。

本文爲CSDN村中少年原創文章,未經允許不得轉載,博主鏈接這裏

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