snort源码分析系列之一

470阅读 0评论2014-05-01 茹旭东
分类:网络与安全

序:

1
        包捕获模块
2
         包解码模块
3
         预处理模块
4
         检测模块(模式匹配)
5
         输出模块




第一部分:包捕获模块

本部分由snort.c 文件中的OpenPcap函数实现。该函数依次调用下图的libpcap库中的函数



 
这里需要提醒的是本模块实现的功能实现到libpcap库流程的循环抓包前为止,循环抓包是
snort.c 主流程的InterfaceThread函数中实现。
以下是libpcap库各个函数的作用:
1
pcap_lookupdev
pcap_lookupdev
用来查找系统第一个可以使用的网络适配器,查找成功后,
返回该设备的名称;如果系统有多个网卡,也可以使用pcap_findalldevs函数
来查找选取;
2
pcap_open_live
函数用于打开指定网络适配器,准备截取数据。其调用形式为:
pd = pcap_open_live
()                  /* 以下是参数 */
(   pv.interface, /*
设备名称*/
snaplen, /*
捕获包的长度,通常设置为65536,表示捕获链路层上的所有数据*/
pv.promisc_flag ? PROMISC : 0, /*
网卡是否工作于混杂模式*/
READ_TIMEOUT, /*
超时时间控制,单位毫秒*/
Errorbuf     );   /*
出错信息存储 */
3
pcap_open_offline
Libpcap
使用库函数pcap_open_offline 进行脱机方式截获,即先将网络上的
数据截获下来,以文件形式储存到磁盘上,等事后方便时再从磁盘上读取数
据文件来做进一步分析。
4
pcap_snapshot
函数用于获取数据链路层协议的类型;
5
pcap_compile pcap_setfilter
我们在进行数据包截获时,常常并不需要捕获所有的包,如只需要捕获ARP
协议等,这就需要过滤规则。Pcap_compile 用于将设置的过滤字符串编译成
一个过滤器程序。而pcap_setfilter则用来设置过滤器的过滤规则;
6
pcap_loop
前面的函数都可以看做捕获数据包的准备工作,pcap_loop 用来从网络中捕
获数据包。需要注意的是函数的第三个参数,它是一个回调函数,捕获到的
数据包,就交由它来处理;




第二部分:包解码模块

本模块由snort.c文件中的SetPktProcessor函数实现。该函数根据datalink(由上面的li
bpcap
库函数得到)的值来判断并关联解码函数。解码结构如下图:


程序大致结构如下:
int SetPktProcessor()
{
    switch(datalink)
    {
        case DLT_EN10MB:                      /* Ethernet */
            
            grinder = DecodeEthPkt;
            break;

        case DLT_IEEE802:                     /* Token Ring */
            
            grinder = DecodeTRPkt;
            break;
   
        /*
以下略 */
      }
}

其中 grinder snort.c 中的全局的函数指针,其定义如下:

typedef void (*grinder_t)(Packet *, struct pcap_pkthdr *, u_char *);  /* ptr t
o the packet processor */




第三部分:预处理模块


Snort
系统在初始化(详见附录一)完成后,在进入检测引擎模块(处理前面两个模块传过
来的网络封包)前,需要对数据包进行预处理。这里请注意SNORT所使用的插件的思想,S
nort
的插件结构允许开发者扩展snort的功能。

需要再次说明的是,循环抓包是在snort.c 主流程的InterfaceThread函数中实现,该函数
调用libpcap 库中的pcap_loop循环抓包函数,这个函数有个回调函数ProcessPacket,这
个回调函数先通过上一个包解码模块得到的相应的解码函数对抓到的数据包进行解码(实现
语句是: (*grinder) (&p, pkthdr, pkt); ),下一步判断snort.c 文件中的runMode全局
遍变量,若 SNORT工作在包记录模式,则调用输出插件CallLogPlugins函数;否则工作在
IDS
模式,继而进入预处理Preprocess函数。

该预处理函数遍历在初始化预处理插件(详见附录一)后得到的PreprocessKeywordList
表,调用配置文件中要求的预处理插件函数对所抓的数据包进行预处理。函数实现如下:

idx = PreprocessList;
while(idx != NULL)
{
    assert(idx->func != NULL);
    idx->func(p);
    idx = idx->next;
}

预处理器在调用检测引擎之前,在数据包被解码之后运行。通过这种机制,snort可以以一
out of band的方式对数据包进行修改或者分析。以下是几个常用的预处理插件:
1
        HTTP解码预处理模块用来处理HTTP URI字符串,把它们转换为清晰的ASCII字符串。这
样就可以对抗evasice web URL扫描程序和能够避开字符串内容分析的恶意攻击者。
2
        frag2模块 :使snort能够消除IP碎片包,给黑客使用IP碎片包绕过系统的检测增加了
难度。
3
        stream4插件为snort提供了TCP数据包重组的功能。在配置的端口上,stream4插件能
够对TCP数据包的细小片段进行重组成为完整的TCP数据包,然后snort可以对其可疑行为进
行检查。
4
        Portscan预处理程序的用处: 向标准记录设备中记录从一个源IP地址来的端口扫描的
开始和结束。端口扫描可以是对任一IP地址的多个端口,也可以是对多个IP地址的同一端
口进行。可以处理分布式的端口扫描(多对一或多对多)。端口扫描也包括单一的秘密扫
描(stealth scan)数据包,比如NULLFINSYNFINXMAS等。
5
        Portscan2模块将检测端口扫描。它要求包含Conversation预处理器以便判定一个会话
是什么时间开始的。它的目的是能够检测快速扫描,例如,快速的nmap扫描。
6
        Conversation 预处理器使Snort 能够得到关于协议的基本的会话状态而不仅仅是由s
pp_stream4
处理的TCP状态。当它接收到一个你的网络不允许的协议的数据包时,它也能产
生一个报警信息。要做到这一点,请在IP协议列表中设置你允许的IP协议,并且当它收到
一个不允许的数据包时,它将报警并记录这个数据包。
7
        Http Flow模块可以忽略HTTP头后面的HTTP服务响应。

由于SNORT的各种预处理模块很有借鉴性,我将在另一章中给出详解。

第四部分:检测模块(模式匹配)


SNORT
系统的快速匹配模块有最重要的设计特色,其在初始规则链表的基础上,重新构造了
一套快速匹配的数据结构,并采用了多模式匹配搜索引擎。按其功能可分三个步骤:
1.
构造初始的规则链表结构,由ParseRule函数实现。(详见附录三)
2.
读入规则链表各节点,并构造用快速匹配的新的数据结构,这是在初始化各个插件和建
立三维链表后,在调用处理模块函数InterfaceThread前完成的。它是由fpcreate.c文件中
fpCreateFastPacketDetection函数完成的。(详见附录四)
3.
对当前数据包执行具体的快速规则匹配任务,主要在fpEvalPacket()上。(本模块实现


注意现在存在两个链表:一个是RuleListNode->ListHead->RTN/OTN/OutputFuncNode链表
,表头是parser.c文件中全局变量RuleLists;另一个是PORT_RULE_MAP -> PROT_GROUP -
> RULE_NODE
链表,表头是fpcreate.c文件中全局数据结构指针prmTcpRTNX/prmUdpRTNX/p
rmIcmpRTNX/prmIpRTNX

通过第一个链表中OTN的规则内容建立第二个链表。

Preprocess函数中,在处理完预处理模块后,调用Detect函数对捕获的packet结构类型
参数的数据包内容进行特征规则匹配。该函数调用fpEvalPacket函数,进而根据捕获数据
包的协议类型判断Tcp/Udp/Icmp协议,若不能规为这三种协议的就算作Ip协议,然后调用
相应的处理函数。以Tcp协议为例,调用fpEvalHeaderTcp函数。

下面以fpEvalHeaderTcp 函数源码为例解析:
static INLINE int fpEvalHeaderTcp(Packet *p)
{
        /*
根据给定参数的源端口和目的端口决定应根据哪个PORT_RULE_MAP->PORT_GROUP*/
/*
结构进行匹配 (该链表的建立参照附录四)                                */
prmFindRuleGroupTcp(p->dp, p->sp, &src, &dst, &gen);

/*
遍历Tcp类型的PORT_RULE_MAP->PORT_GROUP 链表进行http_decode模式特征匹配*/
        fpEvalHeaderSW(dst, p, 1)

}

继续对fpEvalHeaderSW 函数进行解析。该函数遍历 PORT_RULE_MAP->PORT_GROUP链表,进
uri-conetnt/content/non-conetnt匹配:

int fpEvalHeaderSW(PORT_GROUP *port_group, Packet *p, int check_ports)
{
        /*
循环HttpUri类型全局指针数组UriBufs[URI_COUNT] */
        for( i=0; iuri_count; i++)
    {
                /*
以下分别进行 uri-conetnt/content/non-conetnt匹配,仅以content 为例*/

                stat = mpseSearch ( so, p->data, p->dsize, otnx_match, &omd );      
    fpLogEvent(otnx->rtn, otnx->otn, p);
}
}

下面对以上各函数分别解析:
1
        mpseSearch函数:
        SNORT
提供三种完全不同的模式匹配算法:Aho-Corasick/Wu-Manber/Boyer-Moore,默认
使用第三种。注意mpseSearch函数中有两个参数分别是: 所要匹配的数据包指针(Packet
*)
和先前为了快速匹配特征串而建立的对应的结构指针(PORT_RULE_MAP->PORT_GROUP*
,后面的所调用的函数参数都是从这两个参数中得到。
mpseSearch
函数调用mwmSearch,继续调用mwmSearch,继续调用hbm_match函数。这个函数
即是用Boyer-Moore-Horspool模式匹配算法进行特征匹配。具体算法实现请参照msting.c
文件。


2
        fpLogEvent函数:
若匹配成功,则调用本函数记录或报警。该函数的参数分别是:匹配的RTN/ONT指针和数据
包指针(Packet*),具体实现如下:
switch(rtn->type)  
{
        case RULE_PASS: PassAction();
        case RULE_ACTIVATE: ActivateAction(p, otn, &otn->event_data);
        case RULE_ALERT: AlertAction(p, otn, &otn->event_data);
case RULE_DYNAMIC: DynamicAction(p, otn, &otn->event_data);
        case RULE_LOG: LogAction(p, otn, &otn->event_data);
}
若是应用pass规则动作,则仅设置通过包的数目加一;
若是dynamic/log规则动作,则调用detect.c文件中的CallLogFuncs函数进行记录;
若是activate/alert规则动作,则调用detect.c文件中的CallAlertFuncs函数进行报警。

 

 

上一篇:SDRAM芯片初始化、行有效、列读写时序
下一篇:没有了