2017-08-16 82 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