解析pcap文件并提取其中五元组
最近在学习分析pcap文件中的数据包,其中数据包的五元组(源IP,目的IP,源端口,目的端口,协议类型,共13个字节)可以用来作为数据流的标识。我们可以将五元组提取出来,以便日后进行进一步的分析。
首先,我们需要对pcap文件进行解析,然后将五元组数据提取出来并写入二进制文件。这里不再赘述,直接上代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
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;
typedef struct Quintet
{
u_int32 SrcIP; //源IP地址
u_int32 DstIP; //目的IP地址
u_int16_t SrcPort; // 源端口号16bit
u_int16_t DstPort; // 目的端口号16bit
u_int8 Protocol; //协议类型
}Quintet_t;
int main()
{
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;
Quintet_t *quintet = NULL;
//char buf[BUFSIZE];
//初始化
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));
udp_header = (UDPHeader_t *)malloc(sizeof(UDPHeader_t));
quintet = (Quintet_t *)malloc(sizeof(Quintet_t));
//memset(buf, 0, sizeof(buf));
FILE* pFile = fopen( "data.pcap", "r");
if( pFile == 0)
{
printf( "打开pcap文件失败");
return 0;
}
FILE *output = fopen("output.dat","wb");
if( output == 0)
{
printf( "打开dat文件失败");
return 0;
}
//开始读数据包
printf("开始读数据包\n");
long int pkt_offset; //用来文件偏移
pkt_offset = 24; //pcap文件头结构 24个字节
int i = 0;
while(fseek(pFile, pkt_offset, SEEK_SET) == 0) //遍历数据包
{
i++;
memset(ptk_header, 0, sizeof(struct pcap_pkthdr));
memset(quintet,0,sizeof(struct Quintet));
//pcap_pkt_header 16 byte
if(fread(ptk_header, 16, 1, pFile) != 1) //读pcap数据包头结构
{
printf("%d: can not read ptk_header\n", i);
break;
}
pkt_offset += 16 + ptk_header->caplen; //下一个数据包的偏移值
memset(eth_header , 0, 14);
//数据帧头 14字为ethnet协议大小,注意指定网卡抓包 不指定是16字节的linuxcooked
if(fread(eth_header, 14, 1, pFile) != 1) //读Ethernet数据
{
printf("%d: can not read eth_header\n", i);
break;
}
if(eth_header->FrameType != 0x0008)
{
if(eth_header->FrameType == 0x0081)
{
fseek(pFile, 4, SEEK_CUR); //忽略802.1Q
}
else
{
printf("%d: unknown frame type %x\n",i,eth_header->FrameType);
continue;
}
}
//IP数据报头 20字节 不考虑>20字节
memset(ip_header, 0, sizeof(IPHeader_t));
if(fread(ip_header, sizeof(IPHeader_t), 1, pFile) != 1)
{
printf("%d: can not read tcp_header\n", i);
break;
}
quintet->SrcIP = ip_header->SrcIP;
quintet->DstIP = ip_header->DstIP;
quintet->Protocol = ip_header->Protocol;
memset(tcp_header,0,sizeof(TCPHeader_t));
memset(udp_header,0,sizeof(UDPHeader_t));
if(ip_header->Protocol == 0x06) //判断是否是 TCP 协议
{
//TCP头 20字节
if(fread(tcp_header, sizeof(TCPHeader_t), 1, pFile) != 1)
{
printf("%d: can not read tcp_header\n", i);
break;
}
quintet->SrcPort = tcp_header->SrcPort;
quintet->DstPort = tcp_header->DstPort;
}
else if(ip_header->Protocol == 0x11)//UDP
{
if(fread(udp_header, sizeof(UDPHeader_t), 1, pFile) != 1)
{
printf("%d: can not read udp_header\n", i);
}
quintet->SrcPort = udp_header->SrcPort;
quintet->DstPort = udp_header->DstPort;
}
fwrite(quintet,13,1,output);
} // end while
fclose(pFile);
fclose(output);
printf("Finish!\n");
return 0;
}