zoukankan      html  css  js  c++  java
  • 解析pcap抓包文件,包含pcap头、frame头、eth层、ip层、tcp/udp层等。 C语言+python部分解析

      参考http://blog.csdn.net/koudaidai/article/details/7673082

    首先看下最常见的包的层次:frame层是不是实际传输的数据,是tcpdump添加的一层。eth层如果显示linuxcooked的话是没有指定网卡抓包的原因需要tcpdump -i 指定网卡。

    可能会有其他协议层,自行添加即可。例如下图2

                              

    1、几个结构体

    //pacp文件头结构体
    struct pcap_file_header
    {
        bpf_u_int32 magic;       /* 0xa1b2c3d4 */
        u_short version_major;   /* magjor Version 2 */
        u_short version_minor;   /* magjor Version 4 */
        bpf_int32 thiszone;      /* gmt to local correction */
        bpf_u_int32 sigfigs;     /* accuracy of timestamps */
        bpf_u_int32 snaplen;     /* max length saved portion of each pkt */
        bpf_u_int32 linktype;    /* data link type (LINKTYPE_*) */
    };
    
    //时间戳
    struct time_val
    {
        int tv_sec;         /* seconds 含义同 time_t 对象的值 */
        int tv_usec;        /* and microseconds */
    };
    
    //pcap数据包头结构体
    struct pcap_pkthdr
    {
        struct time_val ts;  /* time stamp */  
        bpf_u_int32 caplen; /* length of portion present */  
        bpf_u_int32 len;    /* length this packet (off wire) */ 
    };
    
    //数据帧头
    typedef struct FramHeader_t
    { //Pcap捕获的数据帧头
        u_int8 DstMAC[6]; //目的MAC地址
        u_int8 SrcMAC[6]; //源MAC地址
        u_short FrameType;    //帧类型
    } FramHeader_t;
    
    //IP数据报头
    typedef struct IPHeader_t
    { //IP数据报头
        u_int8 Ver_HLen;       //版本+报头长度
        u_int8 TOS;            //服务类型
        u_int16 TotalLen;       //总长度
        u_int16 ID; //标识
        u_int16 Flag_Segment;   //标志+片偏移
        u_int8 TTL;            //生存周期
        u_int8 Protocol;       //协议类型
        u_int16 Checksum;       //头部校验和
        u_int32 SrcIP; //源IP地址
        u_int32 DstIP; //目的IP地址
    } IPHeader_t;
    
    //TCP数据报头
    typedef struct TCPHeader_t
    { //TCP数据报头
        u_int16 SrcPort;//源端口
        u_int16 DstPort;//目的端口
        u_int32 SeqNO;//序号
        u_int32 AckNO; //确认号
        u_int8 HeaderLen; //数据报头的长度(4 bit) + 保留(4 bit)
        u_int8 Flags; //标识TCP不同的控制消息
        u_int16 Window; //窗口大小
        u_int16 Checksum; //校验和
        u_int16 UrgentPointer;  //紧急指针
    }TCPHeader_t;
    
    //UDP数据
    typedef struct UDPHeader_s
    {
        u_int16_t SrcPort;     // 源端口号16bit
        u_int16_t DstPort;    // 目的端口号16bit
        u_int16_t len;        // 数据包长度16bit
        u_int16_t checkSum;   // 校验和16bit
    }UDPHeader_t;
    View Code

    2、代码只是解析源ip 包大小以及时间输出到文件中,其他字段自行添加。简单的将就是read协议长度数据到对应协议结构体中输出即可。可以认为都是小端序的。

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<netinet/in.h>
    #include<time.h>
    #include <unistd.h>
    #include <sys/stat.h>
    
    #define BUFSIZE 10240
    #define STRSIZE 1024
    
    typedef int32_t bpf_int32;
    typedef u_int32_t bpf_u_int32;
    typedef u_int16_t u_short;
    typedef u_int32_t u_int32;
    typedef u_int16_t u_int16;
    typedef u_int8_t u_int8;
    
    //pacp文件头结构体
    struct pcap_file_header
    {
        bpf_u_int32 magic;       /* 0xa1b2c3d4 */
        u_short version_major;   /* magjor Version 2 */
        u_short version_minor;   /* magjor Version 4 */
        bpf_int32 thiszone;      /* gmt to local correction */
        bpf_u_int32 sigfigs;     /* accuracy of timestamps */
        bpf_u_int32 snaplen;     /* max length saved portion of each pkt */
        bpf_u_int32 linktype;    /* data link type (LINKTYPE_*) */
    };
    
    //时间戳
    struct time_val
    {
        int tv_sec;         /* seconds 含义同 time_t 对象的值 */
        int tv_usec;        /* and microseconds */
    };
    
    //pcap数据包头结构体
    struct pcap_pkthdr
    {
        struct time_val ts;  /* time stamp */  
        bpf_u_int32 caplen; /* length of portion present */  
        bpf_u_int32 len;    /* length this packet (off wire) */ 
    };
    
    //数据帧头
    typedef struct FramHeader_t
    { //Pcap捕获的数据帧头
        u_int8 DstMAC[6]; //目的MAC地址
        u_int8 SrcMAC[6]; //源MAC地址
        u_short FrameType;    //帧类型
    } FramHeader_t;
    
    //IP数据报头
    typedef struct IPHeader_t
    { //IP数据报头
        u_int8 Ver_HLen;       //版本+报头长度
        u_int8 TOS;            //服务类型
        u_int16 TotalLen;       //总长度
        u_int16 ID; //标识
        u_int16 Flag_Segment;   //标志+片偏移
        u_int8 TTL;            //生存周期
        u_int8 Protocol;       //协议类型
        u_int16 Checksum;       //头部校验和
        u_int32 SrcIP; //源IP地址
        u_int32 DstIP; //目的IP地址
    } IPHeader_t;
    
    //TCP数据报头
    typedef struct TCPHeader_t
    { //TCP数据报头
        u_int16 SrcPort;//源端口
        u_int16 DstPort;//目的端口
        u_int32 SeqNO;//序号
        u_int32 AckNO; //确认号
        u_int8 HeaderLen; //数据报头的长度(4 bit) + 保留(4 bit)
        u_int8 Flags; //标识TCP不同的控制消息
        u_int16 Window; //窗口大小
        u_int16 Checksum; //校验和
        u_int16 UrgentPointer;  //紧急指针
    }TCPHeader_t;
    
    //UDP数据
    typedef struct UDPHeader_s
    {
        u_int16_t SrcPort;     // 源端口号16bit
        u_int16_t DstPort;    // 目的端口号16bit
        u_int16_t len;        // 数据包长度16bit
        u_int16_t checkSum;   // 校验和16bit
    }UDPHeader_t;
    
    //
    void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len); //查找 http 信息函数
    //
    int main(int argc, char ** argv)
    {
        struct pcap_file_header *file_header = NULL;
        struct pcap_pkthdr *ptk_header = NULL;
        FramHeader_t *eth_header = NULL;
        IPHeader_t *ip_header = NULL;
        TCPHeader_t *tcp_header = NULL;
        UDPHeader_t *udp_header = NULL;
        char output_name[100] = {};
        FILE *fp, *output, *tmp = NULL;
        u_int64_t   pkt_offset, i=0;
        int ip_len, http_len, ip_proto;
        int src_port, dst_port, tcp_flags;
        char buf[BUFSIZE], my_time[STRSIZE];
        char src_ip[STRSIZE], dst_ip[STRSIZE];
        char  host[STRSIZE], uri[BUFSIZE];
        
        int last_time = 0;
        
        //初始化
        file_header = (struct pcap_file_header *)malloc(sizeof(struct pcap_file_header));
        ptk_header  = (struct pcap_pkthdr *)malloc(sizeof(struct pcap_pkthdr));
        eth_header = (FramHeader_t *)malloc(sizeof(FramHeader_t));
        ip_header = (IPHeader_t *)malloc(sizeof(IPHeader_t));
        tcp_header = (TCPHeader_t *)malloc(sizeof(TCPHeader_t));
        memset(buf, 0, sizeof(buf));
    //
        if(argc != 2)
        {
            printf("./parse_cap file_name.cap
    ");
            return;
        }
        
        if((fp = fopen(argv[1],"r")) == NULL)
        {
            printf("error: can not open pcap file
    ");
            exit(0);
        }
    
        if(access("./cap_stat", F_OK) != 0)
        {
            printf("create new path
    ");
            if(mkdir("./cap_stat", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
            {
                printf("create path fail
    ");
                exit(0);
            }
        }
        //开始读数据包
        pkt_offset = 24; //pcap文件头结构 24个字节
        while(fseek(fp, pkt_offset, SEEK_SET) == 0) //遍历数据包
        {
            i++;
            memset(ptk_header, 0, sizeof(struct pcap_pkthdr));
            //pcap_pkt_header 16 byte
            if(fread(ptk_header, 16, 1, fp) != 1) //读pcap数据包头结构
            {
                printf("
    read end of pcap file
    ");
                break;
            }
        
            pkt_offset += 16 + ptk_header->caplen;   //下一个数据包的偏移值
            strftime(my_time, sizeof(my_time), "%Y-%m-%d %T", localtime((time_t *)&(ptk_header->ts.tv_sec))); //获取时间
            double time = ((u_int64_t)ptk_header->ts.tv_sec * 1000000 + ptk_header->ts.tv_usec)/1000000.000;
    //        printf("time = %.4f ,len = %d
    ", time, ptk_header->len);
    
            
            if(last_time == 0)
            {
                last_time = (int)time;
                memset(output_name, 0, 100);
                char *path = strrchr(argv[1], '/');
                if(path == NULL)
                {
                    sprintf(output_name, "./cap_stat/%s_%d", argv[1], (int)time);
                }
                else
                {
                    sprintf(output_name, "./cap_stat/%s_%d", (path+1), (int)time);
                }
    //            printf("output name = %s
    ",output_name);
                if((output = fopen(output_name,"w+")) == NULL)
                {
                    printf("error: can not open output file
    ");
                    exit(0);
                }
            }
            else if(last_time + 60 <= (int)time)
            {
                last_time = (int) time;
                memset(output_name, 0, 100);
                
                char *path = strrchr(argv[1], '/');
                if(path == NULL)
                {
                    sprintf(output_name, "./cap_stat/%s_%d", argv[1], (int)time);
                }
                else
                {
                    sprintf(output_name, "./cap_stat/%s_%d", (path+1), (int)time);
                }
    //            printf("output name = %s
    ",output_name);
    
                if((tmp = fopen(output_name, "w+")) == NULL)
                {
                    printf("error: can not open new output file
    ");
                }
                else
                {
                    fclose(output);
                    output = tmp;
                    tmp = NULL;
                }
                
            }
    
            memset(eth_header , 0, 14);
            //数据帧头 14字为ethnet协议大小,注意指定网卡抓包 不指定是16字节的linuxcooked
            if(fread(eth_header, 14, 1, fp) != 1) //读Ethernet数据
            {
                printf("
    read end of pcap file
    ");
                break;
            }
            
            if(eth_header->FrameType != 0x0008)
            {
    //            printf("Ethernet type =  %x
    ", eth_header->FrameType);
                if(eth_header->FrameType == 0x0081)
                {
                    fseek(fp, 4, SEEK_CUR); //忽略802.1Q
                }
                else
                {
                    printf("unknown frame type%x
    ",eth_header->FrameType);
                    continue;
                }
            }
            //fseek(fp, 14, SEEK_CUR); //忽略数据帧头
            //IP数据报头 20字节 不考虑>20字节
            memset(ip_header, 0, sizeof(IPHeader_t));
            if(fread(ip_header, sizeof(IPHeader_t), 1, fp) != 1)
            {
                printf("%d: can not read ip_header
    ", i);
                break;
            }
            inet_ntop(AF_INET, (void *)&(ip_header->SrcIP), src_ip, 16);
            inet_ntop(AF_INET, (void *)&(ip_header->DstIP), dst_ip, 16);
            ip_proto = ip_header->Protocol;
            ip_len = ip_header->TotalLen; //IP数据报总长度
    //        printf("%d:src=%s
    ", i, src_ip);
            
            memset(buf, 0 ,BUFSIZE);
            sprintf(buf, "%.4f,%s,%d
    ",time, src_ip, ptk_header->len);// dst_ip,
            if(fwrite(buf, strlen(buf), 1, output) != 1)
            {
                printf("output file can not write %d
    ", i);
                continue;
            }
            
    /*        if(ip_proto == 0x06) //判断是否是 TCP 协议
            {
                //TCP头 20字节
                if(fread(tcp_header, sizeof(TCPHeader_t), 1, fp) != 1)
                {
                    printf("%d: can not read tcp_header
    ", i);
                    break;
                }
                src_port = ntohs(tcp_header->SrcPort);
                dst_port = ntohs(tcp_header->DstPort);
                tcp_flags = tcp_header->Flags;
                // printf("%d:  src=%x
    ", i, tcp_flags);
                if(tcp_flags == 0x18) // (PSH, ACK) 3路握手成功后
                {
                    if(dst_port == 80) // HTTP GET请求
                    {
                        http_len = ip_len - 40; //http 报文长度
                        match_http(fp, "Host: ", "
    ", host, http_len); //查找 host 值
                        match_http(fp, "GET ", "HTTP", uri, http_len); //查找 uri 值
                        sprintf(buf, "%d:  %s  src=%s:%d  dst=%s:%d  %s%s
    ", i, my_time, src_ip, src_port, dst_ip, dst_port, host, uri);
                        //printf("%s", buf);
                        if(fwrite(buf, strlen(buf), 1, output) != 1)
                        {
                            printf("output file can not write");
                            break;
                        }
                    }
                }
            }
            else if(ip_proto == 0x11)//UDP
            {
                if(fread(udp_header, 8, 1, fp) != 1)
                {
                    printf("%d: can not read udp_header
    ", i);
                }
                src_port = ntohs(udp_header->SrcPort);
                dst_port = ntohs(udp_header->DstPort);
                printf("udp src port = %d, dst port = %d
    ", src_port, dst_port);
            }
            
            */
    
        } // end while
        fclose(fp);
        fclose(output);
        return 0;
    }
    //查找 HTTP 信息
    void match_http(FILE *fp, char *head_str, char *tail_str, char *buf, int total_len)
    {
        int i;
        int http_offset;
        int head_len, tail_len, val_len;
        char head_tmp[STRSIZE], tail_tmp[STRSIZE];
        //初始化
        memset(head_tmp, 0, sizeof(head_tmp));
        memset(tail_tmp, 0, sizeof(tail_tmp));
        head_len = strlen(head_str);
        tail_len = strlen(tail_str);
        //查找 head_str
        http_offset = ftell(fp); //记录下HTTP报文初始文件偏移
        while((head_tmp[0] = fgetc(fp)) != EOF) //逐个字节遍历
        {
            if((ftell(fp) - http_offset) > total_len) //遍历完成
            {
                sprintf(buf, "can not find %s 
    ", head_str);
                exit(0);
            }
            if(head_tmp[0] == *head_str) //匹配到第一个字符
            {
                for(i=1; i<head_len; i++) //匹配 head_str 的其他字符
                {
                    head_tmp[i]=fgetc(fp);
                    if(head_tmp[i] != *(head_str+i))
                    break;
                }
                if(i == head_len) //匹配 head_str 成功,停止遍历
                break;
            }
        }
        // printf("head_tmp=%s 
    ", head_tmp);
        //查找 tail_str
        val_len = 0;
        while((tail_tmp[0] = fgetc(fp)) != EOF) //遍历
        {
            if((ftell(fp) - http_offset) > total_len) //遍历完成
            {
                sprintf(buf, "can not find %s 
    ", tail_str);
                exit(0);
            }
            buf[val_len++] = tail_tmp[0]; //用buf 存储 value 直到查找到 tail_str
            if(tail_tmp[0] == *tail_str) //匹配到第一个字符
            {
                for(i=1; i<tail_len; i++) //匹配 head_str 的其他字符
                {
                tail_tmp[i]=fgetc(fp);
                if(tail_tmp[i] != *(tail_str+i))
                break;
                }
                if(i == tail_len) //匹配 head_str 成功,停止遍历
                {
                    buf[val_len-1] = 0; //清除多余的一个字符
                    break;
                }
            }
        }
        // printf("val=%s
    ", buf);
        fseek(fp, http_offset, SEEK_SET); //将文件指针 回到初始偏移
    }
    代码

    3、编译  gcc parse_cap.c -o parse_cap

    输入pcap名,输出文件:./cap_stat/pcap_时间(60S分文件),内容格式:时间、srcip、len

     附加:python dpdk 解析部分字段 仅供参考:

    #!/bin/python
    #-*-encoding = utf-8 -*-
    import time
    import dpkt
    import socket
    import datetime
    import os
    import sys 
    
    def run_as_daemon():
        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError, e:
            print "fork fail %s" % e
        os.setsid()
        os.umask(0)
    
    def saveRecord(ts, src, len):
        global record_fd
        try:
            record_fd.write(str(('%.3f' % ts)) + ',' +str(src) + ',' + str(len + 18) + '
    ')
        except Exception as e:
            print "write fail"
    
    def change_file():
        global record_fd
        global file_name
        global cap_name
        try:
            fd = open("./cap_stat/%s_%d" %( cap_name,file_name), "a+")
            print fd
        os.exit(0)
            if record_fd != -1:
                record_fd.close()
            record_fd = fd
        except Exception as e:
            print "open file fail" + str(e)
    
    def printPcap(pcap):
        global file_name
    
        for (ts,buf) in pcap:
        count = 0
            try:
                eth = dpkt.ethernet.Ethernet(buf)
                ip = eth.data
                try:
                    src = socket.inet_ntoa(ip.src)
                    dst = socket.inet_ntoa(ip.dst)
                except Exception as e:
                   # print "Error:" + str(e)
                    continue
    #            if str(src) == "172.17.173.237":
    #                print "%.3f,%s,%d" % ( ts,str(src),len(buf))
    #                saveRecord(ts,src,len(buf))
                if file_name == -1:
                    file_name =  int(ts)
                    change_file()
                elif file_name ==  int(ts):
                    pass
                elif int(ts)%60 == 0 or int(ts) > file_name + 60:
                    file_name = int(ts)
                    change_file()
                count += 1
                if count%1000 == 0:
                    time.sleep(0.1)
                saveRecord(ts,src,ip.len)
            except Exception as e:
                print str(e)
    
    record_fd = -1
    file_name = -1
    cap_name = -1
    
    def main():
        global cap_name
        input_name = sys.argv[1]
        cap_name = str(os.path.basename(input_name))
        print cap_name,type(cap_name)
        try:
            fd = open(input_name, "r")
        except Exception as e:
            print "open file fail"
            return
        pcap = dpkt.pcap.Reader(fd)
        printPcap(pcap)
        fd.close()
        record_fd.close()
    
    if __name__ == "__main__":
        run_as_daemon()
        main()
    View Code

  • 相关阅读:
    操作系统进程
    Lowest Common Ancestor of a Binary Search Tree
    Java并发编程实践之对象的组合
    字典序排序-求全排列(元素有重复)
    计算机网络基础知识
    多线程的基础知识
    多线程编程题
    Flask安装
    appium使用实例
    调用Excel或Oracle数据,数据加载,selenium等使用实例
  • 原文地址:https://www.cnblogs.com/wocgcow/p/8473736.html
Copyright © 2011-2022 走看看