【问题标题】:printing out TCP flag information from pcap从 pcap 打印出 TCP 标志信息
【发布时间】:2018-05-16 17:32:32
【问题描述】:

我正在编写一个程序来从 pcap 的标头中获取某些信息。我不确定我是否做对了。它适用于我教授的所有测试,但是,我需要注意一些隐藏的测试。这是我不确定的 TCP 标志。它在索引 47 中工作,但不知道为什么,应该是 46。(以太网标头(14)+ IPv4 标头(20)+ TCP 标头中的第 13 个字节(13)-1(考虑从 0 开始的数组)= 46)。它在 47 号点起作用是侥幸吗?

这是我的代码:

#include <pcap/pcap.h>
#include <stdlib.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
/*
 * Most of this file is the background functionality to open a capture file or to
 * open an inteface for a live capture. You can ignore all this unless you are
 * interested in an example of how pcap works.
 *
 * To use the file, simply insert your code in the "Put your code here" section and
 * create a Makefile for compilation.
 */

/* Maximum time that the OS will buffer packets before giving them to your program. */
#define MAX_BUFFER_TIME_MS (300)

/* Maximum time the program will wait for a packet during live capture.
 * Measured in MAX_BUFFER_TIME_MS units. Program closes when it expires. */
#define MAX_IDLE_TIME 100 /* 100*MAX_BUFFER_TIME_MS idle time at most */

/* Function that creates the structures necessary to perform a packet capture and
 * determines capture source depending on arguments. Function will terminate the
 * program on error, so return value always valid. */
pcap_t* setup_capture(int argc, char *argv[], char *use_file);

/* Cleanup the state of the capture. */
void cleanup_capture(pcap_t *handle);

/* Check for abnormal conditions during capture.
 * 1 returned if a packet is ready, 0 if a packet is not available.
 * Terminates program if an unrecoverable error occurs. */
char valid_capture(int return_value, pcap_t *pcap_handle, char use_file);

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

  pcap_t *pcap_handle = NULL;             /* Handle for PCAP library */
  struct pcap_pkthdr *packet_hdr = NULL;  /* Packet header from PCAP */
  const u_char *packet_data = NULL;       /* Packet data from PCAP */
  int ret = 0;                            /* Return value from library calls */
  char use_file = 0;                      /* Flag to use file or live capture */

  /* Setup the capture and get the valid handle. */
  pcap_handle = setup_capture(argc, argv, &use_file);

  /* Loop through all the packets in the trace file.
   * ret will equal -2 when the trace file ends.
   * ret will never equal -2 for a live capture. */
  ret = pcap_next_ex(pcap_handle, &packet_hdr, &packet_data);

  struct ether_header
  {
    u_int8_t  ether_dhost[6];   /* destination eth addr */
    u_int8_t  ether_shost[6];   /* source ether addr    */
    u_int16_t ether_type;               /* packet type ID field */
  };
  struct ether_header *eptr;
  char src[INET_ADDRSTRLEN];
  char dst[INET_ADDRSTRLEN];
  char src6[INET6_ADDRSTRLEN];
  char dst6[INET6_ADDRSTRLEN];

  while( ret != -2 ) {
    if( valid_capture(ret, pcap_handle, use_file) ){
      eptr = (struct ether_header *) packet_data;
      fprintf(stdout,"%s -> ",ether_ntoa((const struct ether_addr *)&eptr->ether_shost));
      fprintf(stdout,"%s \n",ether_ntoa((const struct ether_addr *)&eptr->ether_dhost));
      if(packet_data[12] == 0x08 && packet_data[13] == 0x00)
      {
        printf("    [IPv4] ");
        fprintf(stdout,"%s -> ", inet_ntop(AF_INET,(const void *)packet_data+26,src,INET_ADDRSTRLEN));
        fprintf(stdout,"%s\n", inet_ntop(AF_INET,(const void *)packet_data+30,dst,INET_ADDRSTRLEN));
        if(packet_data[23] == 0x06)
        {
          printf("    [TCP] %d -> ",packet_data[34]*256+packet_data[35]);
          printf("%d ",packet_data[36]*256+packet_data[37]);
        //  printf("%02X ",packet_data[47]); //print out value of flag;
           if(packet_data[47] & (1!=0))
             printf("FIN \n");
             else if((packet_data[47] == 0x02 || packet_data[47] == 0x12) & (2!=0))
             printf("SYN \n");

             else{
             printf("\n");
             }

        }
        else if(packet_data[23] == 0x11)
        {
          printf("    [UDP] %d -> ",packet_data[34]*256+packet_data[35]);
          printf("%d \n",packet_data[36]*256+packet_data[37]);
        }
        else{
          printf("    [%d] \n",packet_data[23]);
        }
      }
      else if(packet_data[12] == 0x86 && packet_data[13] == 0xdd)
      {

        printf("    [IPv6] ");
        printf("%s -> ", inet_ntop(AF_INET6, (const void *)packet_data+22, src6, INET6_ADDRSTRLEN));
        printf("%s \n", inet_ntop(AF_INET6, (const void *)packet_data+38, dst6, INET6_ADDRSTRLEN));

        if(packet_data[20] == 0x06)
        {
          printf("    [TCP] %d -> ",packet_data[54]*256+packet_data[55]);
          printf("%d ",packet_data[56]*256+packet_data[57]);
         // printf("%02X ",packet_data[67]); //print out value of flag

             if(packet_data[67] & (1!=0))
             printf("FIN \n");
             else if((packet_data[67] == 0x02 || packet_data[67] == 0x12) & (2!=0))
             printf("SYN \n");
             else{

          printf("\n");
          }



        }
        else if(packet_data[20] == 0x11)
        {

          printf("    [UDP] %d -> ",packet_data[54]*256+packet_data[55]);
          printf("%d \n",packet_data[56]*256+packet_data[57]);
        }
        else{
          printf("    [%d] \n",packet_data[20]);
        }
      } else {
        fprintf(stdout,"    [%d] \n",ntohs(eptr->ether_type));
      }
    }
    /* Get the next packet */
    ret = pcap_next_ex(pcap_handle, &packet_hdr, &packet_data);
  }

  cleanup_capture(pcap_handle);
  return 0;
}

pcap_t* setup_capture(int argc, char *argv[], char *use_file) {
  char *trace_file = NULL;                /* Trace file to process */
  pcap_t *pcap_handle = NULL;             /* Handle for PCAP library to return */
  char pcap_buff[PCAP_ERRBUF_SIZE];       /* Error buffer used by pcap functions */
  char *dev_name = NULL;                  /* Device name for live capture */

  /* Check command line arguments */
  if( argc > 2 ) {
    fprintf(stderr, "Usage: %s [trace_file]\n", argv[0]);
    exit(-1);
  }
  else if( argc > 1 ){
    *use_file = 1;
    trace_file = argv[1];
  }
  else {
    *use_file = 0;
  }

  /* Open the trace file, if appropriate */
  if( *use_file ){
    pcap_handle = pcap_open_offline(trace_file, pcap_buff);
    if( pcap_handle == NULL ){
      fprintf(stderr, "Error opening trace file \"%s\": %s\n", trace_file, pcap_buff);
      exit(-1);
    }
  }
  /* Lookup and open the default device if trace file not used */
  else{
    dev_name = pcap_lookupdev(pcap_buff);
    if( dev_name == NULL ){
      fprintf(stderr, "Error finding default capture device: %s\n", pcap_buff);
      exit(-1);
    }

    /* Use buffer length as indication of warning, per pcap_open_live(3). */
    pcap_buff[0] = 0;

    pcap_handle = pcap_open_live(dev_name, BUFSIZ, 1, MAX_BUFFER_TIME_MS, pcap_buff);
    if( pcap_handle == NULL ){
      fprintf(stderr, "Error opening capture device %s: %s\n", dev_name, pcap_buff);
      exit(-1);
    }
    if( pcap_buff[0] != 0 ) {
      printf("Warning: %s\n", pcap_buff);
    }

    printf("Capturing on interface '%s'\n", dev_name);
  }

  return pcap_handle;

}

void cleanup_capture(pcap_t *handle) {
  /* Close the trace file or device */
  pcap_close(handle);
}

char valid_capture(int return_value, pcap_t *pcap_handle, char use_file) {
  static int idle_count = 0;  /* Count of idle periods with no packets */
  char ret = 0;               /* Return value, invalid by default */

  /* A general error occurred */
  if( return_value == -1 ) {
    pcap_perror(pcap_handle, "Error processing packet:");
    cleanup_capture(pcap_handle);
    exit(-1);
  }

  /* Timeout occured for a live packet capture */
  else if( (return_value == 0) && (use_file == 0) ){
    if( ++idle_count >= MAX_IDLE_TIME ){
      printf("Timeout waiting for additional packets on interface\n");
      cleanup_capture(pcap_handle);
      exit(0);
    }
  }

  /* Unexpected/unknown return value */
  else if( return_value != 1 ) {
    fprintf(stderr, "Unexpected return value (%i) from pcap_next_ex()\n", return_value);
    cleanup_capture(pcap_handle);
    exit(-1);
  }
  /* Normal operation, packet arrived */
  else{
    idle_count = 0;
    ret = 1;
  }

  return ret;
}

这里有一些示例打印输出:(左边是教授的结果,右边是我的,我有额外的打印输出来查看数组中那个位置的内容)。谢谢

0:0:86:5:80:da -> 0:60:97:7:69:ea                           0:0:86:5:80:da -> 0:60:97:7:69:ea 
    [IPv6] 3ffe:507:0:1:200:86ff:fe05:80da -> 3ffe:501:410:0:2c0:dfff:fe47:33e          [IPv6] 3ffe:507:0:1:200:86ff:fe05:80da -> 3ffe:501:410:0:2c0:dfff:fe47:33e 
    [TCP] 1022 -> 22 SYN                                |       [TCP] 1022 -> 22 02 SYN 
0:60:97:7:69:ea -> 0:0:86:5:80:da                           0:60:97:7:69:ea -> 0:0:86:5:80:da 
    [IPv6] 3ffe:501:410:0:2c0:dfff:fe47:33e -> 3ffe:507:0:1:200:86ff:fe05:80da          [IPv6] 3ffe:501:410:0:2c0:dfff:fe47:33e -> 3ffe:507:0:1:200:86ff:fe05:80da 
    [TCP] 22 -> 1022 SYN                                |       [TCP] 22 -> 1022 12 SYN 

【问题讨论】:

  • 你能证明你所做的“数学”是为了达到你认为标志应该在的索引吗?
  • 他的数学是正确的。从以太网帧开始到 TCP 标志的偏移量(假设它是一个没有 IP 选项的 TCP 数据包)是 47. 14 + 20 + 13
  • 是的,这解释了为什么 47 是正确的,但我在一个从 0 开始的数组中访问这些。我想我需要减去 1。
  • 看看下面我的回答。它从基于零的偏移数组/帧/数据包中为您解决所有问题
  • 很酷,谢谢,我现在正在检查它。我已经得到了以太网信息、IP 信息和 UDP 信息。这只是 TCP 标头的最后一步(已经获得源端口和目标端口)。也只需要 SYN 和 FIN 标志。

标签: c networking hex pcap


【解决方案1】:

以下是定位 TCP 标志的方法:

如果我们假设我们谈论的是以太网,以太网帧头将是 14 字节:一个 6 字节的目标,后跟一个 6 字节的源,然后是一个 2 字节的以太网类型(对于 802.3/SNAP/Ethernet II,即最有可能)

如果从帧开始偏移 12/13 处的 Ethertype 包含 0x0800,则您正在查看 TCP/IP。

 if(frame[12]==0x08 && frame[13]==0x00) { /* IP packet inside */ }

假设您有一个 IP Ethertype,下一个字节将包含两个半字节大小的字段:IP 版本号(对您来说可能是 0x40),然后是 IP 标头长度(可能是 0x05)。将这些半字节放在一起,您将在该字段中找到 0x45。检查该字段非常重要。你可以像这样屏蔽上半字节:

  ihl = frame[14]&0x0f;

获取 IP 标头长度字段。这个数字将告诉您在哪里可以找到下一个协议层的标头。通常你会在这里有一个 5(20 字节的头),但如果有 IP 选项,这个数字会更大。让我们取这个数字并从这里计算:

  embedded_protocol_header = frame[ihl * 4];

接下来,您应该验证您确实有一个 TCP 数据包。这可以通过检查 IP 标头中的字节偏移 9 来验证:

  ip_header_start = frame[14];
  embedded_protocol = ip_header_start[9];
  if(embedded_protocol == 6) { tcp_header = embedded_protocol_header; }

现在我们知道它是 TCP,我们可以获取 TCP 标志。这些将位于 TCP 标头中的偏移量 13:

  tcp_flags = tcp_header[13];

要检查 SYN/ACK 位,您可以屏蔽其他所有内容:

  synack = tcp_flags & 0x3f;

您现在可以检查它是否是 SYN ACK:

  if(synack == 0x12) { /* SYN and ACK were set */

您可能想知道上面的 0x3f 掩码。原因是如果系统支持 ECN,则 TCP 标志中的两个高位用于 ECN。如果支持,ECN 协商会在这些位和 IP 标头的 TOS 字节(差分服务字节)中的两个低位位的 3 次握手期间发生。与其处理所有可能的情况,最简单的方法是完全关闭这些位并检查是否仍有 SYN 和 ACK。

【讨论】:

  • 好的,synack == 0x12 的 0x12 代表什么?我以为 0x01 是 FIN,而 0x02 是 SYN。
  • 0x01 是 FIN。 0x02 是 SYN。 0x10 是确认。我误读并认为您正在尝试查找 SYN ACK 数据包,即 0x10 | 0x02 = 0x12
  • 你真的应该创建一组条件来单独测试 TCP 标志中的每个位,以打印出在给定数据包中启用的完整标志集。
  • 好的,非常感谢您的帮助和解释。最后一个问题,tcp_flags 变量是否内置在 tcp.h 中,所以它已经包含了 pcap 的所有信息?
  • 你必须看看。我不知道。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 2013-02-13
  • 1970-01-01
相关资源
最近更新 更多