基于ARP和WinPcap的网络嗅探

   WinPcapwindows下的一个开源库,简单来说就是用户自己可以发送数据包,比如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"; //源主机ip
const char *pszRecverIpAddr = "10.126.72.35"; //目的主机ip
const unsigned char ucSenderMacAddr[6] = {0x90, 0x2B, 0x34, 0x9A, 0xC2, 0xBB};//源主机mac

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( &eth.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, &eth, sizeof eth );
memcpy( ucSend + sizeof eth, &arp, sizeof arp );

SendPacket(ucSend, sizeof arp + sizeof eth, pszRecverIpAddr );
return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

你可能感兴趣的:(socket,网络,internet,局域网,winpcap)