网络攻击第一步是扫描目标机的开放端口,其原理是(摘自 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; }; //参数是目标机的IP int 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为非阻塞,连接不成功立即返回-1 if (!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; }