2017-08-16 180 views
2

检查数据包,我想一个TCP SYN数据包发送到服务器,在我的机器上的端口8000。然后,我想检查服务器是否回应了SYN ACK。如果是这种情况,那么我会发送一个RST数据包来中止连接。但是,当我嗅探发送出来的SYN数据包时,它告诉我TCP报头伪长度为0,事实并非如此。顺便说一句,我用的嗅探器是tshark。这里是我的代码:伪造TCP报头长度当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的' 指令I用来嗅探分组:

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声明位于分别在系统头文件<netinet/ip.h><netinet/tcp.h>中。

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

+0

转储数据包的十六进制和告诉我们。这可能与endian问题一样简单。 –

+1

strncat()不复制字节。 –

+0

这一行:strncat((char *)buffer,(char *)pseudo_segment,12);具有254:1的失败概率,因为buffer []数组,第一个字节先前未被设置为0x00,并且数据不是char字符串AND可能包含一些0x00字节。建议使用'memcpy()' – user3629249

回答

3

我觉得你的问题是在这里

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); 
+0

非常感谢!试图弄清楚这一点,我正在撞墙。 :) – ssharma

+0

@ssharma不幸的是,在网络代码中滥用C库strxxx()调用是传奇。传说中,这是我在代码中查找的第一件事。每当你发现自己打字'str'时,你需要非常小心:) –

+0

@ssharma我自己打开和关闭了很长一段时间:-D – cleblanc