pcap_next 导致分段错误

pcap_next causes a segmentation fault

我正在编写一个简单的数据包捕获程序,它使用 pcap 函数捕获三个数据包。但它因分段错误而崩溃。下面是程序的源代码。

#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %s\n", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %s\n", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
    if(pcap_handle < 0){
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i++){
        printf("\n===packet %d===\n", i+1);
        packet = pcap_next(pcap_handle, header);
        printf("Got a %d bute packet\n", header->len);
        dump(packet, header->len);
    }
    
    pcap_close(pcap_handle);
}

你能弄清楚这是怎么回事吗??

关于条件 if(pcap_handle < 0):此处的 API 文档, https://www.tcpdump.org/manpages/pcap.3pcap.html,表示函数“pcap_open_live() return成功时为pcap_t *,失败时为NULL。错误检查应该是if(pcap_handle == NULL)案例.

在当前程序中,如果 pcap_open_live() 到 return NULL 指示失败,那么我预计第 packet = pcap_next(pcap_handle, header); 行会因 [=37 而崩溃=] 被设置为 NULL,或将 header 设置为 NULL,或者通过将 pcap_handle 设置为 NULL 识别为错误而使其处于未初始化状态。

在下一行中,header 被取消引用,如果未初始化或 NULL 可能会导致分段错误。

总结一下,请参阅下面添加的评论:

#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %s\n", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %s\n", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf); // << Fails and returns NULL.
    if(pcap_handle < 0){                                      // << Passes since value is NULL (typically zero).
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i++){
        printf("\n===packet %d===\n", i+1);
        packet = pcap_next(pcap_handle, header);              // << Crashes due to pcap_handle being NULL, sets header to NULL or leaves uninitialized.
        printf("Got a %d bute packet\n", header->len);        // << Dereferences pointer to nothing causing segmentation fault.
        dump(packet, header->len);
    }
    
    pcap_close(pcap_handle);
}

我建议使用以下替换代码并进行一些额外检查以增加保护:

#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %s\n", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %s\n", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
    if(pcap_handle == NULL){
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i++){
        printf("\n===packet %d===\n", i+1);
        packet = pcap_next(pcap_handle, header);
        if((packet != NULL) && (header != NULL)){
            printf("Got a %d bute packet\n", header->len);
            dump(packet, header->len);
        }
    }
    
    pcap_close(pcap_handle);
}

正如 Jonathan S. 所说,您对 pcap_open_live() 的测试是错误的。

你应该做

/*opens the packet capturing device*/
pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
if(pcap_handle == NULL){
    fprintf(stderr, "ERROR: while opening pcap device %s: %s\n", device, errbuf);
    exit(1);
}

因为:

  1. pcap_open_live() return 错误时的空指针 - 它不是 return 负数,因为它 return 不是数字;
  2. 错误指示不是 errno 值,所以 perror() 不会正确报告错误 - errbuf 包含一个描述错误的字符串,所以你想打印它以便你知道 为什么 失败了;
  3. 您还应该报告设备名称,因为这也可能有助于确定 pcap_open_live() 失败的原因。

另请注意,在大多数 UN*X 平台(Linux、*BSD、macOS 等)上,您可能需要 运行 您的程序具有提升的权限,例如 root 权限,为了打开一个网络接口进行捕获,所以如果你的程序在你修复它以使用上面的代码后报告权限错误,请尝试运行以root身份运行它。