suricata UT之SigTableSetup中關鍵字的功能函數解析一

本文介紹一些規則中的關鍵字處理過程,主要以TCP協議的ACK爲例進行說明,這類關鍵字代表了二進制協議的關鍵字解析方式。

RegisterUnittests中對所有的用例進行註冊,其中SigRegisterTests以及SigTableRegisterTests是保證規則成功解析加載,同時能夠匹配符合條件的報文的用例。SigTableRegisterTests中的用例由全局變量sigmatch_table來組織,sigmatch_table變量中的用例部分RegisterTests是在SigTableSetup中進行註冊的。

函數SigTableSetup對於所有的關鍵字對應的功能都進行了註冊。其中包括常見的key:value形式的關鍵字,以及只有key的關鍵字。本章節先講述一下key:value這些較爲常見的關鍵字,如下這些關鍵字還是很容易理解的。

DetectSidRegister();
DetectPriorityRegister();
DetectPrefilterRegister();
DetectRevRegister();
DetectClasstypeRegister();
DetectReferenceRegister();
DetectTagRegister();
DetectThresholdRegister();
DetectMetadataRegister();
DetectMsgRegister();
DetectAckRegister();
DetectSeqRegister();
DetectContentRegister();
DetectUricontentRegister();

suricata使用sigmatch_table管理所有關鍵字所具備的功能,依次將對應的功能函數填充sigmatch_table數組中的指定字段。

以SigTableSetup函數中的DetectAckRegister函數爲例加以說明,如下:

void DetectAckRegister(void)
{
    sigmatch_table[DETECT_ACK].name = "ack";
    sigmatch_table[DETECT_ACK].desc = "check for a specific TCP acknowledgement number";
    sigmatch_table[DETECT_ACK].url = DOC_URL DOC_VERSION "/rules/header-keywords.html#ack";
    sigmatch_table[DETECT_ACK].Match = DetectAckMatch;
    sigmatch_table[DETECT_ACK].Setup = DetectAckSetup;
    sigmatch_table[DETECT_ACK].Free = DetectAckFree;

    sigmatch_table[DETECT_ACK].SupportsPrefilter = PrefilterTcpAckIsPrefilterable;
    sigmatch_table[DETECT_ACK].SetupPrefilter = PrefilterSetupTcpAck;

    sigmatch_table[DETECT_ACK].RegisterTests = DetectAckRegisterTests;
}

Match對應的函數如下:

static int DetectAckMatch(DetectEngineThreadCtx *det_ctx,
                          Packet *p, const Signature *s, const SigMatchCtx *ctx)
{
    const DetectAckData *data = (const DetectAckData *)ctx;

    /* This is only needed on TCP packets */
    if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
        return 0;
    }

    return (data->ack == TCP_GET_ACK(p)) ? 1 : 0;
}

該函數會在檢測報文的時候執行,比較規則中的ACK和報文中的ACK是否匹配。通過其入參可以得知Packet *p爲需要匹配的報文,SigMatchCtx *ct存儲的是從規則文件中解析出來的ACK字段的值存。
該函數功能是。

那麼Match函數再什麼時候執行呢?此處只是註冊了match 的功能。
1,Match函數的執行是在DetectEngineInspectRulePacketMatches函數中,該函數中s->sm_arrays[DETECT_SM_LIST_MATCH]數組中的Match函數都會得到執行。

2,DetectEngineInspectRulePacketMatches函數作爲一個回調函數註冊在報文檢測引擎的v1.Callback字段中。註冊階段是在將加載的規則轉換成引擎上下文下掛的內部結構階段,執行路徑爲
LoadSignatures->SigLoadSignatures->SigGroupBuild->SigMatchPrepare->DetectEnginePktInspectionSetup->DetectEnginePktInspectionAppend。
DetectEnginePktInspectionSetup->DetectEnginePktInspectionAppend中其實會註冊兩種回調函數,一種就是像ACK這種,屬於二進制協議字段的匹配,往往是數字的匹配,例如DetectEngineInspectRulePacketMatches,會執行s->sm_arrays[DETECT_SM_LIST_MATCH]中的Match內容。另外一隻是字符串的匹配,往往匹配的是載荷內容或者文本協議(HTTP)的內容,例如DetectEngineInspectRulePayloadMatches,關於字符串匹配這種關鍵字將會在下一章講述。

3,最終的執行路徑爲DetectRun->DetectRulePacketRules->DetectEnginePktInspectionRun中的e->v1.Callback,v1.Callback即步驟1,2中的內容

Setup 對應的函數爲DetectAckSetup,該函數的目的是在規則解析的時候執行,將一行字符串的規則轉換爲規則上下文中對應的字段值。當然不同的關鍵字其解析的key value 的組織形式會有一定的差異,如下:

static int DetectAckSetup(DetectEngineCtx *de_ctx, Signature *s, const char *optstr)
{
    DetectAckData *data = NULL;
    SigMatch *sm = NULL;

    data = SCMalloc(sizeof(DetectAckData));
    if (unlikely(data == NULL))
        goto error;

    sm = SigMatchAlloc();
    if (sm == NULL)
        goto error;

    sm->type = DETECT_ACK;

    if (-1 == ByteExtractStringUint32(&data->ack, 10, 0, optstr)) {
        goto error;
    }
    sm->ctx = (SigMatchCtx*)data;

    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
    s->flags |= SIG_FLAG_REQUIRE_PACKET;

    return 0;

error:
    if (data)
        SCFree(data);
    if (sm)
        SigMatchFree(sm);
    return -1;

}

optstr爲規則文本中對應的ACK值,會被解析到SigMatch *sm 這樣的結構中,sm這掛在s->init_data>smlists[DETECT_SM_LIST_MATCH],Signature *s就是規則解析出來之後再內存中的存儲結構。對於規則中像ACK這種會進行數字匹配的key:value形式(例如seq),都是放在Signature *s中的s->init_data->smlists[DETECT_SM_LIST_MATCH]數組中。對於一些輔助字段例如sid,metadat往往直接掛在引擎上下文結構下面,例如DetectSidRegister,以及DetectMetadataRegister。

Free 對應的函數爲DetectAckFree,功能是內存的回收。由於在Setup階段會爲規則申請對應的空間,在清理的時候需要釋放這些空間。

當然對於ACK這樣的關鍵字還支持prefilter,關於prefilter後面會抽出單獨一篇進行介紹,這裏不再贅述。

RegisterTests對應的就是對於該關鍵字UT用例的註冊,相應的函數爲DetectAckRegisterTests。在該函數中,註冊了一條AC規則的解析用例DetectAckSigTest01。該用例目的很明確,就是將構造的TCP報文,設置特定的ACK值,送入到載入了若干個帶有ACK的規則的引擎中,查看最終的結果是否符合預期。具體包含如下步驟:

  • 第一步,構造報文用到了前一篇文章中提及的UTHBuildPacket函數,並設置特定的ACK。
  • 第二步,函數DetectEngineCtxInit初始化引擎的上下文。
  • 第三步,SigInit加載若干個有效和非法的帶有ACK的規則。
  • 第四步,SigGroupBuild將加載解析好的規則轉化爲引擎運行時的結構,主要是初始化引擎上下文的各個字段。
  • 第五步,由於suricata是多線程的架構,因此可能存在多個檢測線程,DetectEngineThreadCtxInit就是初始化特定的檢測線程的數據。
  • 第六步,SigMatchSignatures入參爲引擎下文上,報文,目的是對於報文進行規則檢測匹配。可以看到最終調用的是DetectRun函數。
  • 第七步,PacketAlertCheck檢測報文的匹配結果是否是特定的sid。可以看到匹配的結果存儲在報文上下文中,即p->alerts.alerts[i].s->id
  • 第八步,上述各個過程涉及到的內存清理,即SigGroupCleanup,DetectEngineThreadCtxDeinit等過程。上述的Free函數在此處就有執行,執行路徑爲SigCleanSignatures->SigFree->SigMatchFree->sigmatch_table[sm->type].Free

其實多數關鍵字用例,都是遵循着上述的7個步驟,只是在構造報文和規則方面存在差異。上述七步也是suricata迎請最爲重要的代碼部分,涵蓋了規則加載,解析,引擎上下文構造,規則匹配等多個過程,這也是學習suricata最爲重要的一些函數。

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

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