zoukankan      html  css  js  c++  java
  • libpcap使用(1)

    0x00 Libpcap介绍

    Libpcap是一个开源库,它为网络数据包捕获提供了一个高级接口。Libpcap作者的主要目标是创建一个独立于平台的API,以消除对每个应用程序中依赖于系统的数据包捕获模块的需求,因为几乎每个操作系统供应商都实现了自己的捕获机制.libpcap的API被设计用来供C和C++使用,然后有许多包装器,也可以让 Perl,Python Java C# or Ruby 使用。libpcap能在大多数类unix的操作系统上。还有一个名为Winpcap的Windows版本。如今,libpcap由tcpdump小组维护。

    0x01 使用Libpcap的第一步

    1.我们首先需要一个网络接口监听

    char *pcap_lookupdev(char *errbuf)
    /*
      1. 函数返回一个指针,该指针指向一个字符串,该字符串包含适合包捕获的第一个网络设备的名称
      2. errbuf参数是用户提供用来存储错误信息的
    */
    

    通常,当最终用户没有指定任何网络接口时,调用此函数网络接口。使用硬编码接口名通常是一个坏主意,因为它们通常不能跨平台移植。libpcap实现的许多函数都使用errbuf这个参数,用来获取错误。在分配缓冲区时我们必须小心,因为它必须至少能够容纳 PCAP_ERRBUF_SIZE 这么多字节。我们可以使用PCAP_ERRBUF_SIZE这个宏来作为数组的大小。
    2. 打开获取的网络接口

    pcap_t *pcap_open_live(const char *device, int snaplen, int promise, int to_ms, char *errbuf)
    /*
    参数:
      1. 该函数的第一参数是一个字符串,包含要打开网络接口的名称。
      2. 第二个参数是要捕获的最大字节数。如果我们只对获取数据头感兴趣或者有限制内存的嵌入式编程,那么可以为第二个参数设置一个较低的值。
      3. 第三个参数promise 标志决定了网络接口是否应该进入混杂模式,混杂模式是说网卡可以接收不是该网卡目的地的数据包。0 表示非混杂模式,其他值表示为混杂模式。即使我们告诉libpcap以非混杂模式侦听,如果接口已经处于混杂模式,可能将仍然保持混杂模式。
      4. 第四个参数 to_ms  定义了在将捕获的信息从内核空间复制到用户空间之前,内核应该等待多少毫秒,值为0将导致读取操作永远等待,直到有足够的数据包到达网络。
      5. 第五个参数errbuf参数是用户提供用来存储错误信息的
    返回值:
      该函数返回一个 pcap_t 类型的接口处理句柄,稍后调用libpcap提供的其余函数时将使用。
    */
    

    通常最大的以太网帧大小为1518字节,但是,其他链路类型,如FDDI或802.11,则具有更大的带宽限制。65535应该足以容纳来自任何网络的任何数据包。内核切换在计算上是昂贵的。如果我们捕获大量的网络流量,最好让内核在越过内核用户空间边界之前对一些数据包进行分组。我们不应该想当然地认为我们不会接收到发往其他主机的流量,相反,最好使用libpcap提供的过滤功能。
    3. 捕获数据包

    u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) 
    /*
    参数:
      1. 第一个参数为接收一个 pcap_open_live 函数返回的pcap_t 句柄
      2. 第二个参数为指向pcap_pkthdr结构体的指针
    返回值:
      返回值为 返回到达网络接口的第一个数据包
    */
    int pcap_loop(pcap_t *p, int ent,pcap_handler callback, u_char *user)
    /*
    该函数用于收集和处理数据包
    参数:
      1. 第一个参数为接收一个 pcap_open_live 函数返回的pcap_t 句柄
      2. 第二个参数为要捕获数据包的个数,在捕获cnt个数据包之前,他不会返回。将ent设置为负值该函数不会返回,直到出错时才返回。
      3. 第三个参数 为处理数据包的回调函数 当获取数据包之后,该函数调用回调函数。
      4. 第四个参数 为用户自定义参数,该参数可以传递给回调函数。 
    *
    */
    int pcap_dispatch(pcap_t*p,int ent,pcap_handler callback,u_char*user)
    /*
    该函数用于收集和处理数据包
    参数:
      1. 第一个参数为接收一个 pcap_open_live 函数返回的pcap_t 句柄
      2. 第二个参数为要捕获数据包的个数,在捕获cnt个数据包之前,他不会返回。将ent设置为负值该函数不会返回,直到出错时才返回。
      3. 第三个参数 为处理数据包的回调函数 当获取数据包之后,该函数调用回调函数。
      4. 第四个参数 为用户自定义参数,该参数可以传递给回调函数。 
    *
    */
    

    pcap_loop 不返回数据包,每次有数据包准备好读取的时候,他都会调用用户定义的函数。这样我们就可以在一个单独的函数中进行自己的处理,而不是在循环中调用pcap_next() 来处理其中的内容。但是有一个问题。如果 pcap_loop() 调用我们的函数,我们应该如何传递参数 。我们不得不用丑陋的全局变量吗?这是用户参数。每次调用都会传递此指针。该参数的类型是 u_char ,因此 我们必须将其转换为调用pcap_loop()和在回调函数中自己使用的类型。我们的数据包处理功能必须有一个特定的原型,否则pcap_loop()将不知道如何使用它。pcap_dispatch和pcap_loop() 十分相似,只是在pcap_open_live()的 to_ms毫秒后 就会超时返回。
    4.回调函数声明

    void function_name(u_char *userarg, const struct pcap_pkthdr* pkthdr, const u_char * packet);
    /*
    参数:
      1. 第一个参数是传递给pcap_loop()的用户指针
      2. 第二个参数是指向包含数据包信息的结构的指针
      3. 第三个参数是pcap数据包的指针 指向内容
     
    */
    //struct pcap_pkthdr  结构体定义
    struct pcap_pkthdr {
        struct timeval ts; /* Timestamp of capture */
        bpf_u_int32 caplen; /* Number of bytes that were stored */
        bpf_u_int32 len; /* Total length of the packet */
    };
    //caplen成员通常具有与len相同的值,除非捕获的数据包的大小超过open_pcap_live()函数中snaplen指定的值
    
    
    

    一个简单的示例:

    #include <pcap.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    #define MAXBYTES2CAPTURE 2048
    void processPacket(u_char *arg, const struct pcap_pkthdr* pkthdr, const u_char * packet){
        int i=0, *counter = (int *)arg;
        printf("Packet Count: %d
    ", ++(*counter));
        printf("Received Packet Size: %d
    ", pkthdr->len);
        printf("Payload:
    ");
        for (i=0; i<pkthdr->len;i++){
            if ( isprint(packet[i]))
                printf("%c ", packet[i]);
            else
                printf(".");
            if( (i%16 == 0 && i!=0) || i==pkthdr->len-1 )
                printf("
    ");
    	}
    	return;
    }
    
    int main( ){
        int i=0, count=0;
        pcap_t *descr = NULL;
        char errbuf[PCAP_ERRBUF_SIZE], *device=NULL;
        memset(errbuf,0,PCAP_ERRBUF_SIZE);
        /* Get the name of the first device suitable for capture */
        device = pcap_lookupdev(errbuf);
        printf("Opening device %s
    ", device);
        /* Open device in promiscuous mode */
        descr = pcap_open_live(device, MAXBYTES2CAPTURE, 1, 512, errbuf);
        /* Loop forever & call processPacket() for every received packet*/
        pcap_loop(descr, -1, processPacket, (u_char *)&count);
        return 0;
    }
    
  • 相关阅读:
    Python函数知识汇总-课堂笔记
    集合set内部常用功能和使用方法-课堂笔记及课后总结
    win7_64位操作系统安装python3.6.3遇到的问题和解决方法
    字典dic内部常用功能和使用方法-课堂笔记及课后总结
    列表内部常用功能和使用方法-课堂笔记及课后总结
    Python Str内部功能-个人课堂笔记,课后总结
    深入理解Java虚拟机读书笔记9----线程完全与锁优化
    深入理解Java虚拟机读书笔记8----Java内存模型与线程
    深入理解Java虚拟机读书笔记7----晚期(运行期)优化
    深入理解Java虚拟机读书笔记6----早期(编译期)优化
  • 原文地址:https://www.cnblogs.com/mhpcuit/p/14798861.html
Copyright © 2011-2022 走看看