解析pcap抓包文件,包含pcap頭、frame頭、eth層、ip層、tcp/udp層等。 C語言+python部分解析

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

首先看下最多見的包的層次:frame層是否是實際傳輸的數據,是tcpdump添加的一層。eth層若是顯示linuxcooked的話是沒有指定網卡抓包的緣由須要tcpdump -i 指定網卡。linux

可能會有其餘協議層,自行添加便可。例以下圖2socket

                          

一、幾個結構體tcp

//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

 

二、代碼只是解析源ip 包大小以及時間輸出到文件中,其餘字段自行添加。簡單的將就是read協議長度數據到對應協議結構體中輸出便可。能夠認爲都是小端序的。ide

#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\n"); return; } if((fp = fopen(argv[1],"r")) == NULL) { printf("error: can not open pcap file\n"); exit(0); } if(access("./cap_stat", F_OK) != 0) { printf("create new path\n"); if(mkdir("./cap_stat", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { printf("create path fail\n"); 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("\nread end of pcap file\n"); 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\n", 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\n",output_name);
            if((output = fopen(output_name,"w+")) == NULL) { printf("error: can not open output file\n"); 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\n",output_name);

            if((tmp = fopen(output_name, "w+")) == NULL) { printf("error: can not open new output file\n"); } 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("\nread end of pcap file\n"); break; } if(eth_header->FrameType != 0x0008) { // printf("Ethernet type = %x\n", eth_header->FrameType);
            if(eth_header->FrameType == 0x0081) { fseek(fp, 4, SEEK_CUR); //忽略802.1Q
 } else { printf("unknown frame type%x\n",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\n", 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\n", i, src_ip);
 memset(buf, 0 ,BUFSIZE); sprintf(buf, "%.4f,%s,%d\n",time, src_ip, ptk_header->len);// dst_ip,
        if(fwrite(buf, strlen(buf), 1, output) != 1) { printf("output file can not write %d\n", 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\n", i); break; } src_port = ntohs(tcp_header->SrcPort); dst_port = ntohs(tcp_header->DstPort); tcp_flags = tcp_header->Flags; // printf("%d: src=%x\n", 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: ", "\r\n", 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\r\n", 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\n", i); } src_port = ntohs(udp_header->SrcPort); dst_port = ntohs(udp_header->DstPort); printf("udp src port = %d, dst port = %d\n", 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 \r\n", 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 \n", 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 \r\n", 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\n", buf);
    fseek(fp, http_offset, SEEK_SET); //將文件指針 回到初始偏移
}
代碼

 

三、編譯  gcc parse_cap.c -o parse_cap函數

輸入pcap名,輸出文件:./cap_stat/pcap_時間(60S分文件),內容格式:時間、srcip、lenthis

 

 附加:python dpdk 解析部分字段 僅供參考:spa

#!/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) + '\n') 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
相關文章
相關標籤/搜索