附录一:初始化插件函数
void InitPlugIns()
void InitPreprocessors()
void InitOutputPlugins()
三个函数都在plugbase.c中,并都转入RegisterXXX()函数调用。举个例子:
void RegisterOutputPlugin(char *keyword, int type, void (*func) (u_char *))
{
OutputKeywordList *idx;
idx = OutputKeywords;
if(idx == NULL)
{
OutputKeywords = (OutputKeywordList *) calloc(sizeof(OutputKeywordList
),sizeof(char));
idx = OutputKeywords;
}
else
{
while(idx->next != NULL)
{
if(!strcasecmp(idx->entry.keyword, keyword))
{
FatalError("%s(%d) => Duplicate output keyword!\n", file_name,
file_line);
}
idx = idx->next;
}
idx->next = (OutputKeywordList *) calloc(sizeof(OutputKeywordList), si
zeof(char));
idx = idx->next;
}
idx->entry.keyword = (char *) calloc(strlen(keyword) + 1, sizeof(char));
strncpy(idx->entry.keyword, keyword, strlen(keyword)+1);
idx->entry.node_type = (char) type;
idx->entry.func = (void *) func;
}
本函数最终得到OutputKeywordList 链表指针,其他两个插件类似,得到 PreprocessLis
t 链表指针和KeywordXlateList链表指针。这三个链表指针都是plugbase.c文件中的全局
变量,且都是对应的OutputKeyNode/PreprocessNode/KeywordXlate结构的链表头,这些结
构都在plugbase.h头文件中定义,它们都包括用来查找的插件名和要用到的处理从规则配
置文件中传过来参数的插件初始化函数。具体要用到那些插件是在parser.c文件中ParseR
ule函数实现。
附录二: SNORT 的三维链表
处理规则文件后,SNORT使用一个三维链表来存储信息以便和所抓的封包进行数据查找匹配
。
RuleList是parser.c文件中的一个全局变量。该指针是一串RuleListNode结构的头指针。
每个RuleListNode结构中都有一个指向ListHead结构的指针。
每个ListHead结构又包括四个RuleTreeNode结构(IpList/TcpList/UdpList/IcmpList)的指
针和两个_OutputFuncNode结构(LogList/AlertList)的指针。
LogList/AlertList是OutputFuncNode结构链表的头指针,是在plugbase.c文件中AddFunc
ToOutputList函数实现的。该函数被初始化输出插件函数InitOutputPlugins调用。这两个
链表被用于SNORT调用输出插件输出信息(记录日志或报警)。
四个RuleTreeNode结构的头指针详见下图:
以上就是三维链表结构,这是由parse.c文件中的ParseRule函数(调用ProcessHeadNode函
数和ParseRuleOptions函数)实现。详见附录三。
附录三:解析ParseRule初始化函数
ParseRule函数对配置文件的每一行(从ParseRuleFile 函数传过来)进行解析。可以分成
两大部分解析:规则头和规则体。举例来说:
alert tcp any 1234 -> 192.168.1.0/24 80 (content:”|00 01 86 a5|”; msg:”moun
td access”
|------------------- Header ------------------|---------------------- Options
------------------------|
规则头包括:规则动作(alert/log/pass/activate/dynamic),协议(tcp),源地址(any),
源端口(1234),方向操作符(“->”),目的地址(192.168.1.0/24),目的端口(80)。
规则选项组成了snort入侵检测引擎的核心,既易用又强大还灵活。snort中有42个规则选
项关键字,如 msg/logto/ipoption/content/classtype等,要提请注意的是选项content、
content-list和uricontent,它们允许用户设置规则在包的净荷中搜索指定的内容并根据
内容触发响应。当进行content选项模式匹配时,Boyer-Moore模式匹配函数被调用,并且
对包的内容进行检查(很花费计算能力)。如果包的净荷中包含的数据确切地匹配了参数
的内容,这个检查成功并且该规则选项的其他部分被执行。这就是模式匹配(基于特征的
规则匹配),也是检测模块的重点。
下面就源码作简要结束:
void ParseRule(FILE *rule_file, char *prule, int inclevel)
{
toks = mSplit(rule, " ", 10, &num_toks, 0);
/* toks[0]对应于配置文件每一行的第一个关键字,可分为四类: */
/* 第一类:alert/log/pass/activate/dynamic (其实这由include语句引入) */
/* 第二类:preprocessor/output 分别对应预处理插件和输出插件,举例 */
/* 如:output database: log, mysql, user=root dbname=db host=localhost */
/* 第三类:设置网络参数如:var ORACLE_PORTS 1521 */
/* 第四类:配置和命令行选项参数 如:config logdir: /var/log/snort */
/* 第五类:自定义规则集,RULE_DECLARE/ RULE_UNKNOW在此不作介绍 */
rule_type = RuleType(toks[0]);
switch(rule_type)
{
/* 先处理规则头 */
/* 以下五条语句ProcessHeadNode形成三维链表的表头 */
case RULE_ALERT:
ProcessHeadNode(&proto_node, &Alert, protocol);
case RULE_LOG:
ProcessHeadNode(&proto_node, &Log, protocol);
case RULE_PASS:
ProcessHeadNode(&proto_node, &Pass, protocol);
case RULE_ACTIVATE:
ProcessHeadNode(&proto_node, &Activation, protocol);
case RULE_DYNAMIC:
ProcessHeadNode(&proto_node, &Dynamic, protocol);
case RULE_PREPROCESS: ParsePreprocessor(rule); //处理预处理插件(附录一
)
case RULE_OUTPUT: ParseOutputPlugin(rule); //处理输出插件(附录一)
/* 以下的parse函数详见源码 */
case RULE_CONFIG: ParseConfig(rule);
case RULE_DECLARE: ParseRuleTypeDeclaration(rule_file, rule);
case RULE_INCLUDE: ParseRulesFile(tmp, inclevel + 1);
case RULE_VAR: VarDefine(toks[1], toks[2]);
case RULE_UNKNOWN: ParseDeclaredRuleType(rule);
}
ProcessIP(); //解析地址
ParsePort(); //解析端口
strcmp("->", toks[4]) && !strcmp("<>", toks[4]); //解析方向
/* 再处理规则选项*/
ParseRuleOptions(rule, rule_type, protocol);
}
1.ProcessHeadNode函数的第二个参数Alert/Log/Pass/Activation/Dynamic是parser.c文
件中定义的五个ListHead全局指针(分别对应五个规则动作),它们都包含RuleTreeNode
结构的四个链表IpList/TcpList/UdpList/IcmpList,RTN链表这是三维链表的第一维。
ProcessHeadNode函数遍历RTN表头(如:Alert->IpList ),调用SetupRTNFuncList函数
初始化RTN链表,该函数是通过调用不同参数的AddRuleFuncToList函数来实现的,最后得
到规则函数指针链表,表头是RTN->RuleFpList类型。这是三维链表的第三维之一。
注意:各个RTN/OTN结构中的函数指针链表即是三维链表的第三维。
2.ParsePreprocessor函数对预处理选项进行初始化,它遍历附录一初始化后所得到的Pr
eprocessKeywords链表,找到配置文件指定要加入的预处理器插件,用相应的插件函数对
配置文件中传入的参数进行初始化。
ParseOutputPlugin函数与此类似。
3.ParseRuleOptions函数处理规则选项。它遍历括号内的规则选项。所有的snort规则选
项用分号";"隔开。规则选项关键字和它们的参数用冒号":"分开。通过匹配规则选项关
键字来调用相关的解析函数。大致结构如下:
if(!strcasecmp(opts[0], "msg")
ParseMessage(opts[1]);
else if(!strcasecmp(opts[0], "logto")
ParseLogto(opts[1]);
else if /*略*/
然后调用AddOptFuncToList函数,得到三维链表的第三维之一:RTN->OptRuleFpList类型
的链表。
注意:至此三维链表建立。