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的基本工作原理如下:
- 每台主机都会根据以往在网络中与其他主机的通信,在自己的 ARP 缓存区(ARP Cache)中建立一个 ARP 列表,以表示网络中主机 IP 地址和 MAC 地址的对应关系。
- 当源主机需要将一个数据包发送到目标主机时,会首先检查自己 ARP 列表中是否存在该包中所包含的目标主机 IP 地址对应的 MAC 地址。如果有,则直接将数据包发送到这个 MAC 地址主机上;如果没有,就向本地网段(局域网)发起一个 ARP 请求的广播包,查询此 IP 地址目标主机对应的 MAC 地址。此 ARP 请求数据包里包括源主机的 IP 地址、硬件地址,以及目标主机的 IP 地址。
- 网络中所有的主机在收到这个 ARP 请求后,会检查数据包中的目标 IP 地址是否和自己的 IP 地址一致。如果不相同就忽略此数据包;如果相同,该主机首先将源端的MAC 地址和 IP 地址的对应表项添加到自己的 ARP 列表中。如果发现 ARP 表中已经存在该 IP 地址所对应的 MAC 地址表项信息,则将其覆盖,然后给源主机发送一个ARP 响应数据包,告诉对方自己是它需要查找的 MAC 地址主机。
- 源主机在收到这个 ARP 响应数据包后,将得到的目标主机的 IP 地址和 MAC 地址对应表项添加到自己的 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;
}
参考文章: