(注:部分摘自”Linux C编程一站式学习“)
以太网(RFC 894)帧格式
图一 以太网数据包类型
其中的源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位,是在网卡出厂时固化的。用ifconfig命令看一下,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地址。协议字段有三种值,分别对应IP、ARP、RARP。帧末尾是CRC校验码。
以太网帧中的数据长度规定最小46字节,最大1500字节,ARP和RARP数据包的长度不够46字节,要在后面补填充位。最大值1500称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU了,则需要对数据包进行分片(fragmentation)。ifconfig命令的输出中也有“MTU:1500”。注意,MTU这个概念指数据帧中有效载荷的最大长度,不包括帧首部的长度。
在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。
每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间(一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?
图二 arp数据包类型
注意到源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型,1为以太网,协议类型指要转换的地址类型,0x0800为IP地址,后面两个地址长度对于以太网地址和IP地址分别为6和4(字节),op字段为1表示ARP请求,op字段为2表示ARP应答。
有了上面的基础知识,下面我们就来实战编程:
1、确定socket的参数
根据图一,我们可以看出arp,rarp和ip虽然同属于网络层(又名IP层),但是他们的数据包装是独立的。虽然icmp和igmp也处在IP层,但是它们又需要ip数据报的包装。所以我们在为arp和rarp建立socket的时候,就不能利用ip的原始数据报(SOCK_RAW)了,我们需要最原始的以太网帧的数包(SOCK_PACKET);在对于网络类型的选择上,可以根据需要选择IPv4(AF_INET)或IPv6(AF_INET6);arp的协议类型跟以太网帧数据类型一样,所以应该是0x0806,在linux的”Ethernet
Protocol ID“中定义了
#define ETH_P_ARP 0x0806
那么,我们就可以简单的以为socket套接字的创建是:sfd = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP));
2、确定ARP数据包的结构
根据图二,确定如下结构体
struct arp_packet { //以太网首部 unsigned char ap_dstmac[6]; //6字节 unsigned char ap_srcmac[6]; //6字节 unsigned short ap_frame; //2字节 //arp unsigned short ap_hwtype; //2字节:硬件地址类型 unsigned short ap_prototype; //2字节:软件地址类型 unsigned char ap_hwlen; //1字节:硬件地址长度 unsigned char ap_prolen; //1字节:软件地址长度 unsigned short ap_op; //2字节:操作类型 unsigned char ap_frommac[6];//6字节 unsigned char ap_fromip[4]; //4字节 unsigned char ap_tomac[6]; //6字节 unsigned char ap_toip[4]; //4字节 //18字节:填充字节,因为以太网数据最少要46字节 unsigned char ap_padding[18]; };
3、获取本地ip地址和mac地址
linux里面提供了mac地址和ip地址获取的ioctl参数;
#define SIOCGIFADDR 0x8915 /* get PA address */ #define SIOCSIFADDR 0x8916 /* set PA address */ #define SIOCSIFHWADDR 0x8924 /* set hardware address */ #define SIOCGIFHWADDR 0x8927 /* Get hardware address */
在获取本机mac地址和ip地址之前,我们需要告诉ioctl我们要获取那个网卡的参数:
struct ifreq eth; strcpy(eth.ifr_name,"eth0"); /* Get eth0 Hardware Address */ int ret = ioctl(fds,SIOCGIFHWADDR, ð); if(ret < 0){ perror("Get Hardware Address Fail:"); goto close_socket; } /* Get eth0 IP Address */ ret = ioctl(fds, SIOCGIFADDR, ð); if(ret < 0){ perror("Get IP Address Fail:"); goto close_socket; } unsigned char mac_addr[6]; memcpy(mac_addr,eth.ifr_hwaddr.sa_data,6); /* In "struct sockaddr",ip address starts from 'sa_data' two bytes later*/ unsigned char ip_addr[4]; memcpy(ip_addr,eth.ifr_addr.sa_data+2,4);
4、数据包数据填写
struct arp_packet arp_in; bzero(&arp_in,sizeof(struct arp_packet)); unsigned char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; memcpy(arp_in.ap_dstmac,broadcast_mac,6); memcpy(arp_in.ap_srcmac,mac_addr,6); arp_in.ap_frame = htons(ETH_P_ARP); arp_in.ap_hwtype = htons(0x0001); arp_in.ap_prototype = htons(ETH_P_IP); arp_in.ap_hwlen = 6; arp_in.ap_prolen = 4; arp_in.ap_op = htons(0x0001);//0x0001-ARP req 0x0002-ARP Reply memcpy(arp_in.ap_frommac,mac_addr,6); memcpy(arp_in.ap_fromip,ip_addr,4);
5、数据与接收
由于在这种情况下,对方的mac地址都是未知的,而且数据的发送也是作为广播模式发送。
所以这个时候我们只需告诉底层我们需要用那个网卡发送就可以了。
struct sockaddr eth; eth.sa_family = AF_INET; strcpy(eth.sa_data,"eth0"); ret = sendto(fds,&arp_in,sizeof(struct arp_packet),0, (struct sockaddr *)ð,sizeof(struct sockaddr)); if(ret < 0){ perror("Send Reqire ARP Packet Fail:"); goto close_socket; } struct arp_packet arp_rc; socklen_t slen = sizeof(struct sockaddr); bzero(&arp_rc,sizeof(struct arp_packet)); ret = recvfrom(fds,&arp_rc,sizeof(struct arp_packet),0, (struct sockaddr *)ð,&slen); if(ret < 0){ perror("Receive Replay ARP Packet Fail:"); goto close_socket; } .... close_socket; close(fds); return (ret > 0 ? 1 : ret);
6、问题到此结束,测试后的结果如下:
-----------------------------Sendto---------------------------- Dest MAC:FF:FF:FF:FF:FF:FF SRC MAC:00:22:15:67:F8:16 HW TYPE:0806 From :210.42.158.204 To :210.42.158.212 ARP OP:0100 -----------------------------recvfrom------------------------- Dest MAC:00:22:15:67:F8:16 SRC MAC:00:E0:4C:DC:AA:1E HW TYPE:0806 From :210.42.158.212 To :210.42.158.204 ARP OP:0200
至于ARP攻击,我小测试了一把,还是可以的。但是许多电脑有arp防护,或者防火墙,arp攻击时没有用的。
为了社会的安宁,这里我就不把arp攻击的代码贴出来了。
如果有什么问题,请各位留言指点!谢谢!