⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sniff_snort.c

📁 嗅探是网络攻击中的重要环节
💻 C
📖 第 1 页 / 共 2 页
字号:
Example 1. 
#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]); 
    } 
  } 
  
} 
PF_PACKET协议簇可以让一个应用程序把数据包变成似乎从网络层接收的样子,但是没有办法抓到那些不是发向自己主机的包。正如我们前面看到的,网卡丢弃所有不含有主机MAC地址的数据包,这是因为网卡处于非混杂模式,即每个网卡只处理源地址是它自己的帧!只有三个例外:如果一个帧的目的MAC地址是一个受限的广播地址(255.255.255.255)那么它将被所有的网卡接收;如果一个帧的目的地址是组播地址,那么它将被那些打开组播接收功能的网卡所接收;网卡如被设置成混杂模式,那么它将接收所有流经它的数据包。
最后一种情况当然是我们最感兴趣的了,把网卡设置成混杂模式,我们只需要发出一个特殊的ioctl( )调用在那个网卡上打开一个socket,因为这是一个具有危险性的操作,所以这个调用只有具有root权限的用户才可完成,假设那个“sock”包含一个已经打开的socket, 
下面的代码将完成这个操作: 
strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ); 
ioctl(sock, SIOCGIFFLAGS, &ethreq); 
ethreq.ifr_flags |= IFF_PROMISC; 
ioctl(sock, SIOCSIFFLAGS, &ethreq); 
下面我们来看一个完整的例子: 
Example 2. 
#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]); 
    } 
  } 
  
} 
如果我们在一个与局域网相连的主机上以root权限编译和运行这个程序,你将会看到流经的所有数据包,即使它不是发向自己主机的,这是因为你的网卡现在处于混杂模式,你可以很容易的用ifconfig这个命令看到!
注意,如果你的局域网用的是交换机而不是集线器,那么你只能抓到在你主机所在的交换分支中的数据包,这是由于交换的工作原理,在这种情况下,你没有什么其他可做的了(除非你利用MAC地址欺骗来瞒过交换机,这个内容超出本文讨论的范围了),如果你想知道这方面更多的信息,你可以去www.google.com,那里你将会搜索到很多你需要的东西! 
我们关于嗅探方面的问题似乎都解决了,但是还有一个问题值得我们去思考!如果我们实验Example2的时候正好遇到了网络拥塞,我们将会看到我们的嗅探器将打出非常多的数据,随着网络拥塞的加剧,主机将不能顺畅的执行这个程序,嗅探器这时会开始丢包!
解决这个问题的方法就是对你抓到的包进行过滤,使它只显示那些你感兴趣的那些部分!在源代码中多使用一些if语句,这将有益于去除那些不需要的信息,当然这样也许效率不是很高!内核仍然将处理所有的报文,这将浪费进程时间。 
最好的解决办法就是把过滤过程尽可能早的放报文进程链中。(packet-processing chain它开始于链路层,终止于应用层)Linux的内核允许我们把一个名为BPF的过滤器直接放到PF_PACKET protocol-processing routines(在网卡接收中断执行后立即执行)中,过滤器将决定哪些包被送到应用程序,哪些包被丢弃! 
为了做到尽可能的灵活,这个过滤程序可以根据使用者的定义来运行!这个程序是由一种名为BPF的伪机器码写成的。BPF看上去很像带着一对寄存器和保存值的汇编语言,完成数学运算和条件分支程序!过滤程序检查每个包,BPF进程操作的内存空间包含着报文数据!过滤的结果是一个整数,指出有多少字节的报文需要送到应用层。这是一个更进一步的优点,因为我们一般只对报文的前面几个字节感兴趣,这样可以避免复制过量的数据以节省进程时间。 
虽然BPF语言非常简单易学,但是大多数人还是习惯于人类可以阅读的表达方式!所以为了不用BPF语言去描述那些细节和代码,我们将下面开始讨论如何得到一个过滤器的代码! 
首先,你需要安装tcpdump,可以从LBL得到!但是,如果你正在读本文,那似乎说明你已经会使用tcpdump了!第一个版本的作者也是那些写BPF的人,事实上,tcpdump要用到BPF,它要用到一个叫libpcap的类库,以此去抓包和过滤。这个类库是一个与操作系统无关的独立的包,为BPF的实现提供支持,当在装有Linux的机器上使用时,BPF的功能就由Linux包过滤器实现了! 
libpcap提供的一个最有用的函数是pcap_compile(), 它可以把一个输入输出的逻辑表达式变为BPF代码!tcpdump利用这个函数完成在用户输入的命令行和BPF代码之间的转换! 
tcpdump有个我们很感兴趣但是很少使用的参数 ,-d,可以输出BPF代码! 
举个例子,如果tcpdump host 192.168.9.10那么将启动嗅探器并只抓到源地址或者目的地址是192.168.9.10的报文!如果tcpdump -d host 192.168.9.10那么将显示出BPF代码:
Example 3. 
Seal:~# tcpdump -d host 192.168.9.10 
(000) ldh      [12] 
(001) jeq      #0x0800          jt 2    jf 6 
(002) ld       [26] 
(003) jeq      #0xc0a8090a      jt 12   jf 4 
(004) ld       [30] 
(005) jeq      #0xc0a8090a      jt 12   jf 13 
(006) jeq      #0x0806          jt 8    jf 7 
(007) jeq      #0x8035          jt 8    jf 13 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -