網路攻擊第一步是掃描目標機的開放連接埠,其原理是(摘自http://www.pconline.com.cn/pcjob/nettech/safe/others/0502/557020_1.html):
根據TCP協議規範,當一台電腦收到一個TCP串連建立請求報文(TCP SYN)的時候,做這樣的處理:
1、 如果請求的TCP連接埠是開放的,則回應一個TCP ACK報文,並建立TCP串連控制結構(TCB);
2、 如果請求的TCP連接埠沒有開放,則回應一個TCP RST(TCP頭部中的RST標誌設為1)報文,告訴發起電腦,該連接埠沒有開放。
相應地,如果IP協議棧收到一個UDP報文,做如下處理:
1、 如果該報文的目標連接埠開放,則把該UDP報文送上層協議(UDP)處理,不回應任何報文(上層協議根據處理結果而回應的報文例外);
2、 如果該報文的目標連接埠沒有開放,則向發起者回應一個ICMP不可達報文,告訴發起者電腦該UDP報文的連接埠不可達。
利用這個原理,攻擊者電腦便可以通過發送合適的報文,判斷目標電腦哪些TCP或UDP連接埠是開放的,過程如下:
1、 發出連接埠號碼從0開始依次遞增的TCP SYN或UDP報文(連接埠號碼是一個16位元的數字,這樣最大為65535,數量很有限);
2、 如果收到了針對這個TCP報文的RST報文,或針對這個UDP報文的ICMP不可達報文,則說明這個連接埠沒有開放;
3、 相反,如果收到了針對這個TCP SYN報文的ACK報文,或者沒有接收到任何針對該UDP報文的ICMP報文,則說明該TCP連接埠是開放的,UDP連接埠可能開放(因為有的實現中可能不回應ICMP不可達報文,即使該UDP連接埠沒有開放)。
這樣繼續下去,便可以很容易的判斷出目標電腦開放了哪些TCP或UDP連接埠,然後針對連接埠的具體數字,進行下一步攻擊,這就是所謂的連接埠掃描攻擊。
根據以上原理,我編寫了以下代碼(只進行了TCP掃描):
#include<stdlib.h>#include<stdio.h>#include<sys/socket.h>#include<netdb.h>#include<string.h>#include<unistd.h>#include<netinet/in.h>#include<arpa/inet.h>#include<fcntl.h>#include<time.h>#include<sys/types.h>#define TIMEOUT 5 //由於把socket設定為非阻塞,使用select函數,觀察其 //5秒後的是否串連成功,串連不成功則認為其連接埠沒有開放struct servenet{ char * s_name;char** s_aliases;int s_port;char* s_proto;};//參數是目標機的IPint main(int argc,char** argv){struct sockaddr_in server;int ret;int len;int scanport; int start_port=0; int end_port=1024;int sockfd;fd_set rset;fd_set wset; struct servenet *sp; for(scanport=start_port;scanport<end_port;scanport++) { if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){perror("can not create socket\n");exit(1);}memset(&server,0,sizeof(struct sockaddr_in));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(argv[1]);server.sin_port = htons(scanport); int flag = fcntl(sockfd, F_GETFL,0);fcntl(sockfd,F_SETFL, flag|O_NONBLOCK);struct timeval tm;tm.tv_sec = TIMEOUT;tm.tv_usec = 0; //connect為非阻塞,串連不成功立即返回-1if (!connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))){ sp=getservbyport(htons(scanport),"tcp"); printf("tcp port %d open:%s\n ",scanport,sp->s_name); }//假如串連不成功,則運行select,直到逾時else {FD_ZERO(&rset);FD_ZERO(&wset);FD_SET(sockfd, &rset);FD_SET(sockfd, &wset);int error; //錯誤碼int len = sizeof(error); //5秒後查看socket的狀態變化 if (select(sockfd+1,&rset,&wset,NULL,&tm)>0){getsockopt(sockfd, SOL_SOCKET, SO_ERROR,&error, &len );if(error == 0)printf("Port %d is opened\n", scanport);}}close(sockfd);}return 0;}