檢查數據包,我想一個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 iphdr
和struct tcphdr
聲明位於分別在系統頭文件<netinet/ip.h>
和<netinet/tcp.h>
中。
我真的失去了對於如何來解決這個問題。事實上,我不確定是什麼導致了這個問題,首先。據我所知,沒有辦法指定TCP頭的長度。任何幫助,將不勝感激。
轉儲數據包的十六進制和告訴我們。這可能與endian問題一樣簡單。 –
strncat()不復制字節。 –
這一行:strncat((char *)buffer,(char *)pseudo_segment,12);具有254:1的失敗概率,因爲buffer []數組,第一個字節先前未被設置爲0x00,並且數據不是char字符串AND可能包含一些0x00字節。建議使用'memcpy()' – user3629249