在 Tshark 中检查数据包时伪造的 TCP Header 长度

Bogus TCP Header Length When Examining Packets in Tshark

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

main函数中,我运行这个:

FLAGS f = SYN;

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

此函数组装IP header:

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 header:

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;

    }

此外,我计算了 header 的校验和。出于我的目的,在计算校验和时,IP header 始终为 20 字节长,因为我没有发送任何数据或选项。这意味着 header 中有 10 个 16 位字。 TCP header 也将是 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 的声明分别位于系统 header 文件 <netinet/ip.h><netinet/tcp.h> 中。

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

我觉得你的问题出在这里

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

header 不是字符串,因此您只能复制每个 header 的一部分。尝试这样的事情;

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