1 引题
这里提出一个概念:什么是主机扫描?主机扫描顾名思义就是扫描网络中存在的主机。那怎么扫描特定的主机是否存在呢?答案就是通过发送ICMP协议包来确定。那什么是ICMP包呢?
我打个不恰当的比方,ICMP包就好比邮局的快递,你要扫描一个主机是否存在,就向这个主机的地址(IP)发一个快递,不管是否投递成功,邮局都会通知你。那么你就知道该网络地址是否存在了。那么邮局的回单,我要如何分析才能知道对方的信息呢?那么我们就要来分析ICMP这个‘快递’包的投送回单。
具体的结构解释我就不多说了,网上很多,可以自己查阅
在Linux中ICMP数据结构()定义如下:
struct icmp { u_int8_t icmp_type; /* type of message, see below */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement checksum of struct */ union { u_char ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* gateway address */ struct ih_idseq /* echo datagram */ { u_int16_t icd_id; u_int16_t icd_seq; } ih_idseq; u_int32_t ih_void;
/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { u_int16_t ipm_void; u_int16_t ipm_nextmtu; } ih_pmtu;
struct ih_rtradv { u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union { struct { u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime; } id_ts; struct { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; u_int8_t id_data[1]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_radv #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data };
|
那么现在的问题我们怎么自己组装ICMP包,也就是我们的包裹呢?答案在下面void make_icmp_packet(struct icmp *icmp,int len,int n)
{
memset((char *)icmp,0,len);
gettimeofday((struct timeval*)(icmp->icmp_data),(struct timezone*)0 );
//生成ICMP报头
icmp->icmp_type=ICMP_ECHO;
icmp->icmp_code=0;
icmp->icmp_id=getpid();
icmp->icmp_seq=n;
//icmp->icmp_cksum=0;//进行检查和,要或不要均可
icmp->icmp_cksum=checksum((u_short*)icmp,len);
}
这里有一个校验和的程序,我在网上down了一个,大家就这么用好了u_short checksum(u_short *data,int len)
{
u_long sum=0;
for(;len>1;len-=2)
{
sum+=*data++;
if(sum & 0x80000000)
sum=(sum & 0xffff)+(sum>>16);
}
if(len==1)
{
u_short i=0;
*(u_char*)(&i)=*(u_char*)data;
sum+=i;
}
while(sum>>16)
{
sum=(sum & 0xffff)+(sum>>16);
}
return (sum==0xffff)?sum:~sum;
}
需要扫描,那么自然需要一个定时器,做超时控制void tvsub(struct timeval* out,struct timeval *in)
{
out->tv_sec-=in->tv_sec;
}
好了,一切就绪,我们现在可以开始我们的主机扫描之旅了#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PACK_LEN 72
#define BUFSIZE 4096
int main(int argc, char *argv[])
{
struct sockaddr_in send_sa;
//struct s;
int i=0,j=0;
int scan_icmp_socket;
char send_buff[PACK_LEN];
char recv_buff[BUFSIZE];
struct in_addr start_addr,end_addr;
struct timeval tv;
fd_set readfd_set;
struct ip *ip;
struct icmp *icmp;
int hlen; //报头 长度
send_sa.sin_family=AF_INET;
scan_icmp_socket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if (scan_icmp_socket<0)
{
perror("scan_icmp_socket:");
return -1;
}
char ip_addr[17];
//循环ip地址
for (j=1;j<254;j++)
{
memset(ip_addr,'\0',sizeof(ip_addr));
sprintf(ip_addr,"%s.%d",argv[1],j);
send_sa.sin_addr.s_addr=inet_addr(ip_addr);
printf("scan %s\n",inet_ntoa(send_sa.sin_addr));
fflush(stdout);
for (i=0;i<3 ;i++ )
{
make_icmp_packet((struct icmp*)send_buff,PACK_LEN,i);
if ( (sendto(scan_icmp_socket,send_buff,PACK_LEN,0,(struct sockaddr*)&send_sa,sizeof(send_sa)))<0 )
{
perror("sendto");
}
//设定超时0.4妙
tv.tv_sec=0;
tv.tv_usec=400*1000;
FD_ZERO(&readfd_set);
FD_SET(scan_icmp_socket,&readfd_set);
while(1)
{
if (select(scan_icmp_socket+1,&readfd_set,NULL,NULL,&tv)<=0)
{
break;
}
//等待0.4妙后如果数据有回应那么就开始接收包含ICMP包的IP报
if (recvfrom(scan_icmp_socket,recv_buff,BUFSIZE,0,NULL,NULL)<=0)
{
perror("recvfrom:");
exit(0);
}
ip=(struct ip*)recv_buff;
//获得IP数据包长度;
hlen=ip->ip_hl<<2;
//根据IP的源ip是否和自己相同判断是否是自己收到的包
if (ip->ip_src.s_addr==send_sa.sin_addr.s_addr)
{
icmp=(struct icmp*)(recv_buff+hlen);
//解析icmp包内容信息
if (icmp->icmp_type==ICMP_ECHOREPLY)
{
printf("%-15s",inet_ntoa( *(struct in_addr*)
&(ip->ip_src.s_addr)));
//获得当前系统时间后与ICMP包内的数据内容对比
gettimeofday(&tv,NULL);
tvsub(&tv,(struct timeval*) (icmp->icmp_data));
printf(":RTT=%8.4f ms\n",tv.tv_sec+tv.tv_usec/1000.0);
break;
}
else
{
printf("ICMP STATE:%d\n",icmp->icmp_type);
}
}
}
}
}
close(scan_icmp_socket);
return 0;
}
至此,我们主机扫描的原理和实现已经讲完了,那么主机扫描有什么用?
没错,主机是否可到达是网络安全测试的第一步内容。而且,每次有人发送ICMP包,你的电脑都
要应答。如果很多人同时发送ICMP包达到你的电脑,也就是说邮局同时送来了1000个包裹,你可能就有些应付不了了吧。这时候的状态就是你的电脑上网很
卡很卡,或者根本上不了。那么我们怎么防范ICMP攻击呢,或者我不想别人知道我的电脑到底在不在上网?很简单,装个防火墙,将拒绝ICMP勾选上就可以
了。
其实说了半天,ICMP多半用来DOS攻击,这种攻击多半基于单机,带宽小,效率低,对于被扫描者不会造成很大的危害。然后,目前的网络攻击多半是类似云计算的形式,当成千上万的肉鸡向你发动攻击的时候,那可不是好玩的。
阅读(1997) | 评论(0) | 转发(0) |