用libpcap抓包并进行底层网络欺骗 --part 2

用libpcap抓包并进行底层网络欺骗 --part 2

(附:同志们好,抱歉让你们久等了。。。毕业找工作是心中主要的痛。。。当所有事都安顿好之后我会将更多的精力放在这上面 :-) :-) )

现在我们对抓包的本质有了一定了解,已经有了一个可以从中拽点东西出来的接口,让我们继续并抓个包你看怎么样?
“赶快给我一个该死的例子让我研究它。。。“,你大喊
非常好。。。现在出发。。。

/***************************************************
* file:     testpcap1.c
* Date:     Thu Mar 08 17:14:36 MST 2001 
* Author:   Martin Casado
* Location: LAX Airport (hehe)
*
* Simple single packet capture program
*****************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h> /* if this gives you an error try pcap/pcap.h */
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h> /* includes net/ethernet.h */

int main(int argc, char **argv)
{
    int i;
    char *dev; 
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t* descr;
    const u_char *packet;
    struct pcap_pkthdr hdr;     /* pcap.h */
    struct ether_header *eptr;  /* net/ethernet.h */

    u_char *ptr; /* printing out hardware header info */

    /* grab a device to peak into... */
    dev = pcap_lookupdev(errbuf);

    if(dev == NULL)
    {
        printf("%s\n",errbuf);
        exit(1);
    }

    printf("DEV: %s\n",dev);

    /* open the device for sniffing.

       pcap_t *pcap_open_live(char *device,int snaplen, int prmisc,int to_ms,
       char *ebuf)

       snaplen - maximum size of packets to capture in bytes
       promisc - set card in promiscuous mode?
       to_ms   - time to wait for packets in miliseconds before read
       times out
       errbuf  - if something happens, place error string here

       Note if you change "prmisc" param to anything other than zero, you will
       get all packets your device sees, whether they are intendeed for you or
       not!! Be sure you know the rules of the network you are running on
       before you set your card in promiscuous mode!!     */

    descr = pcap_open_live(dev,BUFSIZ,0,-1,errbuf);

    if(descr == NULL)
    {
        printf("pcap_open_live(): %s\n",errbuf);
        exit(1);
    }


    /*
       grab a packet from descr (yay!)                    
       u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h) 
       so just pass in the descrīptor we got from         
       our call to pcap_open_live and an allocated        
       struct pcap_pkthdr                                 */

    packet = pcap_next(descr,&hdr);

    if(packet == NULL)
    {/* dinna work *sob* */
        printf("Didn't grab packet\n");
        exit(1);
    }

    /*  struct pcap_pkthdr {
        struct timeval ts;   time stamp 
        bpf_u_int32 caplen;  length of portion present 
        bpf_u_int32;         lebgth this packet (off wire) 
        }
     */

    printf("Grabbed packet of length %d\n",hdr.len);
    printf("Recieved at ..... %s\n",ctime((const time_t*)&hdr.ts.tv_sec)); 
    printf("Ethernet address length is %d\n",ETHER_HDR_LEN);

    /* lets start with the ether header... */
    eptr = (struct ether_header *) packet;

    /* Do a couple of checks to see what packet type we have..*/
    if (ntohs (eptr->ether_type) == ETHERTYPE_IP)
    {
        printf("Ethernet type hex:%x dec:%d is an IP packet\n",
                ntohs(eptr->ether_type),
                ntohs(eptr->ether_type));
    }else  if (ntohs (eptr->ether_type) == ETHERTYPE_ARP)
    {
        printf("Ethernet type hex:%x dec:%d is an ARP packet\n",
                ntohs(eptr->ether_type),
                ntohs(eptr->ether_type));
    }else {
        printf("Ethernet type %x not IP", ntohs(eptr->ether_type));
        exit(1);
    }

    /* THANK YOU RICHARD STEVENS!!! RIP*/
    ptr = eptr->ether_dhost;
    i = ETHER_ADDR_LEN;
    printf(" Destination Address:  ");
    do{
        printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
    }while(--i>0);
    printf("\n");

    ptr = eptr->ether_shost;
    i = ETHER_ADDR_LEN;
    printf(" Source Address:  ");
    do{
        printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
    }while(--i>0);
    printf("\n");

    return 0;
}

好了,看上去还不很坏,不是吗?让我们来运行它。。。

[root@pepe libpcap]# ./a.out
DEV: eth0
Grabbed packet of length 76
Recieved at time..... Mon Mar 12 22:23:29 2001

Ethernet address length is 14
Ethernet type hex:800 dec:2048 is an IP packet
 Destination Address:   0:20:78:d1:e8:1
 Source Address:   0:a0:cc:56:c2:91
[root@pepe libpcap]# 

在输完a.out之后我跳到另外一个终端并试着ping www.google.com。输出捕获到ping到www.google.com的ICMP包。如果你不知道网络的掩盖下究竟发生了什么的话,你可能是个古董级人物对计算机如何获得目的以太网地址。啊哈!你不会真的以为这里的目的以太网地址就是www.google.com主机的地址吧,是不是?
“。。。恩。。啊。。当然不是了”,你结结巴巴地说。
目的地址很有可能是你的网关。。。它把你的网络与英特网连在一起。首先,数据包要找到去网关的路,然后再由网关转送到路由,或者网关自己作出路由决定将包送到哪里。让我们对发包到路由的过程作个快速而清楚的核查吧。。。。哈哈!!用route命令获得你的网关IP

[root@pepe libpcap]# /sbin/route 
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
127.0.0.0       *               255.0.0.0       U     0      0        0 lo
default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

然后用arp命令查看缓存的以太网地址。。。

[root@pepe libpcap]# /sbin/arp 
Address            HWtype    HWaddress        Flags Mask          Iface
192.168.1.1                ether   00:20:78:D1:E8:01   C                     eth0

如果你的网关不在你的arp缓存中,试着telnet到它,然后再用arp命令。嘿,顺便说一下,这可能是一个漫长,痛苦,血腥,无知的获得网关硬件地址的方法,但是我想不出第二个了。。。

注意到我的网关地址与抓到的包的目的地址相同。所有不是送到同一网络的的数据包,都必须通过网关。唉!!!我们还是没有回答这个问题。。。“我的电脑是怎么知道网关的硬件地址的?”,让我们离题一会先,电脑知道了网关的IP地址它当然就知道发出站包给它。正如你从物美价廉的arp命令看到的,有一个包含了映射IP地址到硬件地址的表(arp缓存),“哦。。。但是它是怎样构建这个arp缓存的呢?!!!“,你叫道。

英特网上的硬件地址用Address Resolution Protocol(ARP)地址解析协议获得,ARP在RFC826中有详细描述!在你发出一个包的时候大概发生了些什么呢?内核首先会检查ARP缓存看看是否已经有更高一级的目的硬件地址,如果没有,内核会送出一个ETHERTYPE_ARP类型的ARP请求,ETHERTYPE_ARP在 net/ethernet.h中定义如下:

#define    ETHERTYPE_ARP        0x0806        /* Address resolution */

一旦收到ARP包,拥有更高一级地址的机器(我的意思是网关)将用ARP回答来回复,基本上就是说:喂。。。我在这里,把包发过来吧。。。随便带点吃的过来~!”(太糟糕了。。。)

[root@pepe libpcap]# /sbin/arp -n    # look at arp cache 
Address            HWtype    HWaddress        Flags Mask          Iface
192.168.1.1                ether   00:20:78:D1:E8:01   C                     eth0

[root@pepe libpcap]# /sbin/arp -n -d 192.168.1.1  #delete gateqay entrance
[root@pepe libpcap]# /sbin/arp -n   #make sure gateway hardware addy is empty             
Address            HWtype    HWaddress        Flags Mask          Iface
192.168.1.1                        (incomplete)                              eth0
[root@pepe libpcap]# ./a.out
DEV: eth0
Grabbed packet of length 42
Recieved at time..... Tue Mar 13 00:36:49 2001

Ethernet address length is 14
Ethernet type hex:806 dec:2054 is an ARP packet
 Destination Address:   ff:ff:ff:ff:ff:ff
 Source Address:   0:a0:cc:56:c2:91
[root@pepe libpcap]#echo YAY 

因此你可以看到,一旦从缓存中移除硬件地址,我的电脑需要广播ARP请求(也就是 f:ff:ff:ff:ff:ff)来寻找更高一级的地址拥有者, 这里是 192.168.1.1。如果将你的ARP缓存清除并修改testpcap1.c用来抓两个包的话你认为为发生什么事? 嘿嘿,我知道为什么你不试一试 :-P~~~

让我们通过检查<net/ethernet.h>看看数据包,现在我们不关心网络于传输协议,仅仅想窥探一下以太网的头里面有什么东东,假定我们的带宽为10Mb/s...

/* 10Mb/s ethernet header */
struct ether_header
{
  u_int8_t  ether_dhost[ETH_ALEN];    /* destination eth addr    */
  u_int8_t  ether_shost[ETH_ALEN];    /* source ether addr    */
  u_int16_t ether_type;                /* packet type ID field    */
} __attribute__ ((__packed__));

看起来前ETH_ALEN位是数据包里的以太网地址(在inux/if_ether.h有ETH_ALEN的定义 :-)。接下来的ETH_ALEN位是源地址。最后一个是数据包类型。以下是我机器上 net/ethernet.h 里的协议ID 。

/* Ethernet protocol ID's */
#define    ETHERTYPE_PUP        0x0200      /* Xerox PUP */
#define    ETHERTYPE_IP        0x0800        /* IP */
#define    ETHERTYPE_ARP        0x0806        /* Address resolution */
#define    ETHERTYPE_REVARP    0x8035        /* Reverse ARP */

因为该指南的目的,我将主要讲述IP顺带讲一下ARP。。。其实真正的原因是我根本不知道该死的Xerox PUP是什么东东。

好~!。。讲到哪儿了?我们知道大部分抓包的方法,讲述了如何解析硬件地址和基本的以太网包是什么样子。直到这里我们仍然只用了libpcap九牛之一毛的功能,而且甚至都没看到数据包里面是什么样子(在硬件头里面)。要多的事太多而时间太少 :-) 现在你可能会这样说, 要用一此只抓一个包的程序几乎不能做什么真正的数据包分析。我们真正要做到的是写一个简单的抓包引擎以尽可能地捕获更多的包并滤出我们感兴趣的部分。在下一节我们将构造一个简单的抓包引擎来帮助我们解剖包(呃。。。这位兄台看上去比较粗鲁) .

你可能感兴趣的:(用libpcap抓包并进行底层网络欺骗 --part 2)