WinPcap是windows下的一个开源库,简单来说就是用户自己可以发送数据包,比如windows XP之后就不能用socket发送SYN数据包了,因为操作系统进行了封装。所以想要发送自己的数据包,就要绕开操作系统,WinPcap就提供了这样的功能。
再说ARP——地址解析协议,ARP是数据链路层的协议。一般来说,一个局域网会用一个或多个路由器与Internet连接,那么当外部数据要发送到目的主机是怎么样的呢?同样,局域网内部主机间通信是怎么样的。我们都知道,网络通信的本质是寻找MAC地址,但是在socket编程中我们只是给了目标机的IP地址,并没有MAC地址。
MAC的工作原理:外来的数据包到达路由器时,路由器取出数据包中的目的IP地址,如果路由器知道这个IP地址是哪台主机,那么路由器会直接将数据包发过去。假如路由器不知道这个IP是哪台主机,则路由器会广播一个ARP请求包,意思是“192.168.0.10是谁的IP?”。当局域网的主机收到数据包后会检查自己的IP,看是不是192.168.1.10,如果不是,则丢弃,假如是,则该主机会把发送方的IP和MAC地址记录下来,同时把自己的MAC发过去。
既然ARP的请求是广播形式,那我们可以用ARP来嗅探局域网中的主机,简单来说就是发送一个ARP数据请求包,看有没有回应,如果有,则说明局域网中有对应的IP主机。
0 8 16 32
+-------------------------+-------------------------+
| 硬件类型 | 协议类型 |
+-------------------------+-------------------------+
| 硬件长度 | 协议长度 | 操作 |
+------------+------------+-------------------------+
| 发送端硬件地址 6字节 |
+---------------------------------------------------+
| 发送端协议地址 |
+---------------------------------------------------+
| 目的端硬件地址 6字节 |
+---------------------------------------------------+
| 目的端协议地址 |
+---------------------------------------------------+
硬件类型:以太网上填 1
协议类型:如果是IPV4,则写0x0800
硬件长度:物理地址的长度以字节为单位,mac地址的长度为6
协议长度:逻辑地址长度,即IP地址,以字节为单位,IPV4的长度为4字节
操作:1表示ARP请求,2表示ARP应答
发送端硬件地址 / 发送端协议地址:发送端的mac地址和IP地址
目的端硬件地址 / 目的端协议地址:目的端mac地址和IP地址,ARP请求数据包是以广播的形式发送的,所以他不知道目的硬件地址。《TCP-IP协议簇》上说对于ARP请求报文,目的端硬件地址全为0,但我测试时全为1也可以。
只是一个ARP数据包还是不能发送的,得加上以太网头,最后组成如下格式:
0 6 12 14
+-------------------+------------------+--------+-----------+
| 以太网目的地址 | 以太网源地址 | 帧类型 | ARP报文 |
+-------------------+------------------+--------+-----------+
注意:对于ARP请求报文来说,它是广播发送的,所以以太网目的地址全写1,不然发不出去。
- struct ARP_HEADER
- {
- unsigned char ucHardwareType[2];
- unsigned char ucProtocolType[2];
- unsigned char ucHardwareLen;
- unsigned char ucProtocolLen;
- unsigned char ucOperatorType[2];
- unsigned char ucSenderMacAddr[6];
- unsigned char ucSenderIpAddr[4];
- unsigned char ucRecverMacAddr[6];
- unsigned char ucRecverIpAddr[4];
- unsigned char ucPadding[18];
- };
-
- struct ETHERNET_HEADER
- {
- unsigned char ucDstMacAddr[6];
- unsigned char ucSrcMacAddr[6];
- unsigned char ucEthernetType[2];
- };
-
- struct DEVS_INFO
- {
- char szDevName[512];
- char szDevsDescription[512];
- };
-
- int GetAllDevs( DEVS_INFO devsList[] )
- {
- int nDevsNum = 0;
- pcap_if_t *alldevs;
- char errbuf[PCAP_ERRBUF_SIZE];
- if ( pcap_findalldevs(&alldevs,errbuf) == -1 )
- {
- return -1;
- printf("error in pcap_findalldevs_ex: %s\n",errbuf);
- }
- for ( pcap_if_t *d = alldevs; d != NULL; d = d->next )
- {
- strcpy( devsList[nDevsNum].szDevName, d->name );
- strcpy( devsList[nDevsNum].szDevsDescription, d->description );
- nDevsNum++;
- }
- pcap_freealldevs(alldevs);
-
- return nDevsNum;
- }
-
- void HandlePacketCallBack(unsigned char *param,const struct pcap_pkthdr* packet_header, const unsigned char *ucCaptureContent)
- {
- ETHERNET_HEADER *pEthHeader = ( ETHERNET_HEADER *)ucCaptureContent;
- if ( *((unsigned short *)(pEthHeader->ucEthernetType)) != ntohs(0x0806) )
- {
- return;
- }
-
- ARP_HEADER *pArp = ( ARP_HEADER *)(ucCaptureContent + sizeof (ETHERNET_HEADER) );
- if ( inet_addr( (char *)param) != *((unsigned int *)(pArp->ucSenderIpAddr ) ) )
- {
- return ;
- }
- if ( htons(0x002) != *(unsigned short *)pArp->ucOperatorType )
- {
- return ;
- }
- printf("\nIP: ");
- for ( int i = 0; i < 4; ++i )
- {
- printf("%d.", pArp->ucSenderIpAddr[i]);
- }
- printf("\b MAC: ");
- for ( int i = 0; i < 6; ++i)
- {
- printf("0x%02x-", pArp->ucSenderMacAddr[i]);
- }
- printf("\b \n");
- }
-
- int SendPacket( const unsigned char *ucSendBuffer, int size, const char *pszRecverIpAddr )
- {
- DEVS_INFO devsList[64];
- int nDevsNum = GetAllDevs( devsList );
- if ( nDevsNum < 1 )
- {
- printf("Get adapter infomation failed!");
- exit(0);
- }
-
- for ( int i = 0; i < nDevsNum; ++i )
- {
- printf("%d. %s %s\n", i + 1, devsList[i].szDevName, devsList[i].szDevsDescription);
- }
-
- int selIndex = 0;
- printf("Input the index of adapter: ");
- scanf("%d", &selIndex );
-
- char errbuf[PCAP_ERRBUF_SIZE];
- pcap_t *handle = pcap_open_live(devsList[selIndex-1].szDevName, 65536, 1, 1000, errbuf );
-
- if ( NULL == handle )
- {
- printf("\nUnable to open the adapter. %s is not supported by WinPcap\n");
- return 0;
- }
-
- int index = 0;
- while ( index ++ < 1 )
- {
- if ( pcap_sendpacket(handle, ucSendBuffer, size ) != 0)
- {
- printf("\nError sending the packet: %s\n", pcap_geterr(handle) );
- return 0;
- }
- }
- pcap_loop( handle, 64, HandlePacketCallBack, (unsigned char *)pszRecverIpAddr );
- pcap_close(handle);
- }
-
- int main()
- {
- const char *pszSenderIpAddr = "10.126.72.37";
- const char *pszRecverIpAddr = "10.126.72.35";
- const unsigned char ucSenderMacAddr[6] = {0x90, 0x2B, 0x34, 0x9A, 0xC2, 0xBB};
-
- ARP_HEADER arp;
- memset(&arp, 0x00, sizeof arp);
-
- *(unsigned short *)&arp.ucHardwareType = htons(0x0001);
- *(unsigned short *)arp.ucProtocolType = htons(0x0800);
-
- arp.ucHardwareLen = 0x06;
- arp.ucProtocolLen = 0x04;
-
- *(unsigned short *)arp.ucOperatorType = htons(0x0001);
-
- memcpy( arp.ucSenderMacAddr, ucSenderMacAddr, sizeof ucSenderMacAddr );
- *( unsigned *)arp.ucSenderIpAddr = inet_addr(pszSenderIpAddr);
-
- memset( &arp.ucRecverMacAddr, 0xff, sizeof arp.ucRecverMacAddr );
- *(unsigned *) arp.ucRecverIpAddr = inet_addr(pszRecverIpAddr);
-
- memcpy( arp.ucPadding, "hello world", 18);
-
- ETHERNET_HEADER eth;
- memset( ð.ucDstMacAddr, 0xFF, sizeof eth.ucDstMacAddr);
- memcpy( eth.ucSrcMacAddr, arp.ucSenderMacAddr, 6 );
- *(unsigned short *)eth.ucEthernetType = htons(0x0806);
-
- unsigned char ucSend[1024];
- memset( ucSend, 0, sizeof ucSend );
- memcpy( ucSend, ð, sizeof eth );
- memcpy( ucSend + sizeof eth, &arp, sizeof arp );
-
- SendPacket(ucSend, sizeof arp + sizeof eth, pszRecverIpAddr );
- return 0;
- }