用c语言实现某种协议数据包构造与解析
/*
@author: Isadora
@time:2021/9/20
*/
- 选择协议以及确定数据报格式
以ICMP为例:
其数据报格式如下:
wireshark抓包分析如下:
实际数据域分析:
对应代码片段:
/* 下面是ICMP协议格式的定义*/
class icmp_header
{ public:
u_int8_t icmp_type; /* ICMP类型 */
u_int8_t icmp_code; /* ICMP代码 */
u_int16_t icmp_checksum; /* 校验和 */
u_int16_t icmp_id; /* 标识符 */
u_int16_t icmp_sequence; /* 序列码 */
};
- 构造报文
确定协议各字段后用文件操作按各字段写入文件
#include "stdio.h"
#include "string.h"
int main(void)
{
char ch[4];//定义一个字符型数组
short i; //短整型
FILE *fp1,*fp2;
fp1=fopen("C:/Users/15212/Desktop/Internet/file1.txt","w");//路径自行修改
fp2=fopen("C:/Users/15212/Desktop/Internet/file2.bin","wb");//bin为二进制文件,wb表示以二进制模式访问文件
//判断文件是否打开成功
if (NULL==fp1 || NULL==fp2)
{
printf("can not create file\n");
return -1;
}
memset(ch,0,sizeof(ch));//文件写入0
strcpy(ch,"123");//复制字符串
fwrite(ch,strlen(ch),1,fp1);//写文件
i=123;
fwrite(&i,sizeof(short),1,fp2);
//fwrite(ch,strlen(ch),1,fp2);
fclose(fp1);
fclose(fp2);
return 0;
}
写字段
/*
ICMP类型:8(请求报文)
ICMP代码:0
校验和:0x4ce9
标识符:256(0x0100)
序列:29184(0x7200)
*/
icmp_type = 8;
icmp_code = 0;
icmp_checksum = 0x4ce9;//需要转十进制?可能不需要
icmp_id = 256;
icmp_sequence = 29184;
//接下来开始傻瓜式写入
fwrite(&icmp_type,sizeof(u_int8_t),1,fp1);
fwrite(&icmp_code,sizeof(u_int8_t),1,fp1);
fwrite(&icmp_checksum,sizeof(u_int16_t),1,fp1);
fwrite(&icmp_id,sizeof(u_int16_t),1,fp1);
fwrite(&icmp_sequemce,sizeof(u_int16_t),1,fp1);
wireshark考虑到window系统与Linux系统发出的ping报文(主要指ping应用字段而非包含IP头的ping包)的字节顺序不一样(windows为LE:little-endian byte order,Linux为BE:big-endian),别分告诉信息,其本质内容是没有不变的,只是表达形式不同。
- 校验和计算函数(根据情况自行修改,不是每个协议都需要校验和)
/CheckSum:计算校验和的子函数
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
- 解析报文
/* 下面是实现分析ICMP协议的函数,函数类型与回调函数相同 */
void icmp_protocol_packet_callback(u_char *argument, const pcap_pkthdr *packet_header, const u_char *packet_content)
{
class icmp_header *icmp_protocol; /* ICMP协议变量 */
icmp_protocol = (icmp_header*)(packet_content + 14+20); /* 获得ICMP协议内容 */
cout<<" ICMP协议 "<<endl;
cout<<"ICMP类型:"<<icmp_protocol->icmp_type<<endl; /* 获得ICMP类型 */
switch (icmp_protocol->icmp_type)
{
case 8:
cout<<"ICMP回显请求协议"<<endl;
cout<<"ICMP代码:"<<icmp_protocol->icmp_code<<endl;
cout<<"标识符:"<<icmp_protocol->icmp_id<<endl;
cout<<"序列码:"<<icmp_protocol->icmp_sequence<<endl;
break;
case 0:
cout<<"ICMP回显应答协议"<<endl;
cout<<"ICMP代码: "<< icmp_protocol->icmp_code<<endl;
cout<<"标识符: "<< setw(4) << setfill('0') << hex<<int(icmp_protocol->icmp_id) << endl;
cout<<"序列码: "<< icmp_protocol->icmp_sequence<<endl;
break;
default:
break;
}
cout<<"ICMP校验和:"<<setw(4) << setfill('0') << hex << ntohs(icmp_protocol->icmp_checksum) << endl; /* 获得ICMP校验和 */
return ;
}
- 主函数
/*主函数 */
void main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t *pcap_handle; /* Winpcap句柄 */
char error_content[PCAP_ERRBUF_SIZE]; /* 存储错误信息 */
//char *net_interface; /* 网络接口 */
bpf_program bpf_filter; /* BPF过滤规则 */
char bpf_filter_string[] = ""; /* 过滤规则字符串 */
bpf_u_int32 net_mask; /* 掩码 */
bpf_u_int32 net_ip; /* 网络地址 */
//net_interface = pcap_lookupdev(error_content); /* 获得可用的网络接口 */
if (pcap_findalldevs(&alldevs, error_content) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs:%s\n", error_content);
exit(1);
}
/* 打印 list */
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description) printf(" (%s)\n", d->description);
else printf(" (No description available)\n");
}
if(i==0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return;
}
printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);
if(inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
pcap_freealldevs(alldevs); /* Free the device list */
return;
}
/* 跳转到指定的网卡 */
for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
//pcap_lookupnet(d->description, &net_ip, &net_mask, error_content);/* 获得网络地址和掩码地址 */
IN_ADDR sinaddr;
pcap_addr_t *a;
for(a=d->addresses;a;a=a->next)
{
switch(a->addr->sa_family)
{
case AF_INET:
// printf("\tAddress Family Name: AF_INET\n");//打印网络地址类型
if(a->addr)//打印IP地址
{
sinaddr=((struct sockaddr_in *)a->addr)->sin_addr;
net_ip=sinaddr.S_un.S_addr;
/*printf("\tAddress:%d.%d.%d.%d\n",
sinaddr.S_un.S_un_b.s_b1,sinaddr.S_un.S_un_b.s_b2,
sinaddr.S_un.S_un_b.s_b3,sinaddr.S_un.S_un_b.s_b4);*/
}
if (a->netmask)//打印掩码
{ sinaddr=((struct sockaddr_in *)a->netmask)->sin_addr;
net_mask=sinaddr.S_un.S_addr;
// printf("\tNetmask:%s\n",inet_ntoa(sinaddr));
}
/*if (a->broadaddr)//打印广播地址
{ sinaddr=((struct sockaddr_in *)a->broadaddr)->sin_addr;
printf("\tBroadcast Address:%s\n",inet_ntoa(sinaddr));
}
if (a->dstaddr)//目的地址
{ sinaddr=((struct sockaddr_in *)a->dstaddr)->sin_addr;
printf("\tDestination Address:%s\n",inet_ntoa(sinaddr));
}*/
break;
// default:printf("\tAddress Family Name:Unknown\n"); break;
} /*end of switch*/
}
pcap_handle = pcap_open_live(d->name/*net_interface*/, 65536, 1, 1000, error_content); /* 打开网路接口 */
pcap_freealldevs(alldevs);
pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_ip); /* 编译BPF过滤规则 */
pcap_setfilter(pcap_handle, &bpf_filter); /* 设置过滤规则 */
if (pcap_datalink(pcap_handle) != DLT_EN10MB)
return ;
cout<<"请输入抓包数量:"<<endl;
int n;
cin>>n;
pcap_loop(pcap_handle, n, ethernet_protocol_packet_callback, NULL); /* 注册回调函数,循环捕获网络数据包,利用回调函数来处理每个数据包 */
pcap_close(pcap_handle); /* 关闭Winpcap操作 */
}
交互式终端:
给一个
cin>>n;
即可。
问题来了:
我选择ICMP协议,它的内层是否也需要写(以太网协议)
老师好像说过不用