源程序
#include
#pragma comment(lib,\#pragma comment(lib,\#include
#pragma pack(1)
struct arp_packet //arp包结构 {
unsigned char dest_mac[6]; //目标主机MAC地址 unsigned char source_mac[6]; //源端MAC地址 unsigned short eh_type; //以太网类型
unsigned short hardware_type; //硬件类型:以太网接口类型为1 unsigned short protocol_type; //协议类型:IP协议类型为0X0800 unsigned char add_len; //硬件地址长度:MAC地址长度为6B unsigned char pro_len; //协议地址长度:IP地址长度为4B unsigned short option; //操作:ARP请求为1,ARP应答为2 unsigned char sour_addr[6]; //源MAC地址:发送方的MAC地址 unsigned long sour_ip; //源IP地址:发送方的IP地址
unsigned char dest_addr[6]; //目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址 unsigned long dest_ip; //目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址
unsigned char padding[18]; };
#pragma pack()
unsigned char* BuildArpPacket(unsigned char* source_mac, unsigned char* dest_mac,unsigned long src_ip,unsigned long dest_ip);
void main() { pcap_if_t *alldevs, *d; //用于存储网络设备 int i=0; char errbuf[PCAP_ERRBUF_SIZE]; if(pcap_findalldevs(&alldevs,errbuf)==-1) { fprintf(stderr,\
6 / 37
exit(1); } for(d=alldevs;d;d=d->next) { printf(\ if(d->description) printf(\ else printf(\ } if(i==0) { printf(\找不到指定接口.\\n\ } printf(\选择设备号 (1-%d):\ int inum; pcap_t *adhandle; unsigned char *packet; scanf(\ if(inum < 1 || inum > i) { printf(\超出范围.\\n\ pcap_freealldevs(alldevs); } for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); char chs_src_ip[16] = \ char chs_dest_ip[16] = \ unsigned long ul_src_ip = 0u; unsigned long ul_dest_ip = 0u;
printf(\ scanf(\
ul_src_ip = inet_addr(chs_src_ip); printf(\ scanf(\
ul_dest_ip = inet_addr(chs_dest_ip); unsigned char uchs_src_mac[10] = {0}; unsigned char uchs_dest_mac[10] = {0};
7 / 37
printf(\ scanf(\s_src_mac[3],&uchs_src_mac[4],&uchs_src_mac[5]); printf(\ scanf(%uchs_dest_mac[3],&uchs_dest_mac[4], &uchs_dest_mac[5]);
if((adhandle= pcap_open_live(d->name,65536,1,1000,errbuf)) == NULL) { fprintf(stderr,\无法打开指定适配器.%s 不支持WinPcap\\n\ pcap_freealldevs(alldevs); } packet = BuildArpPacket(uchs_src_mac, uchs_dest_mac, ul_src_ip, ul_dest_ip); //发送arp包的函数:参数1:网络设备、参数2:arp包、参数3:长度 pcap_sendpacket(adhandle,packet,62); }
unsigned char* BuildArpPacket(unsigned char* source_mac, unsigned char* dest_mac,unsigned long src_ip,unsigned long dest_ip) { static struct arp_packet packet; memcpy(packet.dest_mac,dest_mac,6); memcpy(packet.source_mac,source_mac,6); packet.eh_type = htons(0x0806); packet.hardware_type = htons(0x01); packet.protocol_type = htons(0x0800); packet.add_len = 0x06; packet.pro_len = 0x04; packet.option = htons(0x0001); memcpy(packet.sour_addr,source_mac,6); packet.sour_ip = src_ip; packet.dest_ip = dest_ip; memcpy(packet.dest_addr,dest_mac,6); memset(packet.padding,0,18); return (unsigned char*)&packet; }
8 / 37
实验二 解析IP数据包 课程设计目的:
? 设计一个解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识。
课程设计要求:
本实验的目标是捕获网络中的IP数据包,解析数据包的内容,并将结果显示,并同时写入日志文件。 程序的具体要求如下: ? 以命令行形式运行: Ipparse
其中ipparse是程序名
? 在标准输出中显示捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
课程设计分析:
? 使用原始套接字或者winpcap,捕获IP数据包 ? 定义IP头部的数据结构 ? 解析并显示数据包
程序设计分析
网卡设置
为了获取网络中的IP数据包,必须对网卡进行编程,在这里使用套接字
(socket)进行编程。但是,在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的数据包或是以广播形式发出的数据包。对于其他形式的数据
9 / 37
包,如已到达网络接口,但却不是发送到此地址的数据包,网络接口在骓投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取与自己无关的数据包。我们要想获取网络设备的所有数据包,就是需要将网卡设置为混杂模式。
使用套接字
? 套接字分为三种,即流套接字(Stream socket)、数据报套接字(Datagram Socket)和原始套接字(Raw Socket)。要进行IP层数据包的接收和发送,应使用原始套接字。创建原始套接字的代码如下: ? Socket sock:
? Sock=wsasocket(af_inet,sock_raw,ipproto-ip,null,0,wsa-flag-overlapped):
? 本设计不用考虑超时情况。
? 创建套接后,IP头就会包含在接收数据包中。然后,我可以设置IP头操作选项,调用setsockopt函数。其中flag设置为true,并设定IP-HDRINCL选项,表明用户可以亲自对IP头进行处理。最后使用bind()函数将socket绑定到本地网卡上。绑定网卡后,需用WSAIoctl()函数把网卡设置为混杂模式,使网卡能够接收所有的网络数据。如果接收的数据包中的协议类型和定义的原始套接字匹配,那么接收的数据就拷贝到套接字中,因此,网卡就可以接收所有经过的IP包。
接收数据包
? 在程序中可使用recv()函数接收经过的IP包。该函数有四个参数,第一个参数接收操作所用的套接字描述符;第二个参数接收缓冲区的地址;第三个参数接收缓冲区的大小,也就是所要接收的字节数;第四个参数是一个附加标志,如果对所发送的数据没特殊要求,直接设为0。因为IP数
10 / 37