目录
设计要求
设计过程
1)定义pcap文件头结构体,时间戳结构体,pcap数据包头结构体
2)数据链路层结构体定义
3)IP数据包头结构体定义
4)UDP数据包头结构体定义
5)DHCP数据包结构体定义
6)计算校验和字段
程序源码
设计要求
用C语言编写程序,针对wireshark抓获的DHCP数据包(pcap文件),要求按照数据链路层、网络层、传输层、应用层等顺序显示各主要字段的信息。并计算出传输层(UDP)的校验和字段且显示。要求程序能够通过交互方式输入pcap文件的路径。
需要满足该要求,我们就必须需要了解pcap文件是什么,也要了解DHCP协议是讲什么的:
通过上面两篇博客深刻的了解pcap文件和DHCP数据包的详细内容,才更好的理解该篇博客的编程思想,一步步地深入学习,更好地掌握这个知识点,结尾附有源码分享。
设计过程
//给数据类型起别名,方便我们使用
typedef unsigned char u_int8;
typedef unsigned short u_int16;
typedef unsigned int u_int32;
typedef unsigned long long u_int64;
1)定义pcap文件头结构体,时间戳结构体,pcap数据包头结构体
//pacp文件头结构体
struct pcap_file_header
{
u_int32 magic; //识别文件和字节顺序:小端/大端模式
u_int16 version_major; //主版本号
u_int16 version_minor; //次版本号
u_int32 thiszone; //当地的标准时间
u_int32 sigfigs; //时间戳精度
u_int32 snaplen; //最大的存储长度
u_int32 linktype; //链路类型
};
//时间戳
struct time_val
{
u_int32 tv_sec; //时间戳高位,精确到seconds
u_int32 tv_usec; //时间戳地位,精确到microseconds
};
//pcap数据包头结构体
struct pcap_pkthdr
{
struct time_val ts; //捕获时间
u_int32 caplen; //数据帧/区的长度
u_int32 len; //离线数据长度
};
2)数据链路层结构体定义
//数据链路层数据
typedef struct DL_Header
{
u_int8 DstMAC[6]; //源MAC地址
u_int8 SrcMAC[6]; //目的MAC地址
u_int8 Type[2]; //帧类型
}DL_Header;
3)IP数据包头结构体定义
//IP数据报头
typedef struct IP_Header
{
u_int8 Ver_HLen; //版本+报头长度
u_int8 TOS; //服务类型
u_int8 TotalLen[2]; //总长度
u_int8 ID[2]; //标识
u_int8 Flag; //标志
u_int8 Flag_Segment; //片偏移
u_int8 TTL; //生存周期
u_int8 Protocol; //协议类型
u_int8 Checksum[2]; //头部校验和
u_int8 SrcIP[4]; //源IP地址
u_int8 DstIP[4]; //目的IP地址
} IP_Header;
4)UDP数据包头结构体定义
//UDP数据报头
typedef struct UDP_Header
{
u_int8 SrcPort[2]; //源端口
u_int8 DstPort[2]; //目的端口
u_int8 Length[2]; //数据长度
u_int8 Checksum[2]; //校验和
}UDP_Header;
5)DHCP数据包结构体定义
//DHCP数据
typedef struct DHCP
{
u_int8 op;
u_int8 htype;
u_int8 hlen;
u_int8 hops;
u_int8 xid[4];
u_int16 secs;
u_int16 flags;
u_int8 ciaddr[4];
u_int8 yiaddr[4];
u_int8 siaddr[4];
u_int8 giaddr[4];
u_int8 chaddr[16];
u_int8 sname[64];
u_int8 file[128];
u_int8 mag[4];
u_int8 option1[3];
u_int8 option2[6];
u_int8 option3[9];
u_int8 option4;
}Dhcp;
6)计算校验和字段
UDP计算校验和的方法和IP数据报首部校验和的方法相似。不同的是:IP数据报校验和只校验IP数据报的首部,但UDP的校验和是把首部和数据部分一起都检验。
UDP的校验和需要计算UDP首部加数据荷载部分,但也需要加上UDP伪首部。这个伪首部指,源地址、目的地址、UDP数据长度、协议类型(0x11),协议类型就一个字节,但需要补一个字节的0x0,构成12个字节。伪首部+UDP首部+数据一起计算校验和。
UDP检验和的计算方法是:
- 按每16位求和得出一个32位的数;
- 如果这个32位的数,高16位不为0,则高16位加低16位再得到一个32位的数;
- 重复第2步直到高16位为0,将低16位取反,得到校验和。
unsigned short checksum(unsigned short* buf, int nword)
{
unsigned long sum;
for (sum = 0; nword > 0; nword--)
{
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
}
return ~sum;
}
程序源码
int main()
{
char my_time[1000] = { 0 };
char file_name[1000] = { 0 };
struct pcap_file_header* file_header = (struct pcap_file_header*)malloc(sizeof(struct pcap_file_header));
struct pcap_pkthdr* ptk_header = (struct pcap_pkthdr*)malloc(sizeof(struct pcap_pkthdr));
DL_Header* dl_header = (struct DL_Header*)malloc(sizeof(struct DL_Header));
IP_Header* ip_header = (IP_Header*)malloc(sizeof(IP_Header));
UDP_Header* udp_header = (UDP_Header*)malloc(sizeof(UDP_Header));
Dhcp* dhcp = (Dhcp*)malloc(sizeof(Dhcp));
printf("请输入你要抓取的文件名字:");
scanf("%s", file_name);
FILE* fp = fopen(file_name, "rb");
if (fp == NULL)
{
printf("打开pcap文件失败,退出!!");
exit(0);
}
else
{
fread(file_header, sizeof(struct pcap_file_header), 1, fp);
fread(ptk_header, sizeof(struct pcap_pkthdr), 1, fp);
fread(dl_header, sizeof(struct DL_Header), 1, fp);
fread(ip_header, sizeof(struct IP_Header), 1, fp);
fread(udp_header, sizeof(UDP_Header), 1, fp);
fread(dhcp, sizeof(Dhcp), 1, fp);
}
fclose(fp);
struct tm* timeinfo;
time_t t = (time_t)(ptk_header->ts.tv_sec);
time(&t);
timeinfo = localtime(&t);
strftime(my_time, sizeof(my_time), "%Y-%m-%d %H:%M:%S", timeinfo);
printf("Frame 1 : % d bytes on wire(% d bits), % d bytes captured(% d bites)\n", ptk_header->caplen, ptk_header->caplen * 8, ptk_header->caplen, ptk_header->caplen * 8);
printf("Arrival Time:%s\n",my_time);
printf("Epoch Time: %d.%d\n", ptk_header->ts.tv_sec, ptk_header->ts.tv_usec);
printf("Frame Number: 1\n");
printf("Frame Length : %d bytes on wire(% d bits)\n", ptk_header->caplen, ptk_header->caplen * 8);
printf("Frame 1 : %d bytes on wire(% d bits)\n", ptk_header->caplen, ptk_header->caplen * 8);
printf("Capture Length: %d bytes (%d bites)\n", ptk_header->caplen, ptk_header->caplen * 8);
printf("\n");
printf("Ethernet II, Src :InterCor (%02x:%02x:%02x:%02x:%02x:%02x)", dl_header->SrcMAC[0], dl_header->SrcMAC[1], dl_header->SrcMAC[2], dl_header->SrcMAC[3], dl_header->SrcMAC[4], dl_header->SrcMAC[5]);
printf("Dst: %02x:%02x:%02x:%02x:%02x:%02x\n", dl_header->DstMAC[0], dl_header->DstMAC[1], dl_header->DstMAC[2], dl_header->DstMAC[3], dl_header->DstMAC[4], dl_header->DstMAC[5]);
printf("Destination: %02x:%02x:%02x:%02x:%02x:%02x\n", dl_header->DstMAC[0], dl_header->DstMAC[1], dl_header->DstMAC[2], dl_header->DstMAC[3], dl_header->DstMAC[4], dl_header->DstMAC[5]);
printf("Source: %02x:%02x:%02x:%02x:%02x:%02x\n", dl_header->SrcMAC[0], dl_header->SrcMAC[1], dl_header->SrcMAC[2], dl_header->SrcMAC[3], dl_header->SrcMAC[4], dl_header->SrcMAC[5]);
printf("Type: IPv4 0x%02x%02x\n", dl_header->Type[0], dl_header->Type[1]);
printf("\n");
printf("Internet Protocol Version 4, Src:%d.%d.%d.%d ", ip_header->SrcIP[0], ip_header->SrcIP[1], ip_header->SrcIP[2], ip_header->SrcIP[3]);
printf("Dst:%d.%d.%d.%d\n", ip_header->DstIP[0], ip_header->DstIP[1], ip_header->DstIP[2], ip_header->DstIP[3]);
printf("version: 4\n");
printf("Header length: 20 bytes\n");
printf("Diffenentiated Services Filed: 0x%02x\n", ip_header->TOS);
printf("Total Length: %d\n", ip_header->TotalLen[0] * 16 * 16 + ip_header->TotalLen[1]);
printf("Identination: 0x%02x%02x\n", ip_header->ID[0], ip_header->ID[1]);
printf("Flag: 0x%04x\n", ip_header->Flag);
printf("Flagment offset:%d\n", ip_header->Flag_Segment);
printf("Time to live:%d\n", ip_header->TTL);
if (ip_header->Protocol == 6)
{
printf("Protocol:TCP(%d)\n", ip_header->Protocol);
}
else
{
printf("Protocol: UDP(%d)\n", ip_header->Protocol);
}
printf("Header checksum:0x%2x%2x\n", ip_header->Checksum[0], ip_header->Checksum[1]);
printf("Source Address: %d.%d.%d.%d\n", ip_header->SrcIP[0], ip_header->SrcIP[1], ip_header->SrcIP[2], ip_header->SrcIP[3]);
printf("Destination Address: %d.%d.%d.%d\n", ip_header->DstIP[0], ip_header->DstIP[1], ip_header->DstIP[2], ip_header->DstIP[3]);
printf("\n");
printf("Source Port:%d\n", udp_header->SrcPort[0] + udp_header->SrcPort[1]);
printf("DstPort Port:%d\n", udp_header->DstPort[0] + udp_header->DstPort[1]);
printf("Lengyh:%d\n", udp_header->Length[0] * 16 * 16 + udp_header->Length[1]);
printf("Checksum:0x%2x%2x\n", udp_header->Checksum[0], udp_header->Checksum[1]);
printf("\n");
printf("Message type:Boot Requeest(%d)\n", dhcp->op);
printf("Hardware type:Ethernet(0x%02x)\n", dhcp->htype);
printf("Hardware address length: %d\n", dhcp->hlen);
printf("Hops: %d\n", dhcp->hops);
printf("Transaction ID:0x%02x%02x%02x%02x\n", dhcp->xid[0], dhcp->xid[1], dhcp->xid[2], dhcp->xid[3]);
printf("Second elapsed : %d\n", dhcp->secs);
printf("Bootp flag:0x%04x\n", dhcp->flags);
printf("Client IP address:%d.%d.%d.%d\n", dhcp->ciaddr[0], dhcp->ciaddr[1], dhcp->ciaddr[2], dhcp->ciaddr[3]);
printf("Your(client) IP address:%d.%d.%d.%d\n", dhcp->yiaddr[0], dhcp->yiaddr[1], dhcp->yiaddr[2], dhcp->yiaddr[2]);
printf("Next server IP address:%d.%d.%d.%d\n", dhcp->siaddr[0], dhcp->siaddr[1], dhcp->siaddr[2], dhcp->siaddr[3]);
printf("Relay agent IP address:%d.%d.%d.%d\n", dhcp->giaddr[0], dhcp->giaddr[1], dhcp->giaddr[2], dhcp->giaddr[3]);
printf("Client MAC address: chongqin_8f:27:5d(%2x:%2x:%2x:%2x:%2x:%2x)\n", dhcp->chaddr[0], dhcp->chaddr[1], dhcp->chaddr[2], dhcp->chaddr[3],dhcp->chaddr[4],dhcp->chaddr[5]);
printf("Magic cookie:DHCP(0x%2x%2x 0x%2x%2x)\n", dhcp->mag[0], dhcp->mag[1], dhcp->mag[2], dhcp->mag[3]);
printf("Option:(%d) DHCP Message Type\n", dhcp->option1[0]);
printf("Length:%d\n", dhcp->option1[1]);
printf("DHCP:(%d)\n", dhcp->option1[2]);
printf("Option:(%d)\n", dhcp->option2[0]);
printf("Length:%d\n", dhcp->option2[1]);
printf("DHCP Server Identifer:%d.%d.%d.%d\n", dhcp->option2[2], dhcp->option2[3], dhcp->option2[4], dhcp->option2[5]);
printf("Option:(%d)\n", dhcp->option3[0]);
printf("Length:%d\n", dhcp->option3[1]);
printf("Hardware type:Ethernrt(0x%02x)\n", dhcp->option3[2]);
printf("CLient MAC address:chonqin_8f:27:5d(%2x:%2x:%2x:%2x:%2x:%2x)\n", dhcp->option3[3], dhcp->option3[4], dhcp->option3[5], dhcp->option3[6], dhcp->option3[7], dhcp->option3[8]);
printf("Option:(%d)\n", dhcp->option4);
printf("\n");
unsigned short buffer[] = { 0x6402,0xdfc4,0x6402,0xfffe,0x0011,0x0044,0x0043,0x0134,0x0101,0x0600,
0xbc05,0x9194,0x6402,0xdfc4,0x1cbf,0xc08f,0x275d,0x6382,0x5363,0x3501,
0x0736,0x0464,0x02ff,0xfe3d,0x0701,0x1cbf,0xc08f,0x275d,0xff00,0x0134 }; //pcap报文直接抓取非零数字,两次校验和,一次0x0011
int n = sizeof(buffer)/sizeof(buffer[0]);
unsigned short re_checksum = 0;
re_checksum = checksum(buffer, n);
printf("%x\n", re_checksum);
if (re_checksum == 0xb4bc)
{
printf("校验和正确!\n");
}
else
{
printf("校验和不正确!\n");
}
return 0;
}