【问题标题】:Bogus TCP Header Length When Examining Packets in Tshark在 Tshark 中检查数据包时伪造的 TCP 标头长度
【发布时间】:2018-01-24 22:16:25
【问题描述】:

我试图将 TCP SYN 数据包发送到我机器上端口 8000 上的服务器。然后,我想检查服务器是否以SYN ACK 响应。如果是这种情况,那么我会发回一个RST 数据包以中止连接。但是,当我嗅探我发送的SYN 数据包时,它告诉我 TCP 标头的虚假长度为 0,但事实并非如此。顺便说一句,我使用的嗅探器是tshark。这是我的代码:

main 函数中,我运行这个:

FLAGS f = SYN;

tcp_scan("127.0.0.1",8000,f,0);

这个函数组装IP头:

struct iphdr* assemble_ip(char* dest,unsigned int proto) {

    /* Assemble IP Layer */

    struct iphdr* iph;

    iph = malloc(sizeof(struct iphdr)); // allocate memory

    if (iph == NULL) { // if the ip header is NULL

         err();
         return NULL;

    }

    srand((unsigned int)time(NULL)); // seed random number generator

    /* Hardcoded values */

    iph->version = 4; // the version
    iph->tos = 0; // type of services
    iph->ihl = 5; // internet header length
    iph->id = htons(rand() % 65536);  // random id
    iph->ttl = rand() % 257; // ttl
    iph->frag_off = 0; // fragment offset

    if (iph->ttl < 64) iph->ttl += 64; // if TTL is not sufficient

    iph->tot_len = htons(iph->ihl*4); // the internet header length

    /* User defined values */

    iph->saddr = inet_addr(client); // source address
    iph->daddr = inet_addr(dest); // destination address
    iph->protocol = proto; // protocol

    iph->check = 0; // set to zero for later calculation

    return iph;

    }

此函数组装 TCP 标头:

struct tcphdr* assemble_tcp(unsigned int sport,unsigned int dport,FLAGS f) {

    /* Assemble TCP layer */

    struct tcphdr* tcph;

    tcph = malloc(sizeof(struct tcphdr)); // allocate tcp header

    if (tcph == NULL) { // if tcp is NULL

         err();
         return NULL;

    }


    bzero(tcph,sizeof(struct tcphdr));

    srand((unsigned int)time(NULL)); // seed random number generator

    /* Hardcoded values */

    tcph->seq = htonl(rand() % 65001); // generate random sequence number
    tcph->ack_seq = 0; // ack sequence should be 0
    tcph->doff = 5; // set data offset
    tcph->window = htons(rand() % 65536); // set window size

    /* Increase values by random value above 64 */

    if (ntohs(tcph->seq) < 64) tcph->seq += (rand() % 101 + 64);
    if (ntohs(tcph->window) < 64) tcph->window += (rand() % 101 + 64);

    /* User-defined values */

    tcph->source = htons(sport); // source port
    tcph->dest = htons(dport);  // destination port
    tcph = set_flags(tcph,f); // set the TCP flags

    /* Set urgent ptr if URG flag is set*/

    if (tcph->urg == 1) tcph->urg_ptr = 1;
    else tcph->urg_ptr = 0;

    tcph->check = 0; // set the checksum to 0 for other calculations


    return tcph;

    }

另外,我确实计算了标头的校验和。出于我的目的,在计算校验和时,IP 标头总是 20 字节长,因为我没有发送任何数据或选项。这意味着标题中有 10 个 16 位字。由于我没有添加任何选项或数据,因此 TCP 标头也将有 20 个字节长。代码如下:

unsigned short ip_checksum(struct iphdr* iph) {

    /* Acquire IP checksum */

    /*

    Checksum for Internet Protocol:
    One's complement of the one's complement sum of the 16 bit words in the header.
    So we get the first 16 bits of the header then add it to the sum, and then
    we get the next 16 bits, and add it, and so on.
    ...0100101010110101 -> "..." represents more bits 
    1111111111111111 -> this is 131071 in base 10 
    0000100101010110101 -> notice how the "..." bits are now 0's

    */

    /* One's complement sum */

    unsigned long long* ptr;
    unsigned long long hdr;
    unsigned short sum = 0;
    unsigned long mask = 131071;

    ptr = (unsigned long long*)iph;  // cast structure
    hdr = *ptr; // get hdr 

    for (int i = 0; i < 10; i++) { // 20 bytes -> 160 bits / 16 bits = 10 words

            sum += (hdr & mask); // add to sum

            hdr >>= 16; // shift the next 16 bits

  }

  sum = ~sum; // inverse 

  return sum;

  }

TCP 校验和:

  unsigned short tcp_checksum(struct tcphdr* tcph,struct iphdr* iph)  {

          /* Calculate TCP checksum */

          struct pseudo_hdr* pseudo_hdr;
          u_char* buffer;
          u_char* segment;
          u_char* pseudo_segment;
          unsigned short sum = 0;
          unsigned long mask = 131071;
          unsigned long long* ptr;
          unsigned long long hdr;

          pseudo_hdr = malloc(sizeof(struct pseudo_hdr)); // allocate memory
          buffer = malloc(32); // allocate for 32 bytes of information 

          if (pseudo_hdr == NULL || buffer == NULL) { // if memory wasn't allocated properly

              err();
              if (pseudo_hdr != NULL) free(pseudo_hdr);
              if (buffer != NULL) free(buffer);
              return 0;

          }

          pseudo_hdr->saddr = (unsigned long)iph->saddr; // we add the  cast because the fields if of type u_int_32
          pseudo_hdr->daddr = (unsigned long)iph->daddr; // same reason for adding the cast as above
          memset(&pseudo_hdr->reserved,0,8); // set these 8 bits to 0
          pseudo_hdr->proto = IPPROTO_TCP; // this will always be 6
          pseudo_hdr->len = htons(tcph->doff*4); // length of tcp header

          /* Place both headers into a buffer */

          segment = (u_char*)tcph; 
          pseudo_segment = (u_char*)pseudo_hdr; 

          /* Concactenate */

          strncat((char*)buffer,(char*)pseudo_segment,12); // first the pseudo header 
          strncat((char*)buffer,(char*)segment,20); // then the TCP segment 

          /* Calculate checksum just like IP checksum */

          ptr = (unsigned long long*)buffer; // convert buffer 

          hdr = *ptr; // dereference for clarity in following clode

          for (int i = 0; i < 16; i++) { // 32 bytes -> 256 bits / 16 bits = 16 words

               sum += (hdr & mask); // apply mask to header and add to sum

               hdr >>= 16;  // shift the next 16 bits

           }


           sum = ~sum; // bitwise NOT operation

           return sum;

           };

这是我上面提到的所有功能:

int tcp_scan(char* ipaddr,unsigned int port,FLAGS f,unsigned int justsend) {

    /* Do a TCP port scan */

    u_char* buffer;
    u_char recvbuf[65535];
    u_char* ipbuf;
    u_char* tcpbuf;
    int s;
    size_t bufsize;
    size_t size;
    struct sockaddr_in sa;
    struct sockaddr_in recvstruct;
    struct msghdr msg;
    struct iovec iv[1];
    struct iphdr* iph;
    struct tcphdr* tcph;
    FLAGS rst = RST;

    srand((unsigned int)time(NULL)); // seed random number generator

    bufsize = sizeof(struct tcphdr) + sizeof(struct iphdr); // store size in variable

    buffer = malloc(bufsize); // allocate memory to buffer
    iph = assemble_ip(ipaddr,IPPROTO_TCP); // set the ip address to provided and protocol as TCP
    tcph = assemble_tcp(rand() % 65536,port,f); // set flag, source port as rand, and dest port as supplied port num

    if (iph == NULL || tcph == NULL || buffer == NULL) { // if error occurs

           err();

           /* Deallocate memory to variables that still have it */

           if (iph != NULL) free(iph);
           if (tcph != NULL) free(tcph);
           if (buffer != NULL) free(buffer);

           return -1;

     }

           /* Now compute checksum */

           iph->check = htons(ip_checksum(iph));
           tcph->check = htons(tcp_checksum(tcph,iph)); 

           /* Store headers in buffer */

           ipbuf = (u_char*)iph;
           tcpbuf = (u_char*)tcph;

           /* Concactenate to buffer */

           strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
           strncat((char*)buffer,(char*)ipbuf,20); // do same thing but with ip header

           /* Create a socket */

           s = create_socket(); // create a raw socket

           if (s == -1) return -1;  // if the socket wasn't able to be created


           /* Clear memory */

           bzero(&sa,sizeof(struct sockaddr_in));
           bzero(&recvstruct,sizeof(struct sockaddr_in));
           bzero(&msg,sizeof(struct msghdr));
           bzero(&iv,sizeof(struct iovec));

           /* For analyze_packet() */

           sa.sin_family = AF_INET; // address family
           sa.sin_addr.s_addr = inet_addr(ipaddr); // convert ip address
           sa.sin_port = htons(port); // port number

           /* For sendmsg() */

          iv[0].iov_base = buffer;
          iv[0].iov_len = bufsize;

          msg.msg_name = &sa; // caller allocated buffer
          msg.msg_namelen = sizeof(struct sockaddr_in); // specify size of buffer
          msg.msg_iov = iv; // iov structure array
          msg.msg_iovlen = 1; // the length of the array
          msg.msg_control = NULL; // for ancillary data
          msg.msg_controllen = 0; // sizeof ancillary data


          if (sendmsg(s,&msg,0) == -1) {

              err();
              return -1;

          }

          printf("Sent\n");

          if (justsend) return 0; // exit cleanly

          bzero(&recvstruct,sizeof(struct sockaddr_in)); // clear structure

          size = sizeof(struct sockaddr_in); // acquire size of recv structure

          for(int i = 0; i < 100; i++) { // loop until we've received 100 packets

                 printf("Receiving\n");
                 bzero(recvbuf,65535); // clear memory

                 if (recvfrom(s,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&recvstruct,(socklen_t*)&size) == -1) { // recv

                      err();
                      return -1;

                 }

                 if (analyze_packet(recvbuf,sa,recvstruct) == 0) { // if packet is what we wanted

                      printf("\ttcp %d is open\n",port); // print out that port is opened
                      tcp_scan(ipaddr,port,rst,-1); // abort connection with RST flag
                      break;

                  }


             }

           return 0;

}

好的,现在你已经看到了,这是我用来嗅探数据包的“tshark”命令:

sudo tshark -o tcp.check_checksum:TRUE # I also wanted to check the checksum value to make sure it was OK

下面是运行程序的命令:

sudo ./netmap enp0s3 # enp0s3 is the interface I'm sending packets on

在单独的终端中运行这两个之后,tshark 提供以下输出:

1    0.000000    10.0.2.15 -> 127.0.0.1    TCP 74 31280->8000 [<None>] Seq=1 Win=0, bogus TCP header length (0, must be 20)

请注意struct iphdrstruct tcphdr 的声明分别位于系统头文件&lt;netinet/ip.h&gt;&lt;netinet/tcp.h&gt; 中。

我真的不知道如何解决这个问题。事实上,我不确定是什么导致了这个问题,首先。据我所知,没有办法指定 TCP 标头的长度。任何帮助将不胜感激。

【问题讨论】:

  • 转储数据包的十六进制并向我们展示。它可能就像字节序问题一样简单。
  • strncat() 不复制字节。
  • 这一行:strncat((char*)buffer,(char*)pseudo_segment,12);失败的概率为 254:1,因为 buffer[] 数组,第一个字节之前没有设置为 0x00 并且数据不是 char 字符串并且可能包含一些 0x00 字节。建议使用memcpy()
  • 是的,在我被通知使用memcpy() 后,我已经解决了这个问题。谢谢。

标签: c wireshark tcp-ip tshark


【解决方案1】:

我认为你的问题在这里

strncat((char*)buffer,(char*)tcpbuf,20); // copy only 20 bytes...this ensures that no extra bytes are catted
strncat((char*)buffer,(char*)ipbuf,20);

标头不是字符串,因此您可能只复制每个标头的一部分。试试这样的;

memcpy((char*)buffer, (char*)tcpbuf, 20);
memcpy((char*)buffer+20, (char*)ipbuf, 20);

【讨论】:

  • 非常感谢!我正用头撞墙试图弄清楚这一点。 :)
  • @ssharma 不幸的是,在网络代码中滥用 C 库 strxxx() 调用已成为传奇。太传奇了,这是我在你的代码中寻找的第一件事。任何时候你发现自己在输入“str”时都需要非常小心:)
  • @ssharma 我自己看了好一阵子:-D
  • 注意:硬编码的长度和偏移量是一个非常糟糕的主意。建议使用:`sizeof(struct tcphdr)sizeof(struct iphdr)。此外,一些代码期望这些长度之一是 32 字节,而不是 20
猜你喜欢
  • 2014-06-05
  • 2019-04-29
  • 2013-07-06
  • 2016-12-29
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-17
相关资源
最近更新 更多