综合性实验(2) 通过编程获取IP地址与MAC地址的对应关系
实验内容说明
通过编程获取IP地址与MAC地址的对应关系实验,要求如下:
(1)在IP数据报捕获与分析编程实验的基础上,学习WinPcap(NPcap)的数据包发送方法。
(2)通过WinPcap(或NPcap)编程,获取IP地址与MAC地址的映射关系。
(3)程序要具有输入IP地址,显示输入IP地址与获取的MAC地址对应关系界面。界面可以是命令行界面,也可以是图形界面,但应以简单明了的方式在屏幕上显示。
(4)编写的程序应结构清晰,具有较好的可读性。
实验步骤
项目设计思路
本次实验需要获取主机网卡中对应IP的MAC地址,可以利用ARP请求方法,过程如下
- 获取网络接口卡列表,选择需要捕获MAC地址的网卡A(或选择对应的IP)
- 伪造ARP请求报文S,内容要求如下:
- ARP请求
- 广播
- 伪造源MAC地址和源IP地址
- 目的IP地址为网卡A的IP地址
- 用网卡A发送报文S‘
- 对网卡A进行流量监听,筛选其中的ARP报文(类型为0x806),捕获网卡A的ARP响应报文,在响应报文的帧首部源MAC地址部分可以看到发送该ARP响应的网卡对应的MAC地址
- 注意:由于广播发送只能在相同网段的网卡内进行发送,而主机上各个网卡的IP可能并不在同一网段,例如子网掩码为255.255.255.0的两个IP192.168.89.1和192.168.240.1,不能接收到对方的广播消息。所以必须本次实验必须==使用相同的网卡发出和响应ARP报文==
关键代码分析
获取设备列表
将设备列表存储至alldevs中
pcap_findalldevs_ex(pcap_src_if_string, NULL, &alldevs, errbuf)
打印网卡信息和对应IP
int i = 0;
for (pcap_if_t* d = alldevs; d != nullptr; d = d->next)//显示接口列表
{
//获取该网络接口设备的ip地址信息
for (pcap_addr* a = d->addresses; a != nullptr; a = a->next)
{
if (((struct sockaddr_in*)a->addr)->sin_family == AF_INET && a->addr)
{//打印ip地址
i++;
printf("%d ", i);
//打印相关信息
//inet_ntoa将ip地址转成字符串格式
printf("%s\t", d->name, d->description);
printf("%s\t%s\n", "IP地址:", inet_ntoa(((struct sockaddr_in*)a->addr)->sin_addr));
net[i] =d;
//将ip列表存入数组,方便后面选择目的ip和组装数据包
strcpy(ip[i], inet_ntoa(((struct sockaddr_in*)a->addr)->sin_addr));
}
}
}打印示例:
打开网络接口卡
pcap_t* open(char* name)//打开网络接口
{
pcap_t *temp=pcap_open(name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 100, NULL, errbuf);
if (temp==NULL)
printf("error");
return temp;//返回一个指针
}pcap_t* ahandle;//发送arp请求的网卡
ahandle = open(net[index]->name);组装报文
//报文格式
typedef struct FrameHeader_t {//帧首部
BYTE DesMAC[6];//目的地址
BYTE SrcMAC[6];//源地址
WORD FrameType;//帧类型
}FrameHeader_t;
typedef struct ARPFrame_t {//IP首部
FrameHeader_t FrameHeader;
WORD HardwareType;//硬件类型
WORD ProtocolType;//协议类型
BYTE HLen;//硬件地址长度
BYTE PLen;//协议地址长度
WORD Operation;//操作类型
BYTE SendHa[6];//发送方MAC地址
DWORD SendIP;//发送方IP地址
BYTE RecvHa[6];//接收方MAC地址
DWORD RecvIP;//接收方IP地址
}ARPFrame_t;//报文内容
ARPFrame_t ARPFrame;
//将APRFrame.FrameHeader.DesMAC设置为广播地址
for (int i = 0; i < 6; i++)
ARPFrame.FrameHeader.DesMAC[i] = 0xff;//表示广播
//将APRFrame.FrameHeader.SrcMAC设置为本机网卡的MAC地址
for (int i = 0; i < 6; i++)
ARPFrame.FrameHeader.SrcMAC[i] = 0x0f;
ARPFrame.FrameHeader.FrameType = htons(0x806);//帧类型为ARP
ARPFrame.HardwareType = htons(0x0001);//硬件类型为以太网
ARPFrame.ProtocolType = htons(0x0800);//协议类型为IP
ARPFrame.HLen = 6;//硬件地址长度为6
ARPFrame.PLen = 4;//协议地址长为4
ARPFrame.Operation = htons(0x0001);//操作为ARP请求
//将ARPFrame.SendHa设置为本机网卡的MAC地址
for (int i = 0; i < 6; i++)
ARPFrame.SendHa[i] = 0x0f;
//将ARPFrame.SendIP设置为本机网卡上绑定的IP地址
ARPFrame.SendIP = inet_addr("122.122.122.122");
//将ARPFrame.RecvHa设置为0
for(int i=0;i<6;i++)
ARPFrame.RecvHa[i] = 0;//表示目的地址未知
//将ARPFrame.RecvIP设置为请求的IP地址
ARPFrame.RecvIP = inet_addr(ip[index]);注意:帧首部信息中的目的MAC地址为==全1==代表该条报文需要==广播==发送,而请求报文的目的MAC地址为==全0==表示==目的地址未知==
发送消息
用ahandle网卡发送ARPFrame中的内容,报文长度为sizeof(ARPFrame_t),如果发送成功,返回0
pcap_sendpacket(ahandle, (u_char*)&ARPFrame, sizeof(ARPFrame_t))
捕获流量
while (1)//可能会捕获到多条消息
{
pcap_pkthdr* pkt_header;
const u_char* pkt_data;
int rtn = pcap_next_ex(ahandle, &pkt_header, &pkt_data);
if (rtn == 1)
{
ARPFrame_t* IPPacket = (ARPFrame_t*)pkt_data;
if (ntohs(IPPacket->FrameHeader.FrameType) == 0x806)
{//输出目的MAC地址
if (!compare(IPPacket->FrameHeader.SrcMAC, ARPFrame.FrameHeader.SrcMAC))//不是一开始发送的广播arp请求
{
printf(" MAC地址为:");
//输出MAC地址
for (int i = 0; i < 6; i++)
{
printf("%02x.", IPPacket->FrameHeader.SrcMAC[i]);
}
break;//找到MAc地址,退出
}
}
}
}获取远程网卡的MAC地址
获取远程MAC地址有两个方法
方法一:封装ARP请求时使用本机网卡的IP和MAC地址
先利用上述方法请求本地网卡的MAC地址,将本机IP和MAC填入报文
ARPFrame.FrameHeader.SrcMAC[i] = IPPacket->FrameHeader.SrcMAC[i];
ARPFrame.SendHa[i] = IPPacket->FrameHeader.SrcMAC[i];重新发送ARP请求
pcap_sendpacket(ahandle, (u_char*)&ARPFrame, sizeof(ARPFrame_t))
注意这里会返回网关和远程主机的MAC地址
方法二:直接使用伪造的IP和MAC地址进行发送
虽然本机网卡发送时需用的是虚拟MAC和IP地址,但是网关接收到组建的ARP请求后会由网关发出一个ARP请求,找到本机发送网卡的真实IP和MAC地址,从而进一步获取远程主机的MAC。
实验结果
查询1(本机):
使用命令行进行查询
结果一致
查询2(远程):
结果一致
查询3(对应网关)
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.