【作者主頁】只道當時是尋常
【專欄介紹】Suricata入侵檢測。專注網絡、主機安全,歡迎關注與評論。
1. 概要
👋?本文聚焦Suricata網絡安全引擎的協議解析器實現,詳細剖析HTTP、SSL/TLS、FTP、SSH、SMTP等協議的解析流程。
2. 源碼分析
2.1 HTTP協議解析器
RegisterHTPParsers 函數實現注冊 HTTP 協議解析器。下面是該函數代碼實現:
/*** \brief Register the HTTP protocol and state handling functions to APP layer* of the engine.*/
void RegisterHTPParsers(void)
{SCEnter();const char *proto_name = "http";/** HTTP */if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP1, proto_name);if (HTPRegisterPatternsForProtocolDetection() < 0)return;} else {SCLogInfo("Protocol detection and parser disabled for %s protocol",proto_name);return;}if (AppLayerParserConfParserEnabled("tcp", proto_name)) {AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateAlloc, HTPStateFree);AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateTransactionFree);AppLayerParserRegisterGetTxFilesFunc(IPPROTO_TCP, ALPROTO_HTTP1, HTPGetTxFiles);AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateGetAlstateProgress);AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateGetTxCnt);AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateGetTx);AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_HTTP1, HTP_REQUEST_COMPLETE, HTP_RESPONSE_COMPLETE);AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateGetEventInfo);AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_HTTP1, HTPStateGetEventInfoById);AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_HTTP1, HTPGetTxData);AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_HTTP1, HTPGetStateData);AppLayerParserRegisterSetStreamDepthFlag(IPPROTO_TCP, ALPROTO_HTTP1, AppLayerHtpSetStreamDepthFlag);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP1, STREAM_TOSERVER, HTPHandleRequestData);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP1, STREAM_TOCLIENT, HTPHandleResponseData);SC_ATOMIC_INIT(htp_config_flags);/* This parser accepts gaps. */AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_HTTP1, APP_LAYER_PARSER_OPT_ACCEPT_GAPS);AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_TOCLIENT);/* app-layer-frame-documentation tag start: registering relevant callbacks */AppLayerParserRegisterGetFrameFuncs(IPPROTO_TCP, ALPROTO_HTTP1, HTTPGetFrameIdByName, HTTPGetFrameNameById);/* app-layer-frame-documentation tag end: registering relevant callbacks */HTPConfigure();} else {SCLogInfo("Parsed disabled for %s protocol. Protocol detection""still on.", proto_name);}
#ifdef UNITTESTSAppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_HTTP1, HTPParserRegisterTests);
#endifSCReturn;
}
2.1.1 配置開啟
AppLayerProtoDetectConfProtoDetectionEnabled 函數是一個通用的配置開啟檢查函數,通過第二個參數傳入"http"字符串用于檢測配置文件中 app-layer.protocols.http.enabled 選項是否開啟,若開啟則繼續注冊操作,如果未開啟則終止 HTTP 協議解析器的注冊操作。
2.1.2 協議識別
在 Suricata 中,HTTP 協議識別通過在應用數據中匹配特定模式串實現,協議識別器針對報文流方向(STREAM_TOSERVER 或 STREAM_TOCLIENT)定義了不同的模式串,在 HTPRegisterPatternsForProtocolDetection 函數中實現了模式串的注冊。
(1)客戶端 -> 服務端
在下面這些字符串后面添加空格(|20|)或者制表符(|09|)作為模式串。
"GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS","CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN", "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE", "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL".
(2)服務端 -> 客戶端
下面字符串作為模式串。
"HTTP/0.9", "HTTP/1.0", "HTTP/1.1".
2.2 SSL協議解析器
RegisterSSLParsers 函數實現注冊 SSL 協議解析器。下面是該函數代碼實現:
/*** \brief Function to register the SSL protocol parser and other functions*/
void RegisterSSLParsers(void)
{const char *proto_name = "tls";SC_ATOMIC_INIT(ssl_config.enable_ja3);/** SSLv2 and SSLv23*/if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {AppLayerProtoDetectRegisterProtocol(ALPROTO_TLS, proto_name);if (SSLRegisterPatternsForProtocolDetection() < 0)return;if (RunmodeIsUnittests()) {AppLayerProtoDetectPPRegister(IPPROTO_TCP,"443",ALPROTO_TLS,0, 3,STREAM_TOSERVER,SSLProbingParser, NULL);} else {if (AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,proto_name, ALPROTO_TLS,0, 3,SSLProbingParser, NULL) == 0) {SCLogConfig("no TLS config found, ""enabling TLS detection on port 443.");AppLayerProtoDetectPPRegister(IPPROTO_TCP,"443",ALPROTO_TLS,0, 3,STREAM_TOSERVER,SSLProbingParser, NULL);}}} else {SCLogConfig("Protocol detection and parser disabled for %s protocol",proto_name);return;}if (AppLayerParserConfParserEnabled("tcp", proto_name)) {AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER,SSLParseClientRecord);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOCLIENT,SSLParseServerRecord);AppLayerParserRegisterGetFrameFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetFrameIdByName, SSLStateGetFrameNameById);AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfo);AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfoById);AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateAlloc, SSLStateFree);AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER);AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TLS, SSLStateTransactionFree);AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TLS, SSLGetTx);AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxData);AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetStateData);AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxCnt);AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetAlstateProgress);AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_TLS, TLS_STATE_FINISHED, TLS_STATE_FINISHED);ConfNode *enc_handle = ConfGetNode("app-layer.protocols.tls.encryption-handling");if (enc_handle != NULL && enc_handle->val != NULL) {SCLogDebug("have app-layer.protocols.tls.encryption-handling = %s", enc_handle->val);if (strcmp(enc_handle->val, "full") == 0) {ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_FULL;} else if (strcmp(enc_handle->val, "bypass") == 0) {ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;} else if (strcmp(enc_handle->val, "default") == 0) {ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;} else {ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;}} else {/* Get the value of no reassembly option from the config file */if (ConfGetNode("app-layer.protocols.tls.no-reassemble") == NULL) {int value = 0;if (ConfGetBool("tls.no-reassemble", &value) == 1 && value == 1)ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;} else {int value = 0;if (ConfGetBool("app-layer.protocols.tls.no-reassemble", &value) == 1 && value == 1)ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;}}SCLogDebug("ssl_config.encrypt_mode %u", ssl_config.encrypt_mode);#ifdef HAVE_JA3CheckJA3Enabled();
#endif /* HAVE_JA3 */
#ifdef HAVE_JA4CheckJA4Enabled();
#endif /* HAVE_JA4 */if (g_disable_hashing) {if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {SCLogWarning("MD5 calculation has been disabled, disabling JA3");SC_ATOMIC_SET(ssl_config.enable_ja3, 0);}if (SC_ATOMIC_GET(ssl_config.enable_ja4)) {SCLogWarning("Hashing has been disabled, disabling JA4");SC_ATOMIC_SET(ssl_config.enable_ja4, 0);}} else {if (RunmodeIsUnittests()) {
#ifdef HAVE_JA3SC_ATOMIC_SET(ssl_config.enable_ja3, 1);
#endif /* HAVE_JA3 */
#ifdef HAVE_JA4SC_ATOMIC_SET(ssl_config.enable_ja4, 1);
#endif /* HAVE_JA4 */}}} else {SCLogConfig("Parsed disabled for %s protocol. Protocol detection""still on.", proto_name);}return;
}
2.2.1 配置開啟
AppLayerProtoDetectConfProtoDetectionEnabled 函數是一個通用的配置開啟檢查函數,通過第二個參數傳入"tls"字符串用于檢測配置文件中 app-layer.protocols.tls.enabled 選項是否開啟,若開啟則繼續注冊操作,如果未開啟則終止 SSL 協議解析器的注冊操作。
2.2.2 協議識別
在 Suricata 中,SSL 協議識別通過在應用數據中匹配特定模式串實現,協議識別器針對報文流方向(STREAM_TOSERVER 或 STREAM_TOCLIENT)定義了不同的模式串,在 SSLRegisterPatternsForProtocolDetection 函數中實現了模式串的注冊。
Suricata 根據報文方向以及應用報文中攜帶的特定模式串判斷報文所在流的協議類型。下面是匹配規則(其中偏移為從應用數據 0 字節開始偏移,深度為從偏移開始指定字節內包含特定模式串):
-
客戶端->服務端,偏移為 2,深度為 5,命中"|01 00 02|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|01 03 00|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|16 03 00|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|01 03 01|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|16 03 01|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|01 03 02|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|16 03 02|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|01 03 03|"字節序列。
-
客戶端->服務端,偏移為 0,深度為 3,命中"|16 03 03|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|15 03 00|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|16 03 00|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|17 03 00|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|15 03 01|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|16 03 01|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|17 03 01|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|15 03 02|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|16 03 02|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|17 03 02|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|15 03 03|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|16 03 03|"字節序列。
-
服務端->客戶端,偏移為 0,深度為 3,命中"|17 03 03|"字節序列。
2.3 FTP協議解析器
RegisterFTPParsers 函數實現注冊 FTP 協議解析器。下面是該函數代碼實現:
void RegisterFTPParsers(void)
{const char *proto_name = "ftp";const char *proto_data_name = "ftp-data";/** FTP */if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);if (FTPRegisterPatternsForProtocolDetection() < 0 )return;AppLayerProtoDetectRegisterProtocol(ALPROTO_FTPDATA, proto_data_name);}if (AppLayerParserConfParserEnabled("tcp", proto_name)) {AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,FTPParseRequest);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,FTPParseResponse);AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree);AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx);AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxData);AppLayerParserRegisterGetTxIterator(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxIterator);AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetStateData);AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_FTP, FTPLocalStorageAlloc,FTPLocalStorageFree);AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt);AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress);AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_FTP, FTP_STATE_FINISHED, FTP_STATE_FINISHED);AppLayerRegisterExpectationProto(IPPROTO_TCP, ALPROTO_FTPDATA);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER,FTPDataParseRequest);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOCLIENT,FTPDataParseResponse);AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateAlloc, FTPDataStateFree);AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER | STREAM_TOCLIENT);AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateTransactionFree);AppLayerParserRegisterGetTxFilesFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateGetTxFiles);AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTx);AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxData);AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetStateData);AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxCnt);AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetAlstateProgress);AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_FTPDATA, FTPDATA_STATE_FINISHED, FTPDATA_STATE_FINISHED);AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info);AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info_by_id);sbcfg.buf_size = 4096;sbcfg.Calloc = FTPCalloc;sbcfg.Realloc = FTPRealloc;sbcfg.Free = FTPFree;FTPParseMemcap();} else {SCLogInfo("Parsed disabled for %s protocol. Protocol detection""still on.", proto_name);}FTPSetMpmState();#ifdef UNITTESTSAppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
#endif
}
2.3.1 配置開啟
AppLayerProtoDetectConfProtoDetectionEnabled 函數是一個通用的配置開啟檢查函數,通過第二個參數傳入"ftp"字符串用于檢測配置文件中 app-layer.protocols.ftp.enabled 選項是否開啟,若開啟則繼續注冊操作,如果未開啟則終止 FTP 協議解析器的注冊操作。
2.3.2 協議識別
在 Suricata 中,FTP 協議識別通過在應用數據中匹配特定模式串實現,協議識別器針對報文流方向(STREAM_TOSERVER 或 STREAM_TOCLIENT)定義了不同的模式串,在 FTPRegisterPatternsForProtocolDetection 函數中實現了模式串的注冊。
Suricata 根據報文方向以及應用報文中攜帶的特定模式串判斷報文所在流的協議類型。下面是匹配規則(其中偏移為從應用數據 0 字節開始偏移,深度為從偏移開始指定字節內包含特定模式串):
-
服務端->客戶端,偏移為 0,深度為 5,命中"220 ("字符串。
-
客戶端->服務端,偏移為 0,深度為 4,命中"FEAT"字符串。
-
客戶端->服務端,偏移為 0,深度為 5,命中"USER "字符串。
-
客戶端->服務端,偏移為 0,深度為 5,命中"PASS "字符串。
-
客戶端->服務端,偏移為 0,深度為 5,命中"PORT "字符串。
2.4 SSH協議解析器
RegisterSSHParsers 函數實現注冊 SSH 協議解析器。下面是該函數代碼實現:
void RegisterSSHParsers(void)
{const char *proto_name = "ssh";if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {AppLayerProtoDetectRegisterProtocol(ALPROTO_SSH, proto_name);if (SSHRegisterPatternsForProtocolDetection() < 0)return;/* Check if we should generate Hassh fingerprints */int enable_hassh = SSH_CONFIG_DEFAULT_HASSH;const char *strval = NULL;if (ConfGet("app-layer.protocols.ssh.hassh", &strval) != 1) {enable_hassh = SSH_CONFIG_DEFAULT_HASSH;} else if (strcmp(strval, "auto") == 0) {enable_hassh = SSH_CONFIG_DEFAULT_HASSH;} else if (ConfValIsFalse(strval)) {enable_hassh = SSH_CONFIG_DEFAULT_HASSH;} else if (ConfValIsTrue(strval)) {enable_hassh = true;}if (RunmodeIsUnittests() || enable_hassh) {rs_ssh_enable_hassh();}}SCLogDebug("Registering Rust SSH parser.");rs_ssh_register_parser();#ifdef UNITTESTSAppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SSH, SSHParserRegisterTests);
#endif
}
2.4.1 配置開啟
AppLayerProtoDetectConfProtoDetectionEnabled 函數是一個通用的配置開啟檢查函數,通過第二個參數傳入"ssh"字符串用于檢測配置文件中 app-layer.protocols.ssh.enabled 選項是否開啟,若開啟則繼續注冊操作,如果未開啟則終止 SSH 協議解析器的注冊操作。
2.4.2 協議識別
Suricata 在 SSHRegisterPatternsForProtocolDetection 函數中實現模式串的注冊。
Suricata 根據報文方向以及應用報文中攜帶的特定模式串判斷報文所在流的協議類型。下面是匹配規則(其中偏移為從應用數據 0 字節開始偏移,深度為從偏移開始指定字節內包含特定模式串):
-
服務端->客戶端,偏移為 0,深度為 4,命中"SSH-"字符串。
-
客戶端->服務端,偏移為 0,深度為 4,命中"SSH-"字符串。
2.5 SMTP協議解析器
RegisterSMTPParsers 函數實現注冊 SMTP 協議解析器。下面是該函數代碼實現:
void RegisterSMTPParsers(void)
{const char *proto_name = "smtp";if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {AppLayerProtoDetectRegisterProtocol(ALPROTO_SMTP, proto_name);if (SMTPRegisterPatternsForProtocolDetection() < 0 )return;} else {SCLogInfo("Protocol detection and parser disabled for %s protocol.",proto_name);return;}if (AppLayerParserConfParserEnabled("tcp", proto_name)) {AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateAlloc, SMTPStateFree);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOSERVER,SMTPParseClientRecord);AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SMTP, STREAM_TOCLIENT,SMTPParseServerRecord);AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetEventInfo);AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetEventInfoById);AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPLocalStorageAlloc,SMTPLocalStorageFree);AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateTransactionFree);AppLayerParserRegisterGetTxFilesFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetTxFiles);AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetAlstateProgress);AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTxCnt);AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_SMTP, SMTPStateGetTx);AppLayerParserRegisterGetTxIterator(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetTxIterator);AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetTxData);AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_SMTP, SMTPGetStateData);AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_SMTP, 1, 1);} else {SCLogInfo("Parsed disabled for %s protocol. Protocol detection""still on.", proto_name);}SMTPSetMpmState();SMTPConfigure();#ifdef UNITTESTSAppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SMTP, SMTPParserRegisterTests);
#endifreturn;
}
2.5.1 配置開啟
AppLayerProtoDetectConfProtoDetectionEnabled 函數是一個通用的配置開啟檢查函數,通過第二個參數傳入"smtp"字符串用于檢測配置文件中 app-layer.protocols.smtp.enabled 選項是否開啟,若開啟則繼續注冊操作,如果未開啟則終止 smtp 協議解析器的注冊操作。
2.5.2 協議識別
Suricata 在 SMTPRegisterPatternsForProtocolDetection 函數中實現模式串的注冊。
Suricata 根據報文方向以及應用報文中攜帶的特定模式串判斷報文所在流的協議類型。下面是匹配規則(其中偏移為從應用數據 0 字節開始偏移,深度為從偏移開始指定字節內包含特定模式串):
-
客戶端->服務端,偏移為 0,深度為 4,命中"EHLO"字符串。
-
客戶端->服務端,偏移為 0,深度為 4,命中"QUIT"字符串。
2.6 總結
上述協議類型僅為 Suricata 支持的部分協議。除模式串匹配外,其還支持其他識別方式:如 MQTT 協議解析器會監控 1883 端口流量,通過探測函數解析應用數據,若格式符合 MQTT 協議規范則判定為 MQTT 流。此外,部分協議識別器采用 Rust 語言編寫并嵌入 C 語言工程。如需了解更多協議解析器實現,可關注`AppLayerParserRegisterProtocolParsers`函數。