Skip to content

Latest commit

 

History

History
executable file
·
166 lines (140 loc) · 6.1 KB

0x05_规则匹配&插件运行.md

File metadata and controls

executable file
·
166 lines (140 loc) · 6.1 KB

规则匹配&插件运行

这一章节包括两部分,snort的规则匹配过程和插件运行的过程。在之前的章节中也能看到,插件的主要功能函数eval其实是保存在了规则最后解析出的那个数据结构中的,所有这里将两部分的知识写在同一章节了。

这里接上文数据包捕获后的回调packet_callback继续说,首先回顾一下代码

DAQ_Verdict Snort::packet_callback(
    void*, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt)
{

...............
    DAQ_Verdict verdict = process_packet(s_packet, pkthdr, pkt);   //数据包解析,以及规则匹配
    ActionManager::execute(s_packet);                           //这里对这个包进行一些action的操作,主要是基于Action接口,具体见下文

...............
    return verdict;
}

这里需要重点看的,是process_packet这个函数,它在里面进行了数据包的解析工作,并进入检测功能的入口。

DAQ_Verdict Snort::process_packet(
    Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, bool is_frag)
{
    aux_counts.rx_bytes += pkthdr->caplen;

    PacketManager::decode(p, pkthdr, pkt, is_frag);   //对数据包进行解码
.................

    if (is_frag)
    {
        p->packet_flags |= (PKT_PSEUDO | PKT_REBUILT_FRAG);
        p->pseudo_type = PSEUDO_PKT_IP;
    }

    set_policy(p);  // FIXIT-M should not need this here

    if ( !(p->packet_flags & PKT_IGNORE) )
    {
        clear_file_data();
        main_hook(p);         //检测功能的入口

        // FIXIT-L remove this onload when DAQng can push multiple packets
        if ( p->flow )
            DetectionEngine::onload(p->flow);
    }

..............
    return DAQ_VERDICT_PASS;
}

这个main_hook在其他地方进行了初始化,这里它所对应的函数实际为DetectionEngine::inspect

bool DetectionEngine::inspect(Packet* p)
{
    bool inspected = false;
    {
        PacketLatency::Context pkt_latency_ctx { p };

        if ( p->ptrs.decode_flags & DECODE_ERR_FLAGS )  //如果包解析失败了,就将其drop掉
        {
            if ( SnortConfig::inline_mode() and
                SnortConfig::checksum_drop(p->ptrs.decode_flags & DECODE_ERR_CKSUM_ALL) )
            {
                p->active->drop_packet(p);
            }
        }
        else
        {
            enable_content(p);
            p->alt_dsize = 0;  // FIXIT-H should be redundant

            InspectorManager::execute(p);   //这里进行一些预处理的操作
            inspected = true;

            if ( !all_disabled(p) )
            {
                if ( detect(p, true) )    //实际的检测功能
                    return false; // don't finish out offloaded packets
            }
        }
        finish_inspect_with_latency(p);
    }
    finish_inspect(p, inspected);
    return true;
}

这里detect函数会进入检测部分的处理,中间的调用过程这里就不跟了,它最后会调用到static void fpEvalPacket(Packet* p, FPTask task)这个函数

static void fpEvalPacket(Packet* p, FPTask task)
{
    OtnxMatchData* omd = p->context->otnx;

    /* Run UDP rules against the UDP header of Teredo packets */
    // FIXIT-L udph is always inner; need to check for outer
    if ( p->ptrs.udph && (p->proto_bits & (PROTO_BIT__TEREDO | PROTO_BIT__GTP)) )
        fpEvalPacketUdp(p, omd, task);

    switch (p->type())
    {
    case PktType::IP:
        fpEvalHeaderIp(p, omd, task);
        fpEvalHeaderSvc(p, omd, SNORT_PROTO_IP, task);
        break;

    case PktType::ICMP:
        fpEvalHeaderIcmp(p, omd, task);
        fpEvalHeaderSvc(p, omd, SNORT_PROTO_ICMP, task);
        break;

    case PktType::TCP:
        fpEvalHeaderTcp(p, omd, task);
        fpEvalHeaderSvc(p, omd, SNORT_PROTO_TCP, task);
        break;

    case PktType::UDP:
        fpEvalHeaderUdp(p, omd, task);
        fpEvalHeaderSvc(p, omd, SNORT_PROTO_UDP, task);
        break;
...............
    default:
        break;
    }
}

这里会根据各个协议不同的情况进行处理,这里以tcp为例,继续往下分析。首先调用fpEvalHeaderTcp函数

static inline void fpEvalHeaderTcp(Packet* p, OtnxMatchData* omd)
{
    PortGroup* src = nullptr, * dst = nullptr, * any = nullptr;
    //这里首先根据端口信息(p->ptrs.dp目的端口 p->ptrs.sp源端口)等,获取到相应的PortGroup,赋值给src、dst、any变量
    if ( !prmFindRuleGroupTcp(SnortConfig::get_conf()->prmTcpRTNX, p->ptrs.dp, p->ptrs.sp, &src, &dst, &any) )   
        return;
    //然后根据相应的PortGroup的情况,分别进行处理
    if ( dst )
        fpEvalHeaderSW(dst, p, 1, 0, 0, omd);

    if ( src )
        fpEvalHeaderSW(src, p, 1, 0, 0, omd);

    if ( any )
        fpEvalHeaderSW(any, p, 1, 0, 0, omd);
}

该函数首先会根据端口等信息获取到对应的portgroup对象,而这里的portgroup对象,在前面的章节中也有提到,它本身是根据端口保存了带有相应端口的规则信息的,在获取到对应端口的portgroup对象之后,调用fpEvalHeaderSW函数,最后这里会调用进detection_option_node_evaluate,使用相关的规则的node的evaluate函数,对包的信息进行检测和处理,这里对应的evaluate函数即是先前规则解析到对应插件的检测功能函数,调用完成后,会返回相应的检测结果,这里由一个枚举类型描述

enum EvalStatus { NO_MATCH, MATCH, NO_ALERT, FAILED_BIT };

这里简单列一下IpsOptions中的ack检测的,eval函数

IpsOption::EvalStatus TcpAckOption::eval(Cursor&, Packet* p)
{
    Profile profile(tcpAckPerfStats);

    if ( p->ptrs.tcph && config.eval(p->ptrs.tcph->th_ack) )
        return MATCH;

    return NO_MATCH;
}

可以看到,这里仅仅是检查了包的ack项,如果存在的话,则返回MACTH

总结

规则匹配和插件运行这一部分其实比较复杂,我这部分的代码也没有逐行看完,只有了解大致的流程,首先它会根据端口信息获取到对应的portgroup,再对portgroup中的信息进行遍历,并调用相关插件的eval函数,若检测成功则返回MATCH。