snort的gtp解码源码分析

GTP 全称为GPRS Tunneling Protocol即GPRS隧道协议。GTP(GPRS隧道协议)用于建立核心通信网络GSN(GPRS服务节点)之间的通道。snort提供的GTP解码和预处理器提供通过GTP解决对这些网络的入侵企图的方法。它还使检测新攻击更容易。

GTP解码器提取GTP PDU内的有效载荷;GTP预处理器检查所有信令消息,并提供关键字以供进一步检查。

启用和配置解码器后,解码器将剥离GTP头并解析底层IP/TCP/UDP封装的数据包。因此,所有规则和检测工作就像没有GTP头一样。

例子:

大部分数据包时以下格式:

IP -> UDP -> GTP -> IP -> TCP -> HTTP

 

如果您有一个标准HTTP规则"alert tcp any any ->  any $HTTP_PORTS (msg:
"Test HTTP"; flow:to_server,established; content:"SOMETHINGEVIL"; http_uri; 
 .... sid:X; rev:Y;)",如果你配置了GTP decoder(config enable_gtp),它可以对被gtp协议包含内部的http内容发出报警。

下面我们以源码的形式来分析解码过程,关于预处理器在另一篇博客中讲解。

其中关于解析的原理可以参考之前写过的博客snort 源码分析之模式匹配引擎。根据这篇博客我们知道:

config enable_gtp

的解析过程。

ParseConfigFile=》snort_conf_keywords[i].parse_func(sc, p, args)=》ParseConfig=》config_opts[i].parse_func(sc, opts)=》ConfigEnableGTPDecoding 通过这一系列的调用,gtp decoder的配置就算是解析完成了。

void ConfigEnableGTPDecoding(SnortConfig *sc, char *args)
{
    PortObject *portObject;
    int numberOfPorts = 0;

    if (sc == NULL)
        return;

    DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Enabling GTP decoding\n"););
    sc->enable_gtp = 1;//设置启用标志

    /*Set the ports*/
    //解析端口配置 在snort.conf中配置了portvar GTP_PORTS [2123,2152,3386],所以会成功
    portObject = PortVarTableFind( sc->targeted_policies[getParserPolicy(sc)]->portVarTable, "GTP_PORTS");
    if (portObject)
    {
       sc->gtp_ports =  PortObjectCharPortArray(sc->gtp_ports,portObject, &numberOfPorts);//65536大小的数组
    }

    if (!sc->gtp_ports || (0 == numberOfPorts))
    {
        /*No ports defined, use default GTP ports*/
        sc->gtp_ports = (char *)SnortAlloc(UINT16_MAX);
        sc->gtp_ports[GTP_U_PORT] = 1;
        sc->gtp_ports[GTP_U_PORT_V0] = 1;

    }
}

 之前的数据包,上层协议是udp 所以在snort中的DecodeUDP函数中调用了DecodeGTP


void DecodeUDP(const uint8_t * pkt, const uint32_t len, Packet * p)
{
.......
    if (ScGTPDecoding() &&/*为true*/
         (ScIsGTPPort(p->sp)||ScIsGTPPort(p->dp)))/*端口配置 2152 所以为true*/
    {
        if ( !p->frag_flag )//没有分段
            DecodeGTP(pkt + sizeof(UDPHdr), len - sizeof(UDPHdr), p);//调用gtp解码函数
    }

}

ScGTPDecoding:

static inline int ScGTPDecoding(void)
{
    return snort_conf->enable_gtp;//在解析配置的时候,设置为1
}


//--------------------------------------------------------------------
// decode.c::GTP
//--------------------------------------------------------------------

/* Function: DecodeGTP(uint8_t *, uint32_t, Packet *)
 *
 * GTP (GPRS Tunneling Protocol) is layered over UDP.
 * Decode these (if present) and go to DecodeIPv6/DecodeIP.
 *
 */

void DecodeGTP(const uint8_t *pkt, uint32_t len, Packet *p)
{
    uint32_t header_len;
    uint8_t  next_hdr_type;
    uint8_t  version;
    uint8_t  ip_ver;
    GTPHdr *hdr;

    DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Start GTP decoding.\n"););

    hdr = (GTPHdr *) pkt;

    if (p->GTPencapsulated)
    {
        DecoderAlertEncapsulated(p, DECODE_GTP_MULTIPLE_ENCAPSULATION,
                DECODE_GTP_MULTIPLE_ENCAPSULATION_STR,
                pkt, len);
        return;
    }
    else
    {
        p->GTPencapsulated = 1;
    }
    /*Check the length*/
    if (len < GTP_MIN_LEN)//检查数据包长度
       return;
    /* We only care about PDU*/
    if ( hdr->type != 255)
       return;
    /*Check whether this is GTP or GTP', Exit if GTP'*/
    if (!(hdr->flag & 0x10))
       return;

    /*The first 3 bits are version number*/
    version = (hdr->flag & 0xE0) >> 5;
    switch (version)//检查版本
    {
    case 0: /*GTP v0 0版本*/
        DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "GTP v0 packets.\n"););

        header_len = GTP_V0_HEADER_LEN;//数据包头固定长度
        /*Check header fields*/
        if (len < header_len)
        {
            DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
            return;
        }

        p->proto_bits |= PROTO_BIT__GTP;

        /*Check the length field. */
        if (len != ((unsigned int)ntohs(hdr->length) + header_len))
        {
            DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Calculated length %d != %d in header.\n",
                    len - header_len, ntohs(hdr->length)););
            DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
            return;
        }

        break;
    case 1: /*GTP v1 1版本*/
        DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "GTP v1 packets.\n"););

        /*Check the length based on optional fields and extension header*/
        if (hdr->flag & 0x07)
        {

            header_len = GTP_V1_HEADER_LEN;//1版本的头长度

            /*Check optional fields*/
            if (len < header_len)
            {
                DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
                return;
            }
            next_hdr_type = *(pkt + header_len - 1);//下一个扩展属性的类型

            /*Check extension headers*/
            while (next_hdr_type)//循环解码扩展属性,然后更新header_len 的值
            {
                uint16_t ext_hdr_len;
                /*check length before reading data*/
                if (len < header_len + 4)
                {
                    DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
                    return;
                }

                ext_hdr_len = *(pkt + header_len);

                if (!ext_hdr_len)
                {
                    DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
                    return;
                }
                /*Extension header length is a unit of 4 octets*/
                header_len += ext_hdr_len * 4;

                /*check length before reading data*/
                if (len < header_len)
                {
                    DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
                    return;
                }
                next_hdr_type = *(pkt + header_len - 1);
            }
        }
        else
            header_len = GTP_MIN_LEN;

        p->proto_bits |= PROTO_BIT__GTP;

        /*Check the length field. */
        if (len != ((unsigned int)ntohs(hdr->length) + GTP_MIN_LEN))
        {
            DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Calculated length %d != %d in header.\n",
                    len - GTP_MIN_LEN, ntohs(hdr->length)););
            DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
            return;
        }

        break;
    default:
        DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Unknown protocol version.\n"););
        return;

    }

    PushLayer(PROTO_GTP, p, pkt, header_len);//添加gtp头这一层,保存了这一层的相关信息,比如开始地址、长度、协议信息

    if ( ScTunnelBypassEnabled(TUNNEL_GTP) )
        Active_SetTunnelBypass();

    len -=  header_len;//去除gtp头长度,所以如果看起gtp解码,则可以顺利去除这一层,然后重新解码ip层和传输层
    if (len > 0)//如果还有数据,继续处理
    {
        ip_ver = *(pkt+header_len) & 0xF0;//前面的截图,可以看出,内部的数据接下来为ip层
        if (ip_ver == 0x40)//如果ipv4,调用DecodeIP进行解码
            DecodeIP(pkt+header_len, len, p);
        else if (ip_ver == 0x60)
            DecodeIPV6(pkt+header_len, len, p);
        p->packet_flags &= ~PKT_UNSURE_ENCAP;
    }

}

大致的流程就这些了。所以到这里就能理解为什么解码完GTP 之后就可以像普通的数据包一样进行处理并且检测并响应标准的http规则了

Supongo que te gusta

Origin blog.csdn.net/guoguangwu/article/details/119279525
Recomendado
Clasificación