【网络协议】ARP协议

ARP 概述

       在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的 MAC 地址(硬件地址)。而在 TCP/IP 协议中,网络层和传输层只关心目标主机的 IP 地址。这就导致在以太网中使用 IP 协议时,数据链路层的以太网协议接到上层 IP 协议提供的数据中,只包含目的主机的 IP 地址。 ARP 是一种地址解析协议,是一个位于 TCP/IP 协议栈中低层的协议,主要是负责将 IP 地址解析为对应的 MAC 地址。ARP 协议只适用于局域网。另外,当发送主机和目的主机不在同一个局域网中时,即便知道目的主机的 MAC 地址,两者也不能直接通信,必须经过路由转发才可以。所以此时,发送主机通过 ARP 协议获得的将不是目的主机的真实 MAC 地址,而是一台可以通往局域网外的路由器的 MAC 地址。于是此后发送主机发往目的主机的所有帧,都将发往该路由器,通过它向外发送。这种情况称为 ARP 代理。


ARP 报文格式

        报文格式如下图所示:其中操作类型:代表 ARP 数据包类型;0 表示 ARP 请求数据包,1 表示 ARP 应答数据包。




ARP 工作过程

        局域网中每台主机都有一个 ARP 高速缓存,这个缓存存放的是最近发起的 IP 地址到 MAC 地址的映射记录。ARP 高速缓存中的表项一般都要设置超时值,如果一段时间内没有与某主机通信,就将该主机对应的 IP 与 MAC 之间的映射关系去掉,下次在需要通信时,依然发送广播。

       ARP的基本工作原理如下:

  1. 每台主机都会根据以往在网络中与其他主机的通信,在自己的 ARP 缓存区(ARP Cache)中建立一个 ARP 列表,以表示网络中主机 IP 地址和 MAC 地址的对应关系。
  2. 当源主机需要将一个数据包发送到目标主机时,会首先检查自己 ARP 列表中是否存在该包中所包含的目标主机 IP 地址对应的 MAC 地址。如果有,则直接将数据包发送到这个 MAC 地址主机上;如果没有,就向本地网段(局域网)发起一个 ARP 请求的广播包,查询此 IP 地址目标主机对应的 MAC 地址。此 ARP 请求数据包里包括源主机的 IP 地址、硬件地址,以及目标主机的 IP 地址。
  3. 网络中所有的主机在收到这个 ARP 请求后,会检查数据包中的目标 IP 地址是否和自己的 IP 地址一致。如果不相同就忽略此数据包;如果相同,该主机首先将源端的MAC 地址和 IP 地址的对应表项添加到自己的 ARP 列表中。如果发现 ARP 表中已经存在该 IP 地址所对应的 MAC 地址表项信息,则将其覆盖,然后给源主机发送一个ARP 响应数据包,告诉对方自己是它需要查找的 MAC 地址主机。
  4. 源主机在收到这个 ARP 响应数据包后,将得到的目标主机的 IP 地址和 MAC 地址对应表项添加到自己的 ARP 列表中,并利用此信息开始数据的传输。如果源主机一直没有收到 ARP 响应数据包,则表示 ARP 查询失败。
下面是基于winpcap库的模拟发送ARP包的例子,通过抓包工具可以看到成功发送了ARP包和收到对方的ARP回应。但程序中未能读取到arp包。
// ArpAttack.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "pcap.h"
#include "packet32.h"

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"packet.lib")

#define EPT_ARP 0x0806                //定义了一些在构造包的时候要用到的常量
#define EPT_IP 0x0800
#define ARP_HARDWARE 0X0001
#define ARP_REPLY 0x0002
#define ARP_REQUEST 0x0001

#pragma pack(push,1)                //在定义结构的时候一顶要用到pack(push,1)和下面的pack(pop)
                                    //否则你构造的结构的长度会有问题

//以太网头部,长度14
typedef struct ethhdr               
{
  unsigned char dst[6];       //目的的MAC地址
  unsigned char src[6];       //源的MAC地址
  unsigned short type;        //帧类型
}ETHHDR,*PETHDHR;

//arp协议头,长度28
typedef struct eth_arphdr           
{
  unsigned short arp_hrd;     //硬件类型
  unsigned short arp_pro;     //协议类型
  unsigned char  arp_hln;     //硬件地址长度(6)
  unsigned char  arp_pln;     //协议地址长度(4)
  unsigned short arp_op;      //回应还是请求
 
  unsigned char arp_sha[6];   //发送者MAC地址
  unsigned long arp_spa;      //发送者IP
  unsigned char arp_tha[6];   //接收者MAC地址
  unsigned long arp_tpa;      //接收者IP
}ETH_ARPHDR,*PETH_ARPHDR;

//ARP包结构,以太网协议头+ARP协议头
typedef struct arp
{
  ETHHDR	ethhdr;
  ETH_ARPHDR eth_arp;
}ARP,*PARP;

#pragma pack(pop)

#define Max_Num_Adapter 10

char        AdapterList[Max_Num_Adapter][1024];  //定义的网络适配器列表

int main (int argc,char* argv[])
{
	LPADAPTER  lpAdapter = 0;               
	LPPACKET   lpPacket,lpPacketRecv;
	int        i;
	DWORD      dwErrorCode;
	char     AdapterName[8192]; 
	char     *temp,*temp1;                 //将AdapterNames的内容转存到AdapterList时用
	int     AdapterNum=0;
	ULONG     AdapterLength;
	ARP arpPacket;                           //定义的包结构实例
	char szPktBuf[256000];                   //用于存放包的内容


	printf("%d\n",sizeof(ETHHDR));           //这3行是我在测试结构长度时用的,如果没有使用之
	printf("%d\n",sizeof(ETH_ARPHDR));       //前说的pack(push,1),pack(pop)长度就成了14,32
	printf("%d\n",sizeof(ARP));              //48,与我们的arp包的格式不符
	i=0;  
	AdapterLength = sizeof(AdapterName);
	if(PacketGetAdapterNames((char *)AdapterName,&AdapterLength)==FALSE)//获取所有网络适配器
	{
		printf("Unable to retrieve the list of the adapters!\n");
		return -1;
	}
	temp=AdapterName;
	temp1=AdapterName;
	while ((*temp!='\0')||(*(temp-1)!='\0'))          //将AdapterNames的内容转存到AdapterList
	{
		if (*temp=='\0') 
		{
		  memcpy(AdapterList[i],temp1,(temp-temp1)*2);
		  temp1=temp+1;
		  i++;
		}
		temp++;
	}

	AdapterNum=i;
	for (i=0;i<AdapterNum;i++)
		printf("\n%d- %s\n",i+1,AdapterList[i]); //输出获得的所有网络适配器
	printf("\n");  

	lpAdapter =   PacketOpenAdapter(AdapterList[0]);   //得到对应网络适配器的_Adapter结构,我  
	
	//就一个当然是0了  
	if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
	{
		dwErrorCode=GetLastError();
		printf("Unable to open the adapter, Error Code : %lx\n",dwErrorCode); 
		return -1;
	}  

	//以太网协议头
	arpPacket.ethhdr.dst[0]=0xff;                     //目的MAC地址
	arpPacket.ethhdr.dst[1]=0xff;
	arpPacket.ethhdr.dst[2]=0xff;
	arpPacket.ethhdr.dst[3]=0xff;
	arpPacket.ethhdr.dst[4]=0xff;
	arpPacket.ethhdr.dst[5]=0xff;
	//本机MAC地址38-59-F9-1B-42-F2
	arpPacket.ethhdr.src[0]=0x38;                     //源MAC地址
	arpPacket.ethhdr.src[1]=0x59;
	arpPacket.ethhdr.src[2]=0xF9;
	arpPacket.ethhdr.src[3]=0x1B;
	arpPacket.ethhdr.src[4]=0x42;
	arpPacket.ethhdr.src[5]=0xF2;

	arpPacket.ethhdr.type=htons(EPT_ARP);

	//ARP协议头
	arpPacket.eth_arp.arp_hrd=htons(ARP_HARDWARE);
	arpPacket.eth_arp.arp_pro=htons(EPT_IP);
	arpPacket.eth_arp.arp_hln=6;
	arpPacket.eth_arp.arp_pln=4;
	arpPacket.eth_arp.arp_op=htons(ARP_REQUEST);

	arpPacket.eth_arp.arp_sha[0]=0x38;                    //源MAC地址
	arpPacket.eth_arp.arp_sha[1]=0x59;
	arpPacket.eth_arp.arp_sha[2]=0xF9;
	arpPacket.eth_arp.arp_sha[3]=0x1B;
	arpPacket.eth_arp.arp_sha[4]=0x42;
	arpPacket.eth_arp.arp_sha[5]=0xF2;
	arpPacket.eth_arp.arp_spa=inet_addr("192.168.1.102");   //源IP地址

	arpPacket.eth_arp.arp_tha[0]=0x00;						//目的MAC地址
	arpPacket.eth_arp.arp_tha[1]=0x00;
	arpPacket.eth_arp.arp_tha[2]=0x00;
	arpPacket.eth_arp.arp_tha[3]=0x00;
	arpPacket.eth_arp.arp_tha[4]=0x00;
	arpPacket.eth_arp.arp_tha[5]=0x00;
	arpPacket.eth_arp.arp_tpa=inet_addr("192.168.1.105");   //目的IP地址

	lpPacket=PacketAllocatePacket();                       //得到一个包的_Packet结构
	if(lpPacket==NULL)
	{
		printf("alloc lppacket failed");
		return -1;
	}
	PacketInitPacket(lpPacket,(char*)&arpPacket,sizeof(arpPacket));

	if(PacketSendPacket(lpAdapter,lpPacket,true)==false)
	{
		printf("error in sending packet");
		return -1;
	}
	printf("send arp ok");
	//准备接收ARP包
	lpPacketRecv = PacketAllocatePacket();
	PacketReceivePacket(lpAdapter,lpPacketRecv,true);//未收到,通过抓包工具可以看到对方已经回复了ARP请求
	printf("send ok");

	PacketFreePacket(lpPacket);        //一点扫尾的工作
	PacketCloseAdapter(lpAdapter);

	return 1;
}

参考文章:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值