枫林在线论坛>>信息安全 [管理模式] [快速回复] [推荐给朋友]
[6962] 主题: [入侵检测]snort源码分析
作者: little 标题:  [入侵检测]snort源码分析
昵称: 渺小 来自: 203.95.*.* 详细
经验值: 1783 发贴时间: 2002年07月02日 18:05:57
等级: 小有作为 长度: 22440字
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析
发信站: BBS 水木清华站 (Sun Oct 8 11:38:16 2000)
下面我写一点snort源代码的分析。
呵呵,我是面面,有理解的不对的地方,一起讨论。有些地方我读得比较仔
细,有些地方就比较粗糙,所以写起来也含糊其词。
我做事总拖拖拉拉,希望这次能尽快写完。
首先对snort做一个概括的评论。
从工作原理而言,snort是一个NIDS。 网络传输数据的采集
利用了工具包libpcap。snort对libpcap采集来的数据进行分析,从而判断是否
存在可疑的网络活动。
从检测模式而言,snort基本上是误用检测(misuse detection)。具体实现上,仅仅是
对数据进行最直接最简单
的搜索匹配,并没有涉及更复杂的入侵检测办法。
尽管snort在实现上没有什么高深的检测策略,但是它给我们提供了一个非常
优秀的公开源代码的入侵检测系统范例。我们可以通过对其代码的分析,搞清IDS
究竟是如何工作的,并在此基础上添加自己的想法。
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(1)
发信站: BBS 水木清华站 (Sun Oct 8 11:39:33 2000)
首先对snort做一个概括的评论。
从工作原理而言,snort是一个NIDS。 网络传输数据的采集
利用了工具包libpcap。snort对libpcap采集来的数据进行分析,从而判断是否
存在可疑的网络活动。
从检测模式而言,snort基本上是误用检测(misuse detection)。具体实现上,仅仅是
对数据进行最直接最简单
的搜索匹配,并没有涉及更复杂的入侵检测办法。
尽管snort在实现上没有什么高深的检测策略,但是它给我们提供了一个非常
优秀的公开源代码的入侵检测系统范例。我们可以通过对其代码的分析,搞清IDS
究竟是如何工作的,并在此基础上添加自己的想法。
异常检测(anomaly detection)。]具体实现上,仅仅是对数据进行最直接最简单
的搜索匹配,并没有涉及更复杂的入侵检测办法。
尽管snort在实现上没有什么高深的检测策略,但是它给我们提供了一个非常
优秀的公开源代码的入侵检测系统范例。我们可以通过对其代码的分析,搞清IDS
究竟是如何工作的,并在此基础上添加自己的想法。
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(1)
发信站: BBS 水木清华站 (Sun Oct 8 11:39:33 2000)
首先对snort做一个概括的评论。
从工作原理而言,snort是一个NIDS。 网络传输数据的采集
利用了工具包libpcap。snort对libpcap采集来的数据进行分析,从而判断是否
存在可疑的网络活动。
从检测模式而言,snort基本上是误用检测(misuse detection)。具体实现上,仅仅是
对数据进行最直接最简单
的搜索匹配,并没有涉及更复杂的入侵检测办法。
尽管snort在实现上没有什么高深的检测策略,但是它给我们提供了一个非常
优秀的公开源代码的入侵检测系统范例。我们可以通过对其代码的分析,搞清IDS
究竟是如何工作的,并在此基础上添加自己的想法。
snort的编程风格非常优秀,代码阅读起来并不困难,整个程序结构清晰,函
数调用关系也不算复杂。但是,snort的源文件不少,函数总数也很多,所以不太
容易讲清楚。因此,最好把代码完整看一两遍,能更清楚点。
下面看看snort的整体结构。展开snort压缩包,有约50个c程序和头文件,另有
约30个其它文件(工程、数据或者说明文件)。下面对源代码文件分组
说明。
snort.c(.h)是主程序所在的文件,实现了main函数和一系列辅助函数。
decode.c(.h)把数据包层层剥开,确定该包属于何种协议,有什么特征。并
标记到全局结构变量pv中。
log.c(.h)实现日志和报警功能。snort有多种日志格式,一种是按tcpdump
二进制的格式存储,另一种按snort编码的ascii格式存储在日志目录下,日志目
录的名字根据"外"主机的ip地址命名。报警有不同的级别和方式,可以记录

syslog中,或者记录到用户指定的文件,另外还可以通过unix socket发送报警
消息,以及利用SMB向Windows系统发送winpopup消息。
mstring.c(.h)实现字符串匹配算法。在snort中,采用的是Boyer-Moore算法。
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(2)
发信站: BBS 水木清华站 (Sun Oct 8 11:40:44 2000)
下面看看snort的整体结构。展开snort压缩包,有约50个c程序和头文件,另有
约30个其它文件(工程、数据或者说明文件)。下面对源代码文件分组
snort的编程风格非常优秀,代码阅读起来并不困难,整个程序结构清晰,函
数调用关系也不算复杂。但是,snort的源文件不少,函数总数也很多,所以不太
容易讲清楚。因此,最好把代码完整看一两遍,能更清楚点。
下面看看snort的整体结构。展开snort压缩包,有约50个c程序和头文件,另有
约30个其它文件(工程、数据或者说明文件)。下面对源代码文件分组
说明。
snort.c(.h)是主程序所在的文件,实现了main函数和一系列辅助函数。
decode.c(.h)把数据包层层剥开,确定该包属于何种协议,有什么特征。并
标记到全局结构变量pv中。
log.c(.h)实现日志和报警功能。snort有多种日志格式,一种是按tcpdump
二进制的格式存储,另一种按snort编码的ascii格式存储在日志目录下,日志目
录的名字根据"外"主机的ip地址命名。报警有不同的级别和方式,可以记录

syslog中,或者记录到用户指定的文件,另外还可以通过unix socket发送报警
消息,以及利用SMB向Windows系统发送winpopup消息。
mstring.c(.h)实现字符串匹配算法。在snort中,采用的是Boyer-Moore算法。
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(2)
发信站: BBS 水木清华站 (Sun Oct 8 11:40:44 2000)
下面看看snort的整体结构。展开snort压缩包,有约50个c程序和头文件,另有
约30个其它文件(工程、数据或者说明文件)。下面对源代码文件分组
说明。
snort.c(.h)是主程序所在的文件,实现了main函数和一系列辅助函数。
decode.c(.h)把数据包层层剥开,确定该包属于何种协议,有什么特征。并
标记到全局结构变量pv中。
log.c(.h)实现日志和报警功能。snort有多种日志格式,一种是按tcpdump
二进制的格式存储,另一种按snort编码的ascii格式存储在日志目录下,日志目
录的名字根据"外"主机的ip地址命名。报警有不同的级别和方式,可以记录

syslog中,或者记录到用户指定的文件,另外还可以通过unix socket发送报警
消息,以及利用SMB向Windows系统发送winpopup消息。
mstring.c(.h)实现字符串匹配算法。在snort中,采用的是Boyer-Moore算法。
算法书上一般都有。
plugbase.c(.h)实现了初始化检测以及登记检测规则的一组函数。snort中的
检测规则以链表的形式存储,每条规则通过登记(Register)过程添加到链表中。
response.c(.h)进行响应,即向攻击方主动发送数据包。这里实现了两种响应。
一种是发送ICMP的主机不可到达的假信息,另一种针对TCP,发送RST包,断开连接。
rule.c(.h)实现了规则设置和入侵检测所需要的函数。规则设置主要的作用是
把一个规则文件转化为实际运作中的规则链表。检测函数根据规则实施攻击特征的
检测。
sp_*_check.c(.h)是不同类型的检测规则的具体实现。很容易就可以从文件名
得知所实现的规则。例如,sp_dsize_check针对的是包的数据大小,sp_icmp_type
_check针对icmp包的类型,sp_tcp_flag_check针对tcp包的标志位。不再详述。
spo_*.c(.h)实现输出(output)规则。spo_alert_syslog把事件记录到syslog
中;spo_log_tcpdump利用libpcap中的日志函数,进行日志记录。
spp_*.c(.h)实现预处理(preprocess)规则。包括http解码(即把http请求中
的%XX这样的字符用对应的ascii字符代替,避免忽略了恶意的请求)、最小片断检
查(避免恶意利用tcp协议中重组的功能)和端口扫描检测。
下面描述main函数的工作流程。先来说明两个结构的定义。
在snort.h中,定义了两个结构:PV和PacketCount。PV用来记录命令行参数,
snort根据这些命令行参数来确定其工作方式。PV类型的全局变量pv用来实际记录具体

工作方式。结构定义可以参看snort.h,在下边的main函数中,会多次遇到pv中各个域

的设定,到时再一个一个解释。
结构PacketCount用来统计流量,每处理一个数据包,该结构类型的全局变量pc
把对应的域加1。相当于一个计数器。
接下来解释main函数。
初始化设定一些缺省值;然后解析命令行参数,根据命令行参数,填充结构变
量pv;根据pv的值(也就是解析命令行的结果)确定工作方式,需要注意:
如果是运行在Daemon方式,通过GoDaemon函数,创建守护进程,重定向标准输入
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(3)
发信站: BBS 水木清华站 (Sun Oct 8 15:51:03 2000)
下面描述main函数的工作流程。先来说明两个结构的定义。
在snort.h中,定义了两个结构:PV和PacketCount。PV用来记录命令行参数,
snort根据这些命令行参数来确定其工作方式。PV类型的全局变量pv用来实际记录具体

工作方式。结构定义可以参看snort.h,在下边的main函数中,会多次遇到pv中各个域

的设定,到时再一个一个解释。
结构PacketCount用来统计流量,每处理一个数据包,该结构类型的全局变量pc
把对应的域加1。相当于一个计数器。
接下来解释main函数。
初始化设定一些缺省值;然后解析命令行参数,根据命令行参数,填充结构变
量pv;根据pv的值(也就是解析命令行的结果)确定工作方式,需要注意:
如果是运行在Daemon方式,通过GoDaemon函数,创建守护进程,重定向标准输入
输出,实现daamon状态,并结束父进程。
snort可以实时采集网络数据,也可以从文件读取数据进行分析。这两种情况并
没有本质区别。如果是读取文件进行分析(并非直接从网卡实时采集来的),以该文
件名作为libpcap的函数OpenPcap的参数,打开采集过程;如果是从网卡实时采集,
就把网卡接口作为OpenPcap的参数,利用libpcap的函数打开该网卡接口。在unix中,

设备也被看作是文件,所以这和读取文件分析没有多大的差别。
接着,指定数据包的拆包函数。不同的数据链路网络,拆包的函数也不同。利用
函数SetPktProcessor,根据全局变量datalink的值,来设定不同的拆包函数。例如,

以太网,拆包函数为DecodeEthPkt;令牌环网,拆包函数为DecodeTRPkt,等等。这些
Decode*函数,在decode.c中实现。
如果使用了检测规则,那么下面就要初始化这些检测规则,并解析规则文件,转
化成规则链表。规则有三大类:预处理(preprocessor),插件(plugin),输出插
件(outputplugin)。这里plugin就是具体的检测规则,而outputplugin是定义日志
和报警方式的规则。
然后根据报警模式,设定报警函数;根据日志模式,设定日志函数;如果指定了
能够进行响应,就打开raw socket,准备用于响应。
最后进入读取数据包的循环,pcap_loop对每个采集来的数据包都用ProcessPacket
函数进行处理,如果出现错误或者到达指定的处理包数(pv.pkt_cnt定义),就退出
该函数。这里ProcessPacket是关键程序,
最后,关闭采集过程。
现在看看snort如何实现对数据包的分析和检测入侵的。
在main函数的最后部分有如下语句,比较重要:
/* Read all packets on the device. Continue until cnt packets read */
if(pcap_loop(pd, pv.pkt_cnt, (pcap_handler)ProcessPacket, NULL) < 0)
{
......
}
这里pcap_loop函数有4个参数,分别解释:
pd是一个全局变量,表示文件描述符,在前面OpenPcap的调用中已经被正确地
赋值。前面说过,snort可以实时采集网络数据,也可以从文件读取数据进行分析。
在不同情况打开文件(或设备)时,pd分别用来处理文件,或者网卡设备接口。
pd是struct pcap类型的指针,该结构包括实际的文件描述符,缓冲区,等等
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(4)
发信站: BBS 水木清华站 (Tue Oct 10 11:42:22 2000)
现在看看snort如何实现对数据包的分析和检测入侵的。
在main函数的最后部分有如下语句,比较重要:
/* Read all packets on the device. Continue until cnt packets read */
if(pcap_loop(pd, pv.pkt_cnt, (pcap_handler)ProcessPacket, NULL) < 0)
{
......
}
这里pcap_loop函数有4个参数,分别解释:
pd是一个全局变量,表示文件描述符,在前面OpenPcap的调用中已经被正确地
赋值。前面说过,snort可以实时采集网络数据,也可以从文件读取数据进行分析。
在不同情况打开文件(或设备)时,pd分别用来处理文件,或者网卡设备接口。
pd是struct pcap类型的指针,该结构包括实际的文件描述符,缓冲区,等等
域,用来处理从相应的文件获取信息。
OpenPcap函数中对pd赋值的语句分别为:
/* get the device file descriptor,打开网卡接口 */
pd = pcap_open_live(pv.interface, snaplen,
pv.promisc_flag ? PROMISC : 0, READ_TIMEOUT, errorbuf);
或者
/* open the file,打开文件 */
pd = pcap_open_offline(intf, errorbuf);
于是,这个参数表明从哪里取得待分析的数据。
第2个参数是pv.pkt_cnt,表示总共要捕捉的包的数量。在main函数初始化时,
缺省设置为-1,成为永真循环,一直捕捉直到程序退出:
/* initialize the packet counter to loop forever */
pv.pkt_cnt = -1;
或者在命令行中设置要捕捉的包的数量。前面ParseCmdLine(解析命令行)函数
的调用中,遇到参数n,重新设定pv.pkt_cnt的值。ParseCmdLine中相关语句如下:
case 'n': /* grab x packets and exit */
pv.pkt_cnt = atoi(optarg);
第3个参数是回调函数,该回调函数处理捕捉到的数据包。这里为函数
ProcessPacket,下面将详细解释该函数。
第4个参数是字符串指针,表示用户,这里设置为空。
在说明处理包的函数ProcessPacket之前,有必要解释一下pcap_loop的实现。
我们看到main函数只在if条件判断中调用了一次pacp_loop,那么循环一定是在
pcap_loop中做的了。察看pcap.c文件中pcap_loop的实现部分,我们发现的确如此:
int
pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
register int n;
for (;;) { //for循环
if (p->sf.rfile != NULL)
n = pcap_offline_read(p, cnt, callback, user);
else {
/*
* XXX keep reading until we get something
* (or an error occurs)
*/
do { //do循环
n = pcap_read(p, cnt, callback, user);
} while (n == 0);
}
if (n <= 0)
return (n); //遇到错误,返回
if (cnt > 0) {
cnt -= n;
if (cnt <= 0)
return (0); //到达指定数量,返回
}
//只有以上两种返回情况
}
}
现在看看ProcessPacket的实现了,这个回调函数用来处理数据包。该函数是
是pcap_handler类型的,pcap.h中类型的定义如下:
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
const u_char *);
第1个参数这里没有什么用;
第2个参数为pcap_pkthdr结构指针,记录时间戳、包长、捕捉的长度;
第3个参数字符串指针为数据包。
函数如下:
void ProcessPacket(char *user, struct pcap_pkthdr *pkthdr, u_char *pkt)
{
Packet p; //Packet结构在decode.h中定义,用来记录数据包的各种信息
/* call the packet decoder,调用拆包函数,这里grinder是一个全局
函数指针,已经在main的SetPktProcessor调用中设置为正确的拆包函数 */
(*grinder)(&p, pkthdr, pkt);
/* print the packet to the screen,如果选择了详细显示方式,
那么把包的数据,显示到标准输出 */
if(pv.verbose_flag)
{
...... //省略
}
/* check or log the packet as necessary
如果工作在使用检测规则的方式,就调用Preprocess进行检测,
否则,仅仅进行日志,记录该包的信息*/
if(!pv.use_rules)
{
... //进行日志,省略
}
else
{
Preprocess(&p);
}
//清除缓冲区
ClearDumpBuf();
}
这里Preprocess函数进行实际检测。
Proprocess函数很短,首先调用预处理规则处理数据包p,然后调用检测
函数Detect进行规则匹配实现检测,如果实现匹配,那么调用函数CallOutput
Plugins根据输出规则进行报警或日志。函数如下:
void Preprocess(Packet *p)
{
PreprocessFuncNode *idx;
do_detect = 1;
idx = PreprocessList; //指向预处理规则链表头
while(idx != NULL) //调用预处理函数处理包p
{
idx->func(p);
idx = idx->next;
}
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(5)
发信站: BBS 水木清华站 (Fri Oct 13 11:20:55 2000)
Proprocess函数很短,首先调用预处理规则处理数据包p,然后调用检测
函数Detect进行规则匹配实现检测,如果实现匹配,那么调用函数CallOutput
Plugins根据输出规则进行报警或日志。函数如下:
void Preprocess(Packet *p)
{
PreprocessFuncNode *idx;
do_detect = 1;
idx = PreprocessList; //指向预处理规则链表头
while(idx != NULL) //调用预处理函数处理包p
{
idx->func(p);
idx = idx->next;
}
if(!p->frag_flag && do_detect)
{
if(Detect(p)) //调用检测函数
{
CallOutputPlugins(p); //如果匹配,根据规则输出
}
}
}
尽管这个函数很简洁,但是在第1行我们看到定义了ProprocessFuncNode
结构类型的指针,所以下面,我们不得不开始涉及到snort的各种复杂
的数据结构。前面的分析,我一直按照程序运行的调用顺序,忽略了许多函
数(其实有不少非常重要),以期描述出snort执行的主线,避免因为程序中
大量的调用关系而产生混乱。到现在,我们还没有接触到snort核心的数据结构
和算法。有不少关键的问题需要解决:规则是如何静态描述的?运行时这些
规则按照什么结构动态存储?每条规则的处理函数如何被调用?snort给了
我们提供了非常好的方法。
snort一个非常成功的思想是利用了plugin机制,规则处理函数并非固定在
源程序中,而是根据每次运行时的参数设定,从规则文件中读入规则,再把每个
规则所需要的处理函数挂接到链表上。实际检测时,遍历这些链表,调用链表上
相应的函数来分析。
snort主要的数据结构是链表,几乎都是链表来链表去。我们下面做个总的
介绍。
我们有必要先回过头来,看一看main函数中对规则初始化时涉及到的一些
数据结构。
在main函数初始化规则的时候,先建立了几个链表,全局变量定义如下
(plugbase.c中):
KeywordXlateList *KeywordList;
PreprocessKeywordList *PreprocessKeywords;
PreprocessFuncNode *PreprocessList;
OutputKeywordList *OutputKeywords;
OutputFuncNode *OutputList;
这几种结构的具体定义省略。这一初始化的过程把snort中预定义的关键
字和处理函数按类别连接在不同的链表上。然后,在解析规则文件的时候,
如果一条规则的选项中包含了某个关键字,就会从上边初始化好的对应的链表
中查找,把必要的信息和处理函数添加到表示这条规则的节点(用RuleTreeNode
类型来表示,下面详述)的特定域(OptTreeNode类型)中。
同时,main函数中初始化规则的最后,对指定的规则文件进行解析。在最
高的层次上,有3个全局变量保存规则(rules.c):
ListHead Alert; /* Alert Block Header */
ListHead Log; /* Log Block Header */
ListHead Pass; /* Pass Block Header */
这几个变量是ListHead类型的,正如名称所说,指示链表头。Alert中登记
了需要报警的规则,Log中登记了需要进行日志的规则,Pass中登记的规则在处
理过程忽略(不进行任何处理)。ListHead定义如下:
typedef struct _ListHead
{
RuleTreeNode *TcpList;
RuleTreeNode *UdpList;
RuleTreeNode *IcmpList;
} ListHead;
可以看到,每个ListHead结构中有三个指针,分别指向处理Tcp/Udp/Icmp包
规则的链表头。这里又出现了新的结构RuleTreeNode,为了说明链表的层次关系,
下面列出RuleTreeNode的定义,但是忽略了大部分域:
typedef struct _RuleTreeNode
{
RuleFpList *rule_func;
...... //忽略
struct _RuleTreeNode *right;
OptTreeNode *down; /* list of rule options to associate with this
rule node */
} RuleTreeNode;
RuleTreeNode中包含上述3个指针域,分别又能形成3个链表。RuleTreeNode*
类型的right指向下一个RuleTreeNode,相当于普通链表中的next域,只不过这里
用right来命名。这样就形成了规则链表。
RuleFpList类的指针rule_func记录的是该规则的处理函数的链表。一条规则
有时候需要调用多个处理函数来分析。所以,有必要做成链表。我们看看下面的
定义,除了next域,还有一个函数指针:
typedef struct _RuleFpList
{
/* rule check function pointer */
int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _RuleFpList *);

/* pointer to the next rule function node */
struct _RuleFpList *next;
} RuleFpList;
第3个指针域是OptTreeNode类的指针down,该行后面的注释说的很清楚,这是
与这个规则节点相联系的规则选项的链表。很不幸,OptTreeNode的结构也相当复
杂,而且又引出了几个新的链表。忽略一些域,OptTreeNode定义如下:
typedef struct _OptTreeNode
{
/* plugin/detection functions go here */
OptFpList *opt_func;
/* the ds_list is absolutely essential for the plugin system to work,
it allows the plugin authors to associate "dynamic" data structure
s
with the rule system, letting them link anything they can come up
with to the rules list */
void *ds_list; /* list of plugin data struct pointers */
.......//省略了一些域
struct _OptTreeNode *next;
} OptTreeNode;
next指向链表的下一个节点,无需多说。OptFpList类型的指针opt_func指向
选项函数链表,同前面说的RuleFpList没什么大差别。值得注意的是指针数组
ds_list,用来记录该条规则中涉及到的预定义处理过程。每个元素的类型是void*。
在实际表示规则的时候,ds_list被强制转换成不同的预定义类型。
--
垆边人似月,皓腕凝霜雪
※ 来源:·BBS 水木清华站 smth.org·
发信人: shuke (莫失莫忘), 信区: Security
标 题: snort源码分析(5)
发信站: BBS 水木清华站 (Fri Oct 13 11:20:55 2000)
Proprocess函数很短,首先调用预处理规则处理数据包p,然后调用检测
函数Detect进行规则匹配实现检测,如果实现匹配,那么调用函数CallOutput
Plugins根据输出规则进行报警或日志。函数如下:
void Preprocess(Packet *p)
{
PreprocessFuncNode *idx;
do_detect = 1;
idx = PreprocessList; //指向预处理规则链表头
while(idx != NULL) //调用预处理函数处理包p
{
idx->func(p);
idx = idx->next;
}
if(!p->frag_flag && do_detect)
{
if(Detect(p)) //调用检测函数
{
CallOutputPlugins(p); //如果匹配,根据规则输出
}
}
}
尽管这个函数很简洁,但是在第1行我们看到定义了ProprocessFuncNode
结构类型的指针,所以下面,我们不得不开始涉及到snort的各种复杂
的数据结构。前面的分析,我一直按照程序运行的调用顺序,忽略了许多函
数(其实有不少非常重要),以期描述出snort执行的主线,避免因为程序中
大量的调用关系而产生混乱。到现在,我们还没有接触到snort核心的数据结构
和算法。有不少关键的问题需要解决:规则是如何静态描述的?运行时这些
规则按照什么结构动态存储?每条规则的处理函数如何被调用?snort给了
我们提供了非常好的方法。
snort一个非常成功的思想是利用了plugin机制,规则处理函数并非固定在
源程序中,而是根据每次运行时的参数设定,从规则文件中读入规则,再把每个
规则所需要的处理函数挂接到链表上。实际检测时,遍历这些链表,调用链表上
相应的函数来分析。
snort主要的数据结构是链表,几乎都是链表来链表去。我们下面做个总的
介绍。
我们有必要先回过头来,看一看main函数中对规则初始化时涉及到的一些
数据结构。
在main函数初始化规则的时候,先建立了几个链表,全局变量定义如下
(plugbase.c中):
KeywordXlateList *KeywordList;
PreprocessKeywordList *PreprocessKeywords;
PreprocessFuncNode *PreprocessList;
OutputKeywordList *OutputKeywords;
OutputFuncNode *OutputList;
这几种结构的具体定义省略。这一初始化的过程把snort中预定义的关键
字和处理函数按类别连接在不同的链表上。然后,在解析规则文件的时候,
如果一条规则的选项中包含了某个关键字,就会从上边初始化好的对应的链表
中查找,把必要的信息和处理函数添加到表示这条规则的节点(用RuleTreeNode
类型来表示,下面详述)的特定域(OptTreeNode类型)中。
同时,main函数中初始化规则的最后,对指定的规则文件进行解析。在最
高的层次上,有3个全局变量保存规则(rules.c):
ListHead Alert; /* Alert Block Header */
ListHead Log; /* Log Block Header */
ListHead Pass; /* Pass Block Header */
这几个变量是ListHead类型的,正如名称所说,指示链表头。Alert中登记
了需要报警的规则,Log中登记了需要进行日志的规则,Pass中登记的规则在处
理过程忽略(不进行任何处理)。ListHead定义如下:
typedef struct _ListHead
{
RuleTreeNode *TcpList;
RuleTreeNode *UdpList;
RuleTreeNode *IcmpList;
} ListHead;
可以看到,每个ListHead结构中有三个指针,分别指向处理Tcp/Udp/Icmp包
规则的链表头。这里又出现了新的结构RuleTreeNode,为了说明链表的层次关系,
下面列出RuleTreeNode的定义,但是忽略了大部分域:
typedef struct _RuleTreeNode
{
RuleFpList *rule_func;
...... //忽略
struct _RuleTreeNode *right;
OptTreeNode *down; /* list of rule options to associate with this
rule node */
} RuleTreeNode;
RuleTreeNode中包含上述3个指针域,分别又能形成3个链表。RuleTreeNode*
类型的right指向下一个RuleTreeNode,相当于普通链表中的next域,只不过这里
用right来命名。这样就形成了规则链表。
RuleFpList类的指针rule_func记录的是该规则的处理函数的链表。一条规则
有时候需要调用多个处理函数来分析。所以,有必要做成链表。我们看看下面的
定义,除了next域,还有一个函数指针:
typedef struct _RuleFpList
{
/* rule check function pointer */
int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _RuleFpList *);

/* pointer to the next rule function node */
struct _RuleFpList *next;
} RuleFpList;
第3个指针域是OptTreeNode类的指针down,该行后面的注释说的很清楚,这是
与这个规则节点相联系的规则选项的链表。很不幸,OptTreeNode的结构也相当复
杂,而且又引出了几个新的链表。忽略一些域,OptTreeNode定义如下:
typedef struct _OptTreeNode
{
/* plugin/detection functions go here */
OptFpList *opt_func;
/* the ds_list is absolutely essential for the plugin system to work,
it allows the plugin authors to associate "dynamic" data structure
s
with the rule system, letting them link anything they can come up
with to the rules list */
void *ds_list; /* list of plugin data struct pointers */
.......//省略了一些域
struct _OptTreeNode *next;
} OptTreeNode;
next指向链表的下一个节点,无需多说。OptFpList类型的指针opt_func指向
选项函数链表,同前面说的RuleFpList没什么大差别。值得注意的是指针数组
ds_list,用来记录该条规则中涉及到的预定义处理过程。每个元素的类型是void*。
在实际表示规则的时候,ds_list被强制转换成不同的预定义类型。


========== * * * * * ==========
Top

| 用户注册 | 在线用户 | 投票中心 | 常见问题 |

Copyright © 2001-2012 枫林在线(www.FengLin.info) All Rights Reserved
页面运行使用49.4毫秒