天天看點

利用原始套接字抓取資料

項目需求,需要從鍊路層抓包,分析實作網絡登入認證功能,現在網上找到兩個不錯的抓包程式,參考此文章,順利完成任務,現将此文章收藏與此,便參考,同時感謝文章版主,謝謝!

一:抓包分析:http://blog.csdn.net/aaa6695798/archive/2009/03/20/4008322.aspx

二:原始套接字抓包分析

原始套接字的建立

方法1: socket(PF_INET,SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP) 采用這樣的方法建立的套接字,是在IP層接收的資料 資料的資料結構格式為:

第三個參數協定若是指定,那麼該套接字隻能接受符合指定協定的資料包:IPPROTO_TCP接收采用tcp傳輸的資料包,IPPROTO_UDP接受采用udp傳輸的資料包,這一項決定了接受到的資料包中的IP資料標頭struct iphdr中的protocol值,為IPPROTO_TCP或者IPPROTO_UDP.

struct iphdr+struct tcphdr+資料//若struct iphdr中的protocol值為IPPROTO_TCP struct iphdr+struct udphdr+資料//若struct iphdr中的protocol值為IPPROTO_UDP 采用這樣的方法接收到的資料都是發往本機的資料,不能接受從本機發出去的資料,要抓取發送出去的資料,需要采用以下的方法

而且,我們抓取的資料隻是資料的拷貝,不會說阻礙資料的傳送,把資料給攔截了

方法2:socket(PF_PACKET,SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ARP|ETH|RARP))       socket(PF_PACKET,SOCK_DGRAM, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ARP|ETH|RARP)) 這種方法建立的套接字是在資料鍊路層直接抓取以太網幀,其中第二個參數有2種,若為SOCK_RAW,那麼抓取到的資料沒有經過系統處理,完整的保留有以太網幀頭,若為SOCK_DGRAM那麼以太網幀頭會被自動處理掉。 第三個參數的協定類型有: ETH_P_IP 0x0800 隻接收發往本機mac的ip類型的資料幀 ETH_P_ARP 0x0806 隻接受發往本機mac的arp類型的資料幀 ETH_P_RARP 0x8035 隻接受發往本機mac的rarp類型的資料幀 ETH_P_ALL 0x0003 接收發往本機mac的所有類型ip arp rarp的資料幀, 接收從本機發出的所有類型的資料幀.(混雜模式打開的情況下,會接收到非發往本地mac的資料幀) 如果我們要利用這個套接字發送資料,那麼發送的時候需要自己組織整個以太網資料幀.所有相關的位址使用struct sockaddr_ll 而不是struct sockaddr_in(因為協定簇是PF_PACKET不是AF_INET了),比如發送給某個機器,對方的位址需要使用struct sockaddr_ll. 接收到的完整的以太網幀資料格式為: ETH_P_IP:struct ether_header +struct iphdr+....後面為udphdr或者tcphdr,由iphdr中的protocol決定 ETH_P_ARP:struct ether_header+ARP資料包 等等 其中struct ether_header為以太網幀頭 關于上述的資料結構,都有在标準的頭檔案給于定義,不需要自己去寫,因為太長的緣故,需要的自己去搜下,很容易找到。

關于網卡的混亂模式 正常的網卡隻會接受mac位址為本機的以太網,當我們設定它為混亂模式的話,那麼它就會接受所有經過它的以太網資料幀,具體的設定方法可以用函數ioctl函數實作 ,可以參考下面的代碼.

當然有原始套接字的建立的程式執行必須有root權限或者在程式中setuid(0) 自己的寫的一個抓包程式:(在本例子中是從IP層抓取,依據注釋的代碼稍作修改就可以從資料鍊路層抓取以太網幀,同時隻要修改對應的注釋的處理代碼,就可以處理這個資料了,在本實作中僅統計顯示接受到的資料包的傳輸協定,來源IP,端口,目标IP,端口等一些簡單資訊) 執行方式(ubuntu下): gcc -o sniffer sniffer.c sudo ./sniffer eth0   要指定網口eth0

#include <stdio.h> #include <net/ethernet.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <string.h> #include <net/if.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> int analyData(char *data); int rawSocket(); int setPromisc(char *,int *); int count=0; int main(int argc,char **argv) {     if(argc!=2)     {         perror("please enter the ecterface");         exit(1);     }     int sock;     int msgsock;     struct sockaddr_in rcvaddr;     char buf[9216];     struct ifreq ifr;     int len;     int rval;     sock=rawSocket();     setPromisc(argv[1],&sock);     len=sizeof(struct sockaddr);     memset(buf,0,sizeof(buf));     while(1)     {         rval=recvfrom(sock,buf,sizeof(buf),0,(struct sockaddr*)&rcvaddr,&len);         if(rval>0)         {             printf("Get %d bytes/n",rval);             analyData(buf);                 }     }     return 0; } int analyData(char *data) {     struct iphdr *ip;     struct tcphdr *tcp;      struct ether_header *ether; //    ether=(struct ether_header*)data;//若資料是從資料鍊路曾抓取的,那麼就有這個以太網幀頭 //    printf("shu ju bao lei xing xie yi:%d/n",ether->ether_type); //    ip=(struct iphdr*)(data+sizeof(struct ether_header));     ip=(struct iphdr*)data;     count++;     printf("Protocol::%d/n",ip->protocol);     printf("Source IP::%s/n",inet_ntoa(*((struct in_addr*)&ip->saddr)));     printf("Dest IP::%s/n",inet_ntoa(*((struct in_addr*)&ip->daddr)));     tcp=(struct tcphdr*)(data+sizeof(*ip));     printf("Source Port::%d/n",ntohs(tcp->source));     printf("Dest Port::%d/n",ntohs(tcp->dest));     printf("Already get %d package/n",count);     printf("/n");     return 1; } int rawSocket()//建立原始套接字 {     int sock;     sock=socket(PF_INET,SOCK_RAW,IPPROTO_TCP);//IP層抓取

   //soket=socket(PF_PACKET,SOCK_RAW,ETH_P_IP)//資料鍊路層抓取     if(sock<0)     {         printf("create raw socket failed::%s/n",strerror(errno));         exit(1);     }          printf("raw socket ::%d created successful/n",sock);     return sock; } int setPromisc(char *enterface,int *sock)//設定eth0的混亂模式 {     struct ifreq ifr;     strcpy(ifr.ifr_name,"eth0");     ifr.ifr_flags=IFF_UP|IFF_PROMISC|IFF_BROADCAST|IFF_RUNNING;     if(ioctl(*sock,SIOCSIFFLAGS,&ifr)==-1)//設定混亂模式     {         perror("set 'eth0' to promisc model failed/n");         exit(1);     }     printf("set '%s' to promisc successed/n",enterface);     return 1; }