avatar

网络技术与应用作业四 通过编程获取IP地址与MAC地址的对应关系

综合性实验(2) 通过编程获取IP地址与MAC地址的对应关系

实验内容说明

通过编程获取IP地址与MAC地址的对应关系实验,要求如下:

(1)在IP数据报捕获与分析编程实验的基础上,学习WinPcap(NPcap)的数据包发送方法。

(2)通过WinPcap(或NPcap)编程,获取IP地址与MAC地址的映射关系。

(3)程序要具有输入IP地址,显示输入IP地址与获取的MAC地址对应关系界面。界面可以是命令行界面,也可以是图形界面,但应以简单明了的方式在屏幕上显示。

(4)编写的程序应结构清晰,具有较好的可读性。

实验步骤

项目设计思路

本次实验需要获取主机网卡中对应IP的MAC地址,可以利用ARP请求方法,过程如下

  1. 获取网络接口卡列表,选择需要捕获MAC地址的网卡A(或选择对应的IP)
  2. 伪造ARP请求报文S,内容要求如下:
    • ARP请求
    • 广播
    • 伪造源MAC地址和源IP地址
    • 目的IP地址为网卡A的IP地址
  3. 网卡A发送报文S
  4. 网卡A进行流量监听,筛选其中的ARP报文(类型为0x806),捕获网卡A的ARP响应报文,在响应报文的帧首部源MAC地址部分可以看到发送该ARP响应的网卡对应的MAC地址
  • 注意:由于广播发送只能在相同网段的网卡内进行发送,而主机上各个网卡的IP可能并不在同一网段,例如子网掩码为255.255.255.0的两个IP192.168.89.1和192.168.240.1,不能接收到对方的广播消息。所以必须本次实验必须==使用相同的网卡发出和响应ARP报文==
image-20201204155756073

关键代码分析

  • 获取设备列表

    将设备列表存储至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));
    }
    }
    }

    打印示例:

    image-20201206000157645

  • 打开网络接口卡

    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);
  • 组装报文

    //报文格式
    #pragma pack(1)//以1byte方式对齐
    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地址

      1. 先利用上述方法请求本地网卡的MAC地址,将本机IP和MAC填入报文

        ARPFrame.FrameHeader.SrcMAC[i] = IPPacket->FrameHeader.SrcMAC[i];
        ARPFrame.SendHa[i] = IPPacket->FrameHeader.SrcMAC[i];
      2. 重新发送ARP请求

        pcap_sendpacket(ahandle, (u_char*)&ARPFrame, sizeof(ARPFrame_t))

        注意这里会返回网关远程主机的MAC地址

    • 方法二:直接使用伪造的IP和MAC地址进行发送

      虽然本机网卡发送时需用的是虚拟MAC和IP地址,但是网关接收到组建的ARP请求后会由网关发出一个ARP请求,找到本机发送网卡的真实IP和MAC地址,从而进一步获取远程主机的MAC。

实验结果

  1. 查询1(本机):

    image-20201206165835709

    使用命令行进行查询

    image-20201204161001694

    结果一致

  2. 查询2(远程):

    image-20201206170601408

    image-20201206170738918

    结果一致

  3. 查询3(对应网关)

    image-20201206170640547

Author: Michelle19l
Link: https://gitee.com/michelle19l/michelle19l/2021/01/30/大三上/计网/网技第四次实验/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶