Linux下UDP端口掃描


由於UDP協議是非面向連接的,對UDP端口的探測也就不可能像TCP端口的探測那樣依賴於連接建立過程,這也使得UDP端口掃描的可靠性不高。所以雖然UDP協議較之TCP協議顯得簡單,但是對UDP端口的掃描卻是相當困難的。下面具體介紹一下UDP掃描方案:
        方案1:利用ICMP端口不可達報文進行掃描
        本方案的原理是當一個UDP端口接收到一個UDP數據報時,如果它是關閉的,就會給源端發回一個ICMP端口不可達數據報,ICMP協議中的類型字段爲3(目的站點不可達)和代碼字段爲3(表示端口不可達);如果它是開放的,那麼就會忽略這個數據報,也就是將它丟棄而不返回任何的信息,這裏可以設置超時來進行判斷。

        優點:
        可以完成對UDP端口的探測。
        缺點:
        需要系統管理員的權限。掃描結果的可靠性不高。因爲當發出一個UDP數據報而沒有收到任何的應答時,有可能因爲這個UDP端口是開放的,也有可能是因爲這個數據報在傳輸過程中丟失了。另外,掃描的速度很慢。原因是在RFC1812的中對ICMP錯誤報文的生成速度做出了限制。例如Linux就將ICMP報文的生成速度限制爲每4秒鐘80個,當超出這個限制的時候,還要暫停1/4秒。   
        方案2:UDP   recvfrom()和write()掃描   
        本方案實際上是對前一個方案的一些改進,目的在於解決方案1中所需要的系統管理員的權限問題。由於只有具備系統管理員的權限纔可以查看ICMP錯誤報文,那麼在不具備系統管理員權限的時候可以通過使用recvfrom( )和write( )這兩個系統調用來間接獲得對方端口的狀態。對一個關閉的端口第二次調用write()的時候通常會得到出錯信息。而對一個UDP端口使用recvfrom調用的時候,如果系統沒有收到ICMP的錯誤報文通常會返回一個EAGAIN錯誤,錯誤類型碼13,含義是“再試一次(Try   Again)”;如果系統收到了ICMP的錯誤報文則通常會返回一個ECONNREFUSED錯誤,錯誤類型碼111,含義是“連接被拒絕(Connect   refused)”。通過這些區別,就可以判斷出對方的端口狀態如何。
        優點:
        不需要系統管理員的權限。
        缺點:
        除去解決了權限的問題外,其他問題依然存在。


實現代碼:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>


#define MAXPACKET 4096
#define DEFAULT_TIMEOUT 10
#define DEFAULT_RESEND 6


void start_scanning(const char* ipaddress,unsigned short port)
{
unsigned int timeout=4, maxretry=3;
struct sockaddr_in myudp;
int udpsock, rawsock, retry, retval,iplen;
fd_set r;
struct timeval mytimeout;
struct icmp *packet;
struct ip *iphdr;
struct servent *service;
unsigned char recvbuff[MAXPACKET];




if((udpsock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
perror("socket()");
exit(-1);
}


if((rawsock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < 0)
{
perror("socket()");
exit(-1);
}
mytimeout.tv_sec = timeout;
mytimeout.tv_usec = 0;
retry = 0;


myudp.sin_addr.s_addr = inet_addr(ipaddress);
myudp.sin_family = AF_INET;
myudp.sin_port = htons(port);


while(retry++ < maxretry)
{
FD_ZERO(&r);
FD_SET(rawsock,&r);
if((sendto(udpsock,"  ",2,0,(struct sockaddr *)&myudp,sizeof(myudp))) < 0)
{
perror("sendto");
exit(-1);
}
retval = select((rawsock+1),&r,NULL,NULL,&mytimeout);
if(retval ==1)
{//some data reach
if((recvfrom(rawsock,recvbuff,sizeof(recvbuff),0x0,NULL,NULL)) < 0)
{
perror("Recv");
exit(-1);
}
iphdr = (struct ip *)recvbuff;
iplen = iphdr->ip_hl * 4;
packet = (struct icmp *)(recvbuff + iplen);
printf("the icmp type is=%d, code=%d \n",packet->icmp_type,packet->icmp_code);
if((packet->icmp_type == ICMP_UNREACH) && (packet->icmp_code == ICMP_UNREACH_PORT))
break;
} //end if(retval ==1)
else if(retval ==0)
{
printf("time out! the port may be availed !\n");
continue;
}
else
{
printf("occur some errors! scan udp failed !\n");
return ;
}
} //end while(1)


if(retry >= maxretry)
{
if((service = getservbyport(htons(port),"udp")) == NULL)
fprintf(stdout,"Unknown port %u, open.\n",port);
else
fprintf(stdout,"UDP service %s open.\n",service->s_name);
fflush(stdout);
}
else
{
printf("the port:%d is unavailable!\n",port);
}


}


int main(int argc,char **argv)
{
start_scanning("192.168.2.1",27015);
printf("scan over\n");
return 0;




}


 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章