本文介紹一些規則中的關鍵字處理過程,主要以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村中少年原創文章,未經允許不得轉載,博主鏈接這裏。