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