利用原始套接字抓取数据

项目需求,需要从链路层抓包,分析实现网络登录认证功能,现在网上找到两个不错的抓包程序,参考此文章,顺利完成任务,现将此文章收藏与此,便参考,同时感谢文章版主,谢谢!

 

一:抓包分析: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;

}


你可能感兴趣的:(网络与TCP/IP)