C++入侵检测与网络攻防之IP/TCP报文拆解以及SSH识别

发布于:2025-05-01 ⋅ 阅读:(17) ⋅ 点赞:(0)

目录

1.IP报文结构讲解

2.IP报文分片讨论

3.IP报文解析的实现

4.日志模块的讨论

5.TCP报头解析

6.复习

7.协议遍历的实现

8.流程回顾以及调整

9.SSH协议分析以及解析思路

10.识别ssh版本协商报文1

11.识别ssh版本协商报文2

12.剩余报文识别思路


1.IP报文结构讲解

2.IP报文分片讨论

1 ip报文的解析

    版本:IP报文版本号 IPV4:4,IPV6:6
        要判:值为4

    首部长度:IP header 长度,单位是4字节, 没有选项,则一般为5(5x32bit=20B)
        要记住这个长度,用来计算数据区域的起始位置

    8位服务类型:一般没有使用,详细参考RFC
        不用管
    总长度:header+数据 总长度
        要使用来计算数据区域的长度
        数据区域的长度 = 总长度 - ip报头长度

3.IP报文解析的实现

  16位标识:IP 报文的唯一id,分片报文的id 相同,便于进行重组。
    3位标志:标明是否分片。是一个3位的控制字段,包含:
        保留位:1位
        不分段位:1位,取值:0(允许数据报分段)、1(数据报不能分段)
        更多段位:1位,取值:0(数据包后面没有包,该包为最后的包)、1(数据包后面有更多的包)


        判断思路:只判断第一个分片,其他分片先不管,第一个分片的判断条件就是为0

    TTL:生存时间,即路由器的跳数,每经过一个路由器,该TTL 减一,因此路由器需要重新计算IP报文的校验和。
        不用管
    8位协议:ICMP:1,TCP:6,UDP:17,其他的自行百度
        必须判断,而且是区分数据区域的重要标准


    首部校验和:IP header校验和,接收端收到报文进行计算如果校验和错误,直接丢弃。
        目前先默认校验和是对的,不管

4.日志模块的讨论

  源IP地址:无须解释
    目的IP地址:无须解释
    选项:这个一般也没有使用。详细参考RFC
        不需要管


    ip报头的结构定义
    struct iphdr                          
      {                                   
    #if __BYTE_ORDER == __LITTLE_ENDIAN          //小端字节序的情况下
        unsigned int ihl:4;                      //位域,该字段只占用4位
        unsigned int version:4;           
    #elif __BYTE_ORDER == __BIG_ENDIAN           //大端字节序的情况下
        unsigned int version:4;           
        unsigned int ihl:4;               
    #else                                 
    # error "Please fix <bits/endian.h>"  
    #endif                                
        uint8_t tos;                      
        uint16_t tot_len;                 
        uint16_t id;                      
        uint16_t frag_off;                
        uint8_t ttl;                      
        uint8_t protocol;                 
        uint16_t check;                   
        uint32_t saddr;                   
        uint32_t daddr;                   
        /*The options start here. */      
      };                                  
 

5.TCP报头解析

 注意,分片只处理第一个分片, frag_off网络字节序的后13位 == 0
    按位与

    frag_off = 1110000000000000   

   跟这个数按位与 0001111111111111    0x01ff
 

6.复习

2 日志模块
    
    1 并发问题
        线程1 写 aaaaaaa 到文件
        线程2 写 bbbbbbb 到文件
        最后可能日志就串在一起 : aabbababb

    2 日志写到哪里
        1 文件
        2 服务器 syslog

        做手机游戏,游戏闪退,收集手机端游戏日志

    3 日志的格式
        统一,拥有的字段: pid tid uid .... 模块 函数名 文件第几行 .....

        能够方便之后进行二次处理

        开源的日志模块


    4 设计上
        制作wrapper  , 转接口

        使用宏

        #define DPI_LOG_DEBUG(.....) fprintf(stderr,__VA_ARGS__)
 

7.协议遍历的实现

3 TCP报文解析思路

struct tcphdr                                                                                                                                                             [202/374]
  {                                                                                                                                                                         
        uint16_t source;                             
        uint16_t dest;                                                         
        uint32_t seq;                                                            
        uint32_t ack_seq;   

        uint16_t doff:4;     //tcp 头部长度
        uint16_t res1:4;
        uint16_t res2:2;
        uint16_t urg:1;
        uint16_t ack:1;
        uint16_t psh:1;
        uint16_t rst:1;
        uint16_t syn:1;
        uint16_t fin:1;
        uint16_t window;
        uint16_t check;
        uint16_t urg_ptr;

};
 

8.流程回顾以及调整

4 具体协议报文分析

    判断报文属于什么应用协议,对应的报文数量++     
    if(dpi_pkt_ssh_analyze(pkt))                   
    {                                              
        res->ssh_count++;                            
    }                                              
    else if(dpi_pkt_ftp_analyze(pkt))              
    {                                              
        res->ftp_count++;                            
    }                                              
    ........      

    每次要扩展一个协议,那么就要添加一个if else对,比较繁琐
    对于这些类似的代码,或者结构,想办法做成一个循环
    将这些协议报文分析的函数放到一个容器中,做遍历,最终遍历某个函数成功识别了报文,对应报文数量++就行


    解决的问题:
    1 容器
        数组,存函数指针
    2 函数调用
        遍历数组
        for(.....)
        {
            res = arr[i](pkt);
            ....
        }
    3 报文数量结果的存储
        结果也存到一个数组中,方便之后进行输出

    4 扩展性   
        下次如果还要添加新的协议识别函数应该怎么做,尽量减少工作量

9.SSH协议分析以及解析思路

  定义一个枚举,当前项目支持什么协议
    typedef enum dpi_protocol_tcp
    {
        ProtocolBegin=-1,
        SSH,
        FTP,
        ProtocolEnd
    }dpi_protocol_tcp;

    //定义一个函数指针,专门用来识别协议报文
    typedef int (*dpi_protocol_analyze_func_t)(dpi_pkt *pkt);

    dpi_protocol_analyze_func_t  dpi_analyze_funcs[ProtocolEnd];

10.识别ssh版本协商报文1

5 ssh报文识别思路

    流程:
        1 软件版本协商交换
            客户端告诉服务器:
                SSH-2.0-OpenSSH_8.0 

            服务器告诉客户端:
                SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4


            这类型报文容易识别

        2 算法协商
            大家列出支持的加密算法,从中挑选一个来进行通讯


        3 公密钥交换
            ......

        4 正常通讯,ssh操作
            密文

11.识别ssh版本协商报文2

   目前要识别的是TCP的应用层协议
    TCP是面向连接,只要连接没有断开,跑在上面的协议是唯一

        只要有一次被抓到识别是什么协议的,这个连接的报文你都认为是这个协议就行

    识别条件:
        报文如果前面4个字节 是 SSH- 默认是ssh报文

    剩余报文的识别思路:
        直接标识这个连接的报文都是ssh报文就行
 

12.剩余报文识别思路

  1 如何标识一个连接?
            struct dpi_connection
            {
                uint32_t src_ip;
                uint16_t src_port;
                uint32_t dst_ip;
                uint16_t dst_port;

                dpi_protocol protocol;  //该连接是什么协议
            };


        2 如何管理这些链接,下次能够查找得到?
            将这些连接放到一个容器,自己造一个容器
 


网站公告

今日签到

点亮在社区的每一天
去签到