【问题标题】:C - Writing structs to a file (.pcap)C - 将结构写入文件 (.pcap)
【发布时间】:2011-09-05 14:59:06
【问题描述】:

我正在尝试编写一个 .pcap 文件,它可以在 Wireshark 中使用。 为了做到这一点,我有几个需要写入文件的具有各种数据类型的结构。 (见代码)

所以,我创建了结构实例,填写数据,使用 FILE* fp = fopen("test.pcap","w"),然后我不确定如何正确地将其写入文件。我相信我应该使用 memcpy,但我不确定最好的方法。过去,我主要使用 C++ 库来执行此操作。有什么建议吗?

typedef struct pcap_hdr_s {
        uint32_t magic_number;   /* magic number */
        uint16_t version_major;  /* major version number */
        uint16_t version_minor;  /* minor version number */
        int32_t  thiszone;       /* GMT to local correction */
        uint32_t sigfigs;        /* accuracy of timestamps */
        uint32_t snaplen;        /* max length of captured packets, in octets */
        uint32_t network;        /* data link type */
} pcap_hdr_t;

typedef struct pcaprec_hdr_s {
   uint32_t ts_sec;         /* timestamp seconds */
   uint32_t ts_usec;        /* timestamp microseconds */
   uint32_t incl_len;       /* number of octets of packet saved in file */
   uint32_t orig_len;       /* actual length of packet */
} pcaprec_hdr_t;

typedef struct ethernet_hdr_s {
   uint8_t dst[6];    /* destination host address */
   uint8_t src[6];    /* source host address */
   uint16_t type;     /* IP? ARP? RARP? etc */
} ethernet_hdr_t;

typedef struct ip_hdr_s {
   uint8_t  ip_hl:4, /* both fields are 4 bits */
            ip_v:4;
   uint8_t        ip_tos;
   uint16_t       ip_len;
   uint16_t       ip_id;
   uint16_t       ip_off;
   uint8_t        ip_ttl;
   uint8_t        ip_p;
   uint16_t       ip_sum;
   uint32_t ip_src;
   uint32_t ip_dst;
}ip_hdr_t;

typedef struct udp_header
{
  uint16_t src;
  uint16_t dst;
  uint16_t length;
  uint16_t checksum;
} udp_header_t;

【问题讨论】:

  • 注意字节顺序——我认为捕获的数据包总是使用网络字节顺序,但您可能需要检查标头的字节顺序。如果文件格式使用magic_number 字段来确定标头的字节顺序,则可能不是问题。
  • 幻数让你可以用大端或小端写入,如果你愿意,你甚至可以在每个数据包后来回切换。幻数还会提醒您因文件传输不当造成的损坏。
  • 幻数让你以big-endian或little-endian顺序写出数据包data;你必须按照它出现在线路上的顺序写出来,对于以太网、IP、TCP 和 UDP 多字节整数字段,它是大端的。它确实让您可以按照本机字节顺序写出文件中的字段并记录标题,但您不妨让 libpcap/WinPcap 完成这项工作。而且,遗憾的是,幻数不受例如在 Windows 和 UN*X 之间以 ASCII 模式 FTP 文件的影响。

标签: c struct io fopen pcap


【解决方案1】:

使用 libpcap 或 WinPcap - pcap_open_dead() 获取“假”pcap_tpcap_dump_open() 一起使用以指定链路层标头类型(对于以太网,使用 DLT_EN10MB)和快照长度(使用 65535) ,pcap_dump_open() 打开文件进行写入,pcap_dump() 写入数据包,pcap_dump_close() 关闭文件。 比直接使用 fopen()fwrite()fclose()(这是 libpcap/WinPcap“在后台”使用的)更容易。

而且,是的,您必须正确获取数据包中的字节顺序。字节顺序取决于协议;对于以太网标头中的type 字段,以及IP、TCP 和UDP 标头中的所有多字节字段,它们必须采用大端顺序。 (pcap 文件中的幻数与此无关 - 它仅指示文件头和每个数据包记录头中字段的字节顺序,NOT数据包,以及,由于它在 Linux 中的实现方式,Linux USB 中数据包开头的元数据会被捕获。数据包数据应该看起来与“在线”完全一样。)

【讨论】:

  • 你能分享“更多代码”吗?我的意思是写一个例子?
【解决方案2】:

使用fwrite()。您需要检查此信息,但我认为 .pcap 文件是以 binary 模式编写的。

例子:

pcaprec_hdr_t pcaprec_hdr;
// fill pcaprec_hdr with valid info

FILE* pFile = NULL;
pFile = fopen ("myfile.pcap" , "wb"); // open for writing in binary mode

fwrite (&pcaprec_hdr, 1, sizeof(pcaprec_hdr_t) , pFile);

fclose(pFile);

【讨论】:

  • -1 忘记写出文件头并且不只是说“使用libpcap/WinPcap,他们已经知道如何写出文件头和记录头”。
  • 你假设他们已经知道了。但是他们呢?将这个答案选为官方证明了其他东西。
  • “你假设他们已经知道了。但他们知道吗?”是的,他们有。它们是该文件格式的第一个实现。
  • 我遇到了麻烦,你能用“完整”的例子编辑你的答案吗?当我在 Wireshark 中打开我的 pcap 文件时,我收到错误消息 The capture file appears to have been cut short in the middle of a packet. 我只能看到“框架”行。谢谢。
【解决方案3】:

这是我对 Guy Harris 建议的理解。因此,根据 Kyslik 的要求,我们有:

#include <libpcap/pcap.h>

/* Ethernet/IP/SCTP INIT chunk */
static const unsigned char pkt1[82] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */
  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, /* ......E. */
  0x00, 0x44, 0x55, 0xb1, 0x00, 0x00, 0x40, 0x84, /* .DU...@. */
  0x26, 0x83, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, /* &....... */
  0x00, 0x01, 0x00, 0x01, 0x1f, 0x90, 0x00, 0x00, /* ........ */
  0x00, 0x00, 0x68, 0xe5, 0x88, 0x1b, 0x01, 0x00, /* ..h..... */
  0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, /* .$...... */
  0xa0, 0x00, 0x00, 0x04, 0xff, 0xff, 0x00, 0x00, /* ........ */
  0x16, 0x2e, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, /* ........ */
  0x00, 0x04, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x05, /* ........ */
  0x00, 0x00                                      /* .. */
};


int main(int argc, char *argv[]) {

  pcap_t *handle = pcap_open_dead(DLT_EN10MB, 1 << 16);
  pcap_dumper_t *dumper = pcap_dump_open(handle, "/tmp/pktcap/cap.pcap");

  struct pcap_pkthdr pcap_hdr;
  pcap_hdr.caplen = sizeof(pkt1);
  pcap_hdr.len = pcap_hdr.caplen;

  pcap_dump((u_char *)dumper, &pcap_hdr, pkt1);
  pcap_dump_close(dumper);

  return 0;
}

【讨论】:

  • 我必须将 pcap_pkthdr pcap_hdr; 修改为 struct pcap_pkthdr pcap_hdr; 才能使用 gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 进行编译
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-08
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
  • 2016-01-26
相关资源
最近更新 更多