suricata UT测试用例中使用的几个重要的辅助函数

对于suricata UT用例来说,很多时候需要构造报文作为输入,有的时候还要构造流作为输入,获取报文匹配结果。为此在suricata中使用util-unittest-helper.c文件来组织这些用于构造UT用例的辅助函数。

unittest-helper.c文件是由编译宏控制编译的,即#ifdef UNITTESTS,在正式的发布版本中不会被编进入执行程序中,但是在运行UT的时候只要定义UNITTESTS,即可以运行,如图1:
在这里插入图片描述
图1

util-unittest-helper.c文件主要分为两大类,即辅助的构造函数以及针对这次构造函数的测试用例。辅助构造函数又分为如下几类:

1,构造报文相关
构造报文最基本的是UTHBuildPacketReal函数,该函数的作用是构造特定IP以及特定端口的TCP,UDP和ICMP报文,该函数的入参就是报文payload数据及其长度,以及五元组。该函数内部的实现也是很明确,即申请Packet这样干一个内存空间,将入参的值赋上后即完成了报文的创建。因为在外部传递到引擎的报文最终都会转化为Packet结构体进行处理。可以看到很多的测试用例都会使用该函数进行报文的创建,例如DetectDnsQueryTest04。当然此函数针构造的是IPV4报文,如果想要构造IPV6报文,则可以使用UTHBuildPacketIPV6Real函数,由此可见suricata内置是支持IPV6的。

在一条用例执行完毕之后,需要对于申请的报文空间进行释放,就需要使用UTHFreePacket进行内存的释放。

UTHBuildPacketReal为最原始的构造报文函数,还有一些函数封装了此函数提供了更多差异化的函数构造功能,如下:

UTHBuildPacket使用默认的IP和端口进行报文构造,使用者只需要传输payload内容,长度和协议三个参数即可。

UTHBuildPacketSrcDst使用了默认端口,需要payload内容,长度,协议以及目的源IP五个参数即可。

UTHBuildPacketSrcDstPorts使用了默认IP,需要payload内容,长度,协议以及目的源端口五个参数即可。因为对于有的用例来说需要关注端口而不关注IP,则使用UTHBuildPacketSrcDstPorts即可,其他同理。

上述的函数是构造单个报文,如果想要构造多个报文的话使用一组循环即可。上述构造报文的函数还有一个问题在于,其入参关注的是五元组,那么除了五元组之外的,比如TCP的ACK字段是没有构造的,对于这种情况该怎么办呢?可以使用UTHBuildPacketFromEth,对于构造单个报文来说,比较简洁的办法是使用一些真实的报文数据进行导入。可以看到函数的入参便是原始的报文数据,以及数据大小还有报文个数。其本质是通过引擎的解码函数DecodeEthernet来填充Packet结构体的内容。正常来说引擎的工作模式也是对于接收到的数据,送入DecodeEthernet进行解码,然后填充Packet结构体各个字段的内容,对于IP,TCP的各个字段在Packet结构体中就会有所体现。由此你也可以看出引擎是从DecodeEthernet开始处理的,当然该函数还是要被wrapper的,因为外层还有一些逻辑。对于构造多个详细报文来说,使用UTHBuildPacketArrayFromEth函数即可。释放多个报文的函数为UTHFreePackets。

2,构造流相关

引擎中使用流表来管理流,如果想要创建单条流,使用UTHBuildFlow函数。同报文创建相同,该函数为单条流申请内存,空间并将四元组以及IPV4或者IPV6对应的标志位赋值即生成流。

由于特定的报文属于某一个流,因此实用 UTHAssignFlow函数指定报文的归属,其实内容很简单就是将报文结构体中的flow和标志位关联即可。其实可以想象当引擎收到一个报文的时候,会通过四元组去查找流表,如果流表中流已经存在,将报文进行关联,如果不存在,新建流在关联。

释放流内存的函数为UTHFreeFlow。

另外还发现存在UTHAddStreamToFlow,UTHAddSessionToFlow等函数。首先flow,session,stream这几个概念如下:

flow:通常所说的流,包括TCP流以及UDP流,由五元组唯一确定的通信双方的虚拟链接。
stream:这个就不像flow是一个比较通用的概念,stream是suricata中特指TCP的一条流的上行链接或者下行链接。通常来说flow包含了双向的报文,而一条stream特指TCP的上行报文或者下行报文内容。
session:session也是suricata中为了处理TCP的内容而设置的概念,可以说session和tcp 的flow总体上是指的一回事,当然两个结构体并不相同。由于flow除了表示TCP流之外,还能够表示UDP流,因此flow一个比较通用的结构体。由CP流的处理远比UDP复杂,包括各种报文重组等等,因此使用了session来表示独立于flow之外的一些功能,比如报文重组,因此可以简单的理解session就是一个TCP流的处理结构。

由于TCP的处理比之UDP要复杂许多,为此suricata提供了stream 表示单向的流内容,使用session来进行额外的流处理。

UTHAddSessionToFlow即申请一个session的结构空间,和flow进行关联。由于在session结构中存在了client以及server的stream,因此在UTHAddStreamToFlow的主要工作就是将接受到的数据加入到上下行stream的buffer中。详细使用可以参考DetectUriSigTest06用例。UTHRemoveSessionFromFlow表示从flow中去除session。

3,构造匹配规则相关
UTHAppendSigs向引擎上下文加入规则。通常来说规则文件是以.rules文件提供的,文件中每一行就是一条规则,通过文件加载形式赋值给引擎。但是在UT测试的时候,需要时用UTHAppendSigs函数给引擎上下文添加规则,其中入参包括引擎上下文结构,规则数组,即一行行规则的字符串数组,还有规则个数。其本质也是调用引擎的DetectEngineAppendSig函数完成具体任务,这个函数也是规则加载过程中非常重要的函数。可以参考SCSigOrderingTest12用例中的使用方式。

UTHMatchPackets的入参是引擎上下文,以及若干个报文,目的是让报文去匹配规则。在函数中封装了引擎最为重要的一些函数,包括SigGroupBuild将解析的规则转换为内部的结构;DetectEngineThreadCtxInit初始化引擎上下文;SigMatchSignatures->DetectRun规则检测等等。构造UTHMatchPackets的目的在于作为用例使用的公共部分(但是实际使用的并不多),更多的用例会单独的使用SigGroupBuild以及DetectEngineThreadCtxInit等函数构造测试逻辑。但是通过UTHMatchPackets可以了解到报文匹配规则前后的一些设置,执行的逻辑,这是很重要的。

UTHCheckPacketMatchResults检查报文匹配的结果是否正确,调用PacketAlertCheck进行检查。通过PacketAlertCheck也可以得知,匹配的结果存储在p->alerts.alerts[i].s->id中。

用例SCSigOrderingTest12中正是体现了上述的内容,先是示给引擎上下文添加规则UTHAppendSigs;然后通过报文进行规则的匹配UTHMatchPackets;最后检查匹配结果UTHCheckPacketMatchResults。

UTHPacketMatchSig中使用报文去匹配引擎上下文中的规则,UTHPacketMatchSig的入参为报文和规则,其目的是直接使用报文匹配指定的规则。对比UTHMatchPackets函数,他们用到的执行逻辑都是一致的,即SigGroupBuild将解析的规则转换为内部的结构;DetectEngineThreadCtxInit初始化引擎上下文;SigMatchSignatures->DetectRun规则检测等等。UTHPacketMatchSigMpm比之UTHPacketMatchSig更近一步,增加了多模匹配算法的类型这个入参。

UTHParseSignature测试规则解析是否正确。

UTHGenericTest对前面介绍的函数又进行了封装,即直接扔进去报文,规则,预期结果,然后给出检测结果是否符合预期。

明白了上述几类辅助函数,相信对于该文件中剩下的用例也是很容易理解的。

本文为CSDN村中少年原创文章,未经允许不得转载,博主链接这里

猜你喜欢

转载自blog.csdn.net/javajiawei/article/details/104881346