之前的几篇文章,分别介绍了 ARP 协议格式,在vs2012里配置winpcap环境,我们该做的准备都已经做完了。如今我们真正来实现了。
一、定义数据结构
依据ARP的协议格式,设计一个ARP协议格式
依据ARP的分组格式,我们知道它有两部分组成:
1、以太网首部,这是数据包在数据链路层上传输所必不可缺的部分。它的后面跟着相关的协议数据包(ARP/IP等)
2、ARP数据包
所以。我们有这么几个数据结构:
// 以太网的首部 typedef struct EthHead { u_char dest_mac[6]; // 以太网的目的地址 u_char src_mac[6]; // 以太网的源地址 u_char type[2]; // 帧类型 ARP:0x0806 }EthHead;
// ARP数据包 typedef struct ArpMsg { u_char mac_type[2]; // 硬件类型 以太网: 1 u_char protocal_type[2]; // 协议类型 IP地址:0x0800 u_char mac_len; // 硬件地址长度 6 u_char protocal_len; // 协议地址长度 4 u_char op[2]; // 操作字段 ARP请求:1 ARP应答:2 RARP请求:3 RARP应答:4 u_char sender_mac[6]; // 发送端以太网地址 u_char sender_ip[4]; // 发送端IP地址 u_char target_mac[6]; // 目的以太网地址 u_char target_ip[4]; // 目的IP地址 }ArpMsg;
// 以太网ARP typedef struct Arp { EthHead eth_head; ArpMsg arpmsg; }Arp;
每一个数据结构都有凝视说明
注:
【1】因为网络传输数据都是大端模式,所以在设计这些数据结构,以及在封装数据的时候,注意要把本地序转为网络序
【2】为了方便,我把全部的字段类型都设为u_char(unsigned char)型。在封装数据的时候,仅仅要按字节赋值。就不须要在进行本地序和网络序的转换了(假设是多字节存储,如short、int等。切记要进行转换)
二、组包
/* 函数名: PacketArp 功 能: 封装ARP包 參 数: arp_req : Arp类型,出參 op : 操作字段 ARP请求:1 ARP响应:2 RARP请求:3 RARP对应:4 dest_mac : 以太网目的地址 src_mac : 以太网源地址 sender_mac:发送端以太网地址 sender_ip : 发送端IP地址 target_mac: 目的以太网地址 target_ip : 目的IP地址 返 回: 0正确,-1错误 */ int PacketArp(Arp *arp_req, u_char op, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { if(arp_req == NULL) return -1; memcpy(arp_req->eth_head.dest_mac, dest_mac, 6); memcpy(arp_req->eth_head.src_mac, src_mac, 6); arp_req->eth_head.type[0] = 0x08; arp_req->eth_head.type[1] = 0x06; //arp_req->eth_head.type = htons(arp_req->eth_head.type); arp_req->arpmsg.mac_type[0] = 0x00; arp_req->arpmsg.mac_type[1] = 0x01; arp_req->arpmsg.protocal_type[0] = 0x08; arp_req->arpmsg.protocal_type[1] = 0x00; arp_req->arpmsg.mac_len = 0x06; arp_req->arpmsg.protocal_len = 0x04; arp_req->arpmsg.op[0] = 0x00; arp_req->arpmsg.op[1] = op; memcpy(arp_req->arpmsg.sender_mac, sender_mac, 6); memcpy(arp_req->arpmsg.sender_ip, sender_ip, 4); memcpy(arp_req->arpmsg.target_mac, target_mac, 6); memcpy(arp_req->arpmsg.target_ip, target_ip, 4); return 0; } // ARP 请求包 int PacketArpRequest(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], <span style="white-space:pre"> </span> u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { <span style="white-space:pre"> </span>return PacketArp(arp_req,0x01,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip); } // ARP 响应包 int PacketArpReplay(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], <span style="white-space:pre"> </span> u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { <span style="white-space:pre"> </span>return PacketArp(arp_req,0x02,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip); }
三、发包
/* 功能: src_mac 向局域网广播:target_ip 192.168.1.111 的 mac 是多少 */ int Text1(Arp *arp) { u_char dest_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};// 广播 u_char src_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85};// 源mac地址 F0-7B-CB-A3-15-85 u_char sender_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85}; // 发送端mac F0-7B-CB-A3-15-85 u_char sender_ip[4] = {0xC0,0xA8,0x01,0x65}; //发送端IP地址 192.168.1.101 u_char target_mac[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; // 由于不知道mac,所以mac为空 u_char target_ip[4] = {0xC0,0xA8,0x01,0x6F}; // 目的端ip 192.168.1.111; if(PacketArpRequest(arp,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip) == -1) { printf("Packet arp request error "); return -1; } return 0; }
Arp arp; if(Text1(&arp) == -1) { return ; } memcpy(packet, (void*)&arp, ARP_LEN); /* Send down the packet */ if (pcap_sendpacket(fp, packet, ARP_LEN /* size */) != 0) { fprintf(stderr," Error sending the packet: %s ", pcap_geterr(fp)); return; }
注:pcap_sendpacket 是winpcap里面的发包函数,此函数不支持linux
至此,我们的ARP组包、发包就完毕了,就是这么简单(winpcap的功劳),以下我们用wireshark来抓包,看我们的包有没有发送出去。
从抓包来看,我们的包已经正确发出去了(192.168.1.111是我本机的ip),并且从还能够看出,已经有回包了。回包的详细内容:
我们再来对照下请求包和响应包的差别:
不言而喻吧
同一时候。我们再看下我们本地的ARP缓存:
192.168.1.111这台机器的IP和MAC映射也就增加到ARP快速缓存了。
附源代码:
#include <stdlib.h> #include <stdio.h> // pcap_findalldevs_ex #define HAVE_REMOTE #include <pcap.h> #define ARP_LEN 42 // 以太网的首部 typedef struct EthHead { u_char dest_mac[6]; // 以太网的目的地址 u_char src_mac[6]; // 以太网的源地址 u_char type[2]; // 帧类型 ARP:0x0806 }EthHead; // ARP数据包 typedef struct ArpMsg { u_char mac_type[2]; // 硬件类型 以太网: 1 u_char protocal_type[2]; // 协议类型 IP地址:0x0800 u_char mac_len; // 硬件地址长度 6 u_char protocal_len; // 协议地址长度 4 u_char op[2]; // 操作字段 ARP请求:1 ARP应答:2 RARP请求:3 RARP应答:4 u_char sender_mac[6]; // 发送端以太网地址 u_char sender_ip[4]; // 发送端IP地址 u_char target_mac[6]; // 目的以太网地址 u_char target_ip[4]; // 目的IP地址 }ArpMsg; // 以太网ARP typedef struct Arp { EthHead eth_head; ArpMsg arpmsg; }Arp; /* 函数名: PacketArp 功 能: 封装ARP包 參 数: arp_req : Arp类型,出參 op : 操作字段 ARP请求:1 ARP响应:2 RARP请求:3 RARP对应:4 dest_mac : 以太网目的地址 src_mac : 以太网源地址 sender_mac:发送端以太网地址 sender_ip : 发送端IP地址 target_mac: 目的以太网地址 target_ip : 目的IP地址 返 回: 0正确。-1错误 */ int PacketArp(Arp *arp_req, u_char op, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { if(arp_req == NULL) return -1; memcpy(arp_req->eth_head.dest_mac, dest_mac, 6); memcpy(arp_req->eth_head.src_mac, src_mac, 6); arp_req->eth_head.type[0] = 0x08; arp_req->eth_head.type[1] = 0x06; //arp_req->eth_head.type = htons(arp_req->eth_head.type); arp_req->arpmsg.mac_type[0] = 0x00; arp_req->arpmsg.mac_type[1] = 0x01; arp_req->arpmsg.protocal_type[0] = 0x08; arp_req->arpmsg.protocal_type[1] = 0x00; arp_req->arpmsg.mac_len = 0x06; arp_req->arpmsg.protocal_len = 0x04; arp_req->arpmsg.op[0] = 0x00; arp_req->arpmsg.op[1] = op; memcpy(arp_req->arpmsg.sender_mac, sender_mac, 6); memcpy(arp_req->arpmsg.sender_ip, sender_ip, 4); memcpy(arp_req->arpmsg.target_mac, target_mac, 6); memcpy(arp_req->arpmsg.target_ip, target_ip, 4); return 0; } // ARP 请求包 int PacketArpRequest(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { return PacketArp(arp_req,0x01,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip); } // ARP 响应包 int PacketArpReplay(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6], u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4]) { return PacketArp(arp_req,0x02,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip); } // 打开网络适配器 pcap_if_t* choose_interface() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; char errbuf[PCAP_ERRBUF_SIZE]; /* Retrieve the device list on the local machine */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %s ", errbuf); exit(1); } /* Print the list */ for(d=alldevs; d; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s) ", d->description); else printf(" (No description available) "); } if(i==0) { printf(" No interfaces found! Make sure WinPcap is installed. "); return NULL; } printf("Enter the interface number (1-%d):",i); scanf_s("%d", &inum); if(inum < 1 || inum > i) { printf(" Interface number out of range. "); /* Free the device list */ pcap_freealldevs(alldevs); return NULL; } /* Jump to the selected adapter */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); return d; } /* 功能: src_mac 向局域网广播:target_ip 192.168.1.111 的 mac 是多少 */ int Text1(Arp *arp) { u_char dest_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};// 广播 u_char src_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85};// 源mac地址 F0-7B-CB-A3-15-85 u_char sender_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85}; // 发送端mac F0-7B-CB-A3-15-85 u_char sender_ip[4] = {0xC0,0xA8,0x01,0x65}; //发送端IP地址 192.168.1.101 u_char target_mac[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; // 由于不知道mac,所以mac为空 u_char target_ip[4] = {0xC0,0xA8,0x01,0x6F}; // 目的端ip 192.168.1.111; if(PacketArpRequest(arp,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip) == -1) { printf("Packet arp request error "); return -1; } return 0; } void main(int argc, char **argv) { pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; u_char packet[ARP_LEN]; int i; pcap_if_t *d = choose_interface(); if(d == NULL) { exit(1); } char *source = d->name; /* Open the output device */ if ( (fp= pcap_open(source, // name of the device 100, // portion of the packet to capture (only the first 100 bytes) PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode 1000, // read timeout NULL, // authentication on the remote machine errbuf // error buffer ) ) == NULL) { fprintf(stderr," Unable to open the adapter. %s is not supported by WinPcap ", argv[1]); return; } Arp arp; if(Text1(&arp) == -1) { return ; } memcpy(packet, (void*)&arp, ARP_LEN); /* Send down the packet */ if (pcap_sendpacket(fp, packet, ARP_LEN /* size */) != 0) { fprintf(stderr," Error sending the packet: %s ", pcap_geterr(fp)); return; } return; }