Snort入侵检测系统分析
作者:浩海孤帆@bbs.net130.com
Snort的特点
Snort是一个强大的清量级的网络入侵检测系统。它具有实时数据流量分析和日志Ip网络数据包的能力,能够进行协议分析,对内容搜索/匹配。它能够检测各种不同的攻击方式,对攻击进行实时警报。此外,Snort具有很好的扩展性和可移植性。还有,这个软件遵循公用许可GPL,所以只要遵守GPL任何组织和个人都可以自由使用。
Snort是一个轻量级的入侵检测系统。
1.Snort虽然功能强大,但是其代码极为简洁,短小,其源代码压缩包只有200KB不到。Snort可移植性非常好。Snort的跨平台性能极佳,目前已经支持Linux系列, Solaris,BSD系列,IRIX,HP-UX,Windows系列,ScoOpenserver,Unixware等。
2.Snort具有实时流量分析和日志Ip网数据包的能力。能够快速地检测网络攻击,及时地发出警报。Snort的警报机制很丰富。
例如:Syslog,用户指定文件,UnixSocket,还有使用SAMBA协议向Windows客户程序发出WinPopup消息。利用XML插件,Snort可以使用SNML(简单网络标记语言.simple network markup language)把日志存放在一个文件或者适时警报。
3.Snort能够进行协议分析,内容的搜索/匹配。现在Snort能够分析的协议有TCP,UDP和ICMP。将来的版本,将提供对ARP.ICRP,GRE,OSPF,RIP,ERIP,IPX,APPLEX等协议的支持。它能够检测多种方式的攻击和探测,例如:缓冲区溢出,CGI攻击,SMB检测,探测操作系统质问特征的企图等等。
4.Snort的日至格式既可以是Tcpdump的二进制格式,也可以编码成ASCII字符形式,更便于拥护尤其是新手检查,使用数据库输出插件,Snort可以把日志记入数据库,当前支持的数据库包括:Postagresql,MySQL,任何UnixODBC数据库,MicrosoftMsSQL,还有Oracle等数据库。
5.使用TCP流插件(TCPSTREAM),Snort可以对TCP包进行重组。Snort能够对IP包的内容进行匹配,但是对于TCP攻击,如果攻击者使用一个程序,每次发送只有一个字节的数据包,完全可以避开Snort的模式匹配。而被攻击的主机的TCP协议栈会重组这些数据,将其发送给目标端口上监听的进程,从而使攻击包逃过Snort的监视。使用TCP流插件,可以对TCP包进行缓冲,然后进行匹配,使Snort具备对付上面攻击的能力。
6.使用Spade(Statistical Packet Anomaly Detection Engine)插件,Snort能够报告非正常的可以包,从而对端口扫描进行有效的检测。
7.Snort还有很强的系统防护能力。如:是用其IPTables,IPFilter插件可以使入侵检测主机与防火墙联动,通过FlexResp功能,Snort能够命令防火墙主动短开恶意连接。
8.扩展性能较好,对于新的攻击威胁反应迅速。作为一个轻量级的网络入侵检测系统,Snort有足够的扩展能力。它使用一种简单的规则描述语言(很多商用入侵检测系统都兼容Snort的规则语言)。最基本的规则知识包含四个域:处理动作,协议,方向,端口。
例如 Log Tcp Any any -> 10.1.1 .0/24 80(谁都看得明白)
9.Snort支持插件,可以使用具有特定功能的报告,检测子系统插件对其功能进行扩展。Snort当前支持的插件包括:数据库日志输出插件,破碎数据包检测插件,断口扫描检测插件,HTTP URI插件,XML网页生成等插件。
10.Snort的规则语言非常简单,能够对新的网络攻击做出很快的反应。发现新攻击后,可以很快地根据Bugtrag邮件列表,找到特征码,写出新的规则文件。
总之,对于世界上各安全组织来讲,Snort入侵检测都是一个优秀入侵检测系统的一个标准的标准。通过研究它,我们可以学到到所有入侵检测系统的内部框架及工作流程(也包括同类型的商业入侵检测系统的框架及工作流程)。
7.2 Snort系统各程序模块代码分析
系统架构分析Snort主工作流函数为SnortMain()流程大体如下:
Snort2.0系列主工作图
图7.1 Snort主函数模块
相关函数解释:
1) 命令行参数解析函数ParseCmdLine(),解析个中命令参数,并将其放入全局PV类型的变量PV中。数据类型PV包含各种标示字段,用来知识个中系统的参数设置。
例如:规则文件,系统运行模式,显示模式,插件激活等。
2)检测引擎初始化fpInitDetectinEngine(),用于制定快速规则匹配模块的配置参数,包括模式搜索算法等(Snort可使用的算法有Aho-Corasick , Wu-Manber , Boyer-Moore 等算法,缺省Snort使用Byer-More算法。)。并负责在协议解析过程中产生警报信息。
3)取得从网络截取到的数据流的主要进程OpenPcap()。起主要作用是根据命令行参数分析结果,分别调用Libpcap函数库Pcap_open_live()—通过网卡驱动实时截取网络数据 和 Pcap_open_offline()—通过文件来访问以前网卡驱动截取数据保存成为的文件,并获得相对应的数据包数据结构。
4)各种插件初始化主要包括输入/输出插件初始化,检测插件初始化和预处理插件初始化等。主要就是将各种插件的关键字与对应的初始化处理函数相调用,然后注册到对应的关键字链表结构中,随后逐个弹出以便规则解析模块使用。
例如:预处理模块插件. 链表结构PreprocessKeyWordList定义如下:
typedef struct_preprocessKeywordNode
{
char *keyword;
void(*func)(u_char*);
}PreprocessKeywordNode;
typedef struct_preprocessKeywordList
{
PreprocessNode entry;
Struct_PreprocessKeywordList *next;
}PreprocessKeywordlist;
预处理插件的初始化函数 void (*func)(u_char *)并非插件的直接工作模块,而是由规则解释模块负责调用,然后将对应的插件处理模块连接到预处理工作数据链表结构中。
预处理工作函数链表结构PreprocessFuncNode定义如下:
typedef struct_PreprocessFuncNode
{
void (*func)(Packet *);
struct_PreprocessFuncNode *next;
} PreprocessFuncNode;
函数void (*func)(Packet *)才是真正进行预处理的工作模块,将被检测模块Preprocess() 在遍历预处理插件链表时调用。
4)规则结构初始化和解析
CreateDefaultRules()负责进行初始的规则结果建设。
我们学习到的规则链表模型结构多是多个二维链表结构(前面描述过)。而Snort是新三维链表结构形式,涉及到关键数据结构RuleListNode如下:
typedef struct_RuleListNode
{
ListHead *RuList; /* The rule list associated with this node*/
int mode; /* The rule mode */
int rval; /* 0—no detection , 1 –detection event */
int evalIndex ; /* eval index for this rule set */
char *name; /* Name of this rule list */
struct_RuleListNode Next; /* Rhe next RuleListNode */
} RuleListNode;
数据结构RuleListNode链表代表了系统支持的规则类型,包括Alert,Log,Pass,Activate,Dynamic。每链表节电又通过ListHead结构类型字段RuleList指向下面的规则链表。
第二层数据结构ListHead,定义如下:
typedef struct_ListHead
{
RuleTreeNode *IpList;
RuleTreeNode *TcpList;
RuleTreeNode *UdpList;
RuleTreeNode *IcmpList;
Struct_OutputFuncNode *LogList;
Struct_OutputFuncNode *AlertList;
Struct_RuleListNode *ruleListNode;
} ListHead;
数据结构ListHead中若干字段IpList , TcpList , UdpList , IcmpList等又分别指向各协议类型划分的规则链表,同时,可能有一个回指针去对应RuleListNode节点。
最基础的数据结构包括RuleTreeNode和OptTreeNode,分别代表最基本的规则链表头和规则节点,并分别定义。
应用这个链表来解决实例问题并说明此链表结构与工作过程.
函数CreatDefaultRules()此时并没有执行规则解析的任务,只是负责上层规则构架,代码如下:
ListHead Alert; /*Alert Block Header*/
ListHead Log; /*Log Block Header*/
ListHead Pass ; /*Pass Blok Header*/
ListHead Activation; /*Activation Block Header*/
ListHead Dynamic; /*Dynamic Block Header*/
Void CreateDefaultRules()
{
CreateRuleType(“activation”,RULE_ACTIVATE,1,&Activation);
CreateRuleType(“dynamic”,RULE_DYNAMIC,1,&Dynamic);
CreateRuleType(“alert”,RULE_ALERT,1,&Alert);
CreateRuleType(“Pass”,RULE_PASS,0,&Pass);
CreateRuleType(“log”,RULE_LOG,1,&Log); }
链表头动作(5种):
图7.2 Snort 二维规则检测树
说明:以上举的两个例子一个是对WEB服务的CGI攻击,另一个是telnet攻击。还有很多应用层上的攻击,就不举例了。其中字符:Flow: 链接到检测插件。
Content: 用特定算法检查的字符串。
5)规则优化及快速匹配模块是Snort2.0版本中引入的最重要的组件。在SnortMain()函数中,主要涉及调用了建立和初始化规则优化和快速匹配数据结构的类型。包含有2个主要函数:OtnXMatchInfoInitialize(),fpCreateFastPacketDetection()
OtnXMatchInfoInitialize()主要是对数据类型OTNX_MATCH_DATA数据结构类型的全局变量omd,为它而申请空间。OTNX_MATCH_DATA数据结构中包括快速匹配时所需要的重要信息。而fpCreateFastPacketDetection()是建立快速匹配引擎的主要接口函数,严厉是读入有规则解析模块建立的规则链表中的所有规则链表头(上图所显示的RuleTreeNode类型)和规则选项节点(OptTreeNode类型),一边快速匹配之用。
6)数据包处理模块InterfaceThread()。
InterfaceThread()中功能简单,主要调用Libpcap库函数Pcap_loop()。Libpcap 库函数Pcap_loop()的接口函数,接口函数ProcessPacket()主要功能包括:
1) 调用数据包协议解码模块,并将解码结果存储在特定的Packet类型数据结构中。
2) 根据Snort的运行模式,调用不同的处理模块。
3) 对入侵检测的运行模式,调用Preprocess()模块。
ProcessPacket()函数代码如下显示:
void ProcessPacket(char *user, struct pcap_pkthdr * pkthdr, u_char * pkt)
{
Packet p; /* reset the packet flags for each packet */
p.packet_flags = 0;
pc.total++;
/* reset the thresholding subsystem checks for this packet */
sfthreshold_reset();
#if defined(WIN32) && defined(ENABLE_WIN32_SERVICE)
if( pv.terminate_service_flag || pv.pause_service_flag )
{
ClearDumpBuf(); /* cleanup and return without processing */
return;
}
#endif /* WIN32 && ENABLE_WIN32_SERVICE */
/* call the packet decoder */
(*grinder) (&p, pkthdr, pkt);
/* print the packet to the screen */
if(pv.verbose_flag)
{
if(p.iph != NULL)
PrintIPPkt(stdout, p.iph->ip_proto, &p);
else if(p.ah != NULL)
PrintArpHeader(stdout, &p);
else if(p.eplh != NULL)
{
PrintEapolPkt(stdout, &p);
}
else if(p.wifih && pv.showwifimgmt_flag)
{
PrintWifiPkt(stdout, &p); } }
switch(runMode)
{
case MODE_PACKET_LOG:
CallLogPlugins(&p, NULL, NULL, NULL);
break;
case MODE_IDS: /* allow the user to throw away TTLs that won't apply to the
detection engine as a whole. */
if(pv.min_ttl && p.iph != NULL && (p.iph->ip_ttl < pv.min_ttl))
{
DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
"MinTTL reached in main detection loop/n");
return;
} /* start calling the detection processes */
Preprocess(&p);
break;
default:
break;
}
ClearDumpBuf(); }
接口函数Process() 主要功能:
1) 遍历所有的预处理器模块。
2) 如果满足进行规则检测的条件,调用规则调用模块Detect()。
7.3几个重要的数据结构
Snort系统中最重要的全局数据结构就是Packet结构,Packet数据结构控制着整个系统正常工作的关键信息。所以,该数据结构在代码中出现的频率最高。
Packet数据结构如下(代码太长,截取部分):
Typedef struct_Packet
{
struct pcap_pkthdr pkth; /*BPF data*/
u_int8_t *pkt; /*base pointer to the raw packet data*/
Fddi_hdr fddihdr; /*FDDI support headers*/
…………
}
7.4规则检测组件
Snort2.0系统的快速匹配模块有最重要的设计特色,其在初始规则链表的基础上,重新构造了一套快速匹配的数据结构,并采用了多模式匹配搜索引擎。
Snort2.0快速规则检测模块,其功能主要涉及三步骤:
1. 构造初始的规则链表结构,主要由ParseRulesFile()和ParseRule()两个函数完成。
2. 读入规则链表各节点,并构造用快速匹配的新数据结构,该步骤由fpCreateFastPacketDetection()完成。
3. 对当前数据包执行具体的快速规则匹配任务,主要在fpEvalPacket()上。
图7.3 检测组建的主要函数
7.4.1 构造规则链表ParseRulesFile()和ParseRule()
ParseRulesFile()在主函数SnortMain()中调用。但是实际上ParseRulesFile()只是一个过渡接口,主要功能是读取规则配置文件并送实际的规则解析模块ParseRule()最后解析。规则解析模块ParseRule()其功能是解析所有的系统配置规则,包括插件配置,检测规则配置,变量定义,类定义等。
Snort系统的检测规则格式如下:
Alert Tcp any any ->192.168.1.0/24
ParseRule()基本算法流程图:
7.4.2 结构快速匹配规则引擎fpCreateFastPacketDetection()
Snort2.0系统快速匹配规则引擎的设计基础是如何更有效地划分规则集合。系统采用的基本思想是通过规则中的目的端口和源断口值来划分类别。
1) 如果源端口值为特定值,目的端口为任意值(ANY),则该规则加入到源端口值对应的子集合中。如果目的端口的值为特定值,则该规则同时加入到目的端口对应的子集合中。
2) 如果源端口值为特定值,目的端口为任意值(ANY),则该规则加入到源端口值对应的子集合中。如果目的端口的值也为任意值(ANY),则该规则同时加入到目的端口对应的子集合中。
3) 如果目的端口和源端口都为任意值(ANY),则该规则加入到通用子集合中。
4) 对于规则中端口值求反操作或者指定值范围的情况,等于端口值为ANY情况。
在构建快速规则匹配引擎中所涉及到的主要数据结构如下:
最高层数据结构是Port_Lule_Map数据结构,其定义如下:
#define Max_PORTS 64*1024
#define ANYPORT –1
Typedef struct{
Int prmNumDstRules;
Int prmNumSrcRules;
Int prmNumGenericRules;
Int prmNumDstGroups;
Int prmNumSrcGroups;
PORT_GROUP *prmSrcPort[MAX_PORTS];
PORT_GROUP *prmDrcPort[MAX_PORTS];
PORT_GROUP *prmGeneric;
}PORT_RULE_MAP;
对于通用规则节点链表而言,在构建快速规则匹配引擎中,它仅是作为一个过渡性的函数结构。对于未来的规则检测任务而言,每个数据包都有特定的源/目的端口值。
为了达成根据端口进行快速规则匹配的任务,函数fpCreateFastPacketDetection()在完成遍历操作后,对各个PORT_RULE_MAP结构中的通用规则链表进行了进一步处理,调用prmCompileGroups().该函数功能将通用规则链表中的各个规则节点加入到另外对应于特定端口值的两个规则链表中,前提条件是目标规则链表中已经存在节点,既在Port_Rule_Map结构中的Port_Group数组中每个非空元素中加入。
函数prmAddRule()根据端口值,选定当前RULE_PORT_MAP中对应的PORT_GROUP结构,然后实际调用prmxAddPortRule()来具体执行在指定PORT_GROUP结构中的对应规则节点连表中加入的OTNX结构变量。
快速匹配规则引擎结构图中,函数fpCreateFastPacketDetection()在完成规则链表的遍历操作后,既完成了各个规则子集合的划分,所有的规则节点都已经加入了如下三种类型之一的子集合里:
1) 对应于特定源端口值的PORT_GROUP结构的规则链表
2) 对应于特定目的端口值的PORT_GROUP结构的规则链表
3) 通用一般的规则节点链表。
最后,为适应多模式引擎算法,fpCreateFastPacketDetection调用BuildMultiPaternGroups()在每个Port_Group结构中构建多模式搜索引擎所需的数据结构。
Typedef struct_otnx_
{
OptTreeNode *otn;
RuleTreeNode *rtn;
}OTNX;
7.5预处理模块
预处理模块的作用是对当前截获的数据包进行预先处理,以便后续处理模块对数据包的处理操作。之所以最后讲是因为,有些入侵检测系统没有此模块,此模块是嵌入在数据截取部分中的。
Snort2.0以上系统中预处理模块按功能分三种:
1) 数据包分片重组及数据流重组
在正常情况下,数据包在网络上由于最大数据传输单元可能有限制MTU及网络延迟等问题,路由器会对数据包进行分片处理。但是恶意攻击者也会故意发送经过软件加工过的数据包,以便把一个带有攻击性的数据包分散到各个小的数据包中,并有可能打乱数据包传输次序,分多次传输到目标主机。这样做的好处是减少被检测到的概率。为此,入侵检测系统有专门针对数据包上DM字段(第四章重点讲述过)标示为分段数据流的处理,此模块对这些分段数据包可以进行正确的从组(即使包的发送次序被打乱)。
2) 协议编码
协议编码是指有些协议是比较灵活的,如HTTP等协议。它支持多种编码,如ASCII码等,这就需要预处理模块进行处理,以便后续模块进行操作。同时,还有可能发现一些特定攻击类型。如:
http://Domain.com/scripts/..%c1%c...../cmd.exe?/c+dir
就是我们熟悉的目录遍历漏洞特征
/default.ida?NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.. ..NNNNNNNNNNNNNNNN%u9090%6858%ucbd3…
就是我们熟悉的红色代码蠕虫特征
其中包含了ASCII码来标示。
更如:http://www.xxxx.com/%73%63%72%69%70...二进制表示URI为
HTTP://www.xxxx.com/scripts/hackme.exe的危险程序。
3) 协议异常检测
这指的是给予异常行为统计检查功能,能够针对一些异常的网络行为发出报警。如端口及特定服务(finger x.500等服务)的搜索。
7.5.1 预处理器的基本框架
所涉及到的函数如下:(在Spp_template.c/h中)
SetupTemplate() //注册函数,由InitPreprocessors调用
TemplateInit(u_char *) //预处理模块的初始化
ParseTemplateArgs(char *) //参数解析
PreproFunction(Packet *) //根据预处理模块的不同,执行不同
PreproRestartFunction(int)
PreproCleanExitFunction(int) //退出清理函数
重点介绍协议处理中的Http规范Spp_Http_Decode模块,与拒绝服务DOS 紧密相关的检测分段模块Spp_frag2.
7.5.2 Spp_Http_Decode模块
该模块主要负责对HTTP协议中URL字符进行规范化编码处理,避免攻击者通过某些特殊的字符编码方式来逃避后续的规则检测。对与IIS 等可以接受同URL字符串的多个编码形式:
http://www.xxx.com/%73%63%72%69%70%...6D%65.%65%78%65
http://www.xxx.com/%73%63%72%69%70%...6D%65.%65%78%65
HTTP://www.xxxx.com/scripts/hackme.exe
对于IIS会认为上面是一个请求。
该模块参数格式如下:
preprocedssor http_decode : var1 var2
如:
preprocedssor http_decode: 80 8080 8000 unicode iis_alt_Unicode
double_encode iis_flip_slash
7.5.3 Spp_frag2模块
Spp_frag2模块能够检测到若干种基于IP数据包分片技术的Dos攻击方法。这些类型的拒绝服务攻击经常利用操作系统协议栈(IP堆栈)的弱点,通过发送经过精心设计的异常数据包分片来对目标进行攻击。
该模块参数如:
preprocedssor frag2: var1 , var2
Spp_frag2预处理模块使用Frag2Data结构来存储当前模块的参数配置情况,其结构如下:
Typedef struct_frag2Data
{
u_int8_t os_flags;
u_int32_t memcap; // 内存
………
u_int8_t min_ttl //最小的生存值ttl
char frag2_alert; //*frag2 的警报是否打开
……..
SPMemControl frag_sp_data; //内存使用控制
…..
}Frag2Data
关于frag2的数据格式就不在多写了,这里简要分析一下Spp_frag2模块中的主功能模块Frag2Defrag().代码如下:(只列出重点代码,代码经过处理)
//主功能模块处理函数
void Frag2Defrag(Packet *p)
{
//如果表示字段显示不用分片处理操作,立即返回
if( !( p->preprocessors & pp_FRAG2 ))
{
return;
}
/*确定了数据包类型后,对只有分片的数据包进行种组处理,P->packet_flags & PKT_REBULT_FRAG的处理。 Snort会调用此函数。*/
if(p == NULL || p->iph == NULL || !p->frag_flag )
{
return;
}
if(p->packet_flags & PKT_REBUILT_FRAG)
{
return;
}
…….
//主二叉树中进行查询,是否存在满足当强数据包所指示IP源/目的地址条件的二叉树节点。返回Frag2Tracker结构变量。
Ft=GetFragTracker(p);
If(ft==Null)
{
//如果主树中不存在满足条件的节点,则表明当前数据包是一个新出现的IP分段数据包
if((f2_emergency.status == OPS_NORMAL)||(p->mf && p->frag_offset == 0)
{
//调用NewFragTracker()插入新节点。构造新节点的一个主要限制条件是当新出现的IP分段一定要为一个首分片才可以。此点主要防止泛滥IP分片数据,对IDS系统产生新的拒绝服务攻击
ft=NewFragTracker(p);
}
//如果不满足限制条件,则不进行后续的规则处理
Else
DisableDetect(p);
Return;
//返回调用模块
}
}
7.6 Snort网络检测模块总流程
网络数据包截获机制
网络数据截获方法
作者:浩海孤帆@bbs.net130.com
网络数据截获方法
网络数据包截获机制是网络入侵检测系统的基础组件。一般指通过截获整个网络的所有信息流量,根据信息源主机,目标主机,服务协议端口等信息简单过滤掉不关心的数据,再将用户感兴趣的数据发送给更高层的应用程序进行分析。流程图如下:
图5.1 网络数据截获流程
一方面要,网络截取模块要能保证截取到所有网络上的数据包,尤其是检测到被分片的数据包(这可能蕴涵着攻击)。
另方面,数据截取模块截取数据包的效率也是很重要的。
它直接影响整个入侵检测系统的运行速度。
5.2各种数据流截获方法
5.2.1 利用广播截取网络数据流
数据包的截取技术是依赖网卡的。而网卡可以通过广播监听到以太网络上的数据包,这就是数据包截取技术的基础。
要想截获不是给自己数据流,就必须绕开系统正常工作的机制,直接通过网卡的混杂模式,使之可以接受目标地址不是自己的MAC地址的数据包,直接访问数据链路层,取数据。
5.2.2各系统截取数据包机制
Linux系统为用户提供一种在理论上是数据链路层的,基于网卡驱动程序的,可以不用操作系统自身协议栈的接口(也称套接字)-SockPacket. 这种套接字可以从数据链路层(就是网线)上直接截取所有链路层数据包。而Unix则是通过Libpcap库直接与内核交互,实现网络截取。如:Libpcap,Tcpdump等。如图:
图5.2 Unix系统监听机制
BSD Packet Filter(BPF)机制来截取数据包。BPF可以说是各系统中最棒的截获方式。很多开源嗅探工具就是基于它而开发的。Windows系统也有类似情况,如:win系列上有*.vxd (VxD,VirtualDeviceDrive)(packet*.vxd)和 网卡.sys(为网卡芯片所开发)来驱动网卡截取数据包。
图5.3 Windows系统监听机制
5.2.3 BPF过滤
Unix&Linux系统有两种数据链路层截取机制,分别是BSD系列系统(NetBSD,OpenBSD,FreeBSD等)的BPF和Linux的SOCKET_PACKET。
BPF过滤
BPF主要由两大部分组成:
网络头接口
数据包过滤器。
网络头接口从网络设备驱动程序处收集数据包复制(在提交给系统协议栈之前),并传递给正在截获数据包的应用程序。而过滤器决定某一数据包是被接受或者拒绝以及如果被接受,数据包的那些部分会被复制给应用程序。BPF结构图如下:
图5.4 BPF结构示意图
如:TCPDump注:(1), Libpcap, Sniffer, eeye,等。一般情况,网卡驱动通过网卡把网络上的电平信号转化成数据包,再把截取到的数据传给系统自带的协议栈,然后在交由系统处理。这种方式与Unix下的BPF不同,它使得网卡驱动在把从网络截取的数据提交给系统之前,先拷贝一份给BPF,再由BPF 决定是否符合规则,不符合则丢弃,符合则存放到内存指定区,等待处理。
当然,BPF对网卡驱动交给系统协议栈的数据包不做任何干涉。
注1:TCPDump是伯克利实验室的Van Jacobson,Craig Leres和Stenven McCanne为分析TCP性能问题而写的跨平台的监听程序。
5.3基于Libpcap库的通用数据截获技术
Libpcap是用户态的数据包截获API函数接口,有独立和可移植行。最初,Libpcap是为了强大的,健壮TCPDump而编写的。它支持BPF过滤机制。Snort就是依赖于libpcap库进行数据包截取的程序之一(还有Ethereal,eeye等)。它的优点是可以从任何Unix内核平台上截取数据包,而不考虑什么芯片类型的网卡和驱动程序。更重要的是,它可以使开发人员编写自己的解码,显示,记录等程序。
5.2.2.1 Libpcap库主要函数
1. 头文件特征(pcap.h)
Libpcap库(数据流存储头文件 的结构定义如下图)。前半部分是数据库存储文件头的数据结构定义。
图5.5 头文件(pcap.h)定义部分截图
后半部是信息包头文件数据结构定义。
2. 打开并读取设备,设置过滤器部分
与最基层设备打交道。有三个函数:pcap_read() pcap_open_live 和 pcap_setfilter()
3. 脱机方式截取数据
及读取存储在营盘上的文件。有两个Pcap_open_offline()和Pcap_offline_read().
4. 本地网络设置检测部分
主要检测网络设置的函数有几个,包括Pcap_lookupdev() pcap_lookupnet()等。因为前面提过,Libpcap可移植。所以各种平台的Socket借口都是兼容的。
5. 主程序
都在Pcap.c中,该文件定义了读取数据的统一接口函数pcap_next(),调用此函数获取下一个数据包。
5.4 Snort调用Libpcap
在Snort运行启动时,Snort调用Libpcap库。当调用libpcap函数并初始化接口时,进入截取数据的循环模块—pcap循环。
在这个主循环—Pcaploop(),当网卡从网络介质上接收数据开始,Pcap_loop便对采集来的每个数据包都ProcessPacket()函数处理,如果出错或达到指定的处理包数就退出。(相关代码如下)
具体就是,Pcap_loop()最后根据数据链路类型来选择数据包,然后由ProcessPacket()来进行协议分析,实施信息流的匹配。
如:ProcessPaceket函数调用DecodeEthPkt函数来对以太网数据进行解码。其中DecodeEthPkt()函数再调用子函数Decode IP来对IP协议进行解码……
Libpcap函数功能列举:
Pcap_open_live(): 获得数据包通用句柄。
Pcap_lookup_dev(): 指向网络可用设备。
Pcap_looknet(): 初步判断网络设备本身的IP & netmask。
Pcap_Dump(): 基于TCPDump的,将网络数据包保存成文件。
程序部分代码如下:
/* Read all packets on the device. Continue until cnt packets read */
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
do {
n = p->read_op(p, cnt, callback, user);
} while (n == 0);
}
if (n <= 0)
return (n); //遇错误,返回
if (cnt > 0) {
cnt -= n;
if (cnt <= 0)
return (0); //达到制定数量,返回
}
}
}
pcap_loop()有几个重要参数:
参数是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);
Snort.c主程序中,Pcap_loop()函数有两种提取数据模式:
打开网卡和打开文件。
/* 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);
只有以上两种返回情况。
Snort把真实的数据包存储在内存中指针指向的数据结构中。在decode.h中,定义了所有Snort要使用到的数据结构,包括Tcp,Ip,以太桢,vlan桢等。Snort的这些结构将指针指向截获的包上来代表相应的协议。如指向以太桢用数据指针使用_EthrHdr头。
数据结构:
Typededf struct _etherhdr
{
u_int8_t ether_dst[6]; //目的地址;
u_int8_t ether_src[6]; //源地址;
u_int16_t ether_type; //协议类型;
}
typedef struct _Tcphdr
{
u_int16_t th_sport; //源端口;
u_int16_t th_dport; //目端口;
u_int32_t th_seq; //sequence number;
u_int32_t th_ack; //acknowledgement number;
u_int8_t th_offx2; //offset and reserved;
u_int8_t th_flags; //flags;
u_int16_t th_win; //window;
u_int16_t th_sum; //checksum;
u_int16_t th_urp; //urgent pointer;
}
小结
网络数据的截取是入侵检测系统的基础。本节从网络数据截取机制开始讲,讲述了常见的两种机制。重点讲述了Libpcap库的网络数据包截取机制。