ICMP扫描程序.docx
《ICMP扫描程序.docx》由会员分享,可在线阅读,更多相关《ICMP扫描程序.docx(18页珍藏版)》请在冰豆网上搜索。
ICMP扫描程序
课程名称
网络技术课程设计
时间
2010~2011学年第二学期17~18周
学生姓名
帅哥
指导老师
刘青
题目
ICMP扫描程序的设计与实现
主要内容:
(1)了解ICMP协议,知道ICMP协议的概念,内容,重要性等。
(2)学会编写ICMP扫描程序。
要求:
(1)综合运用计算机网络基本理论和编程语言设计本系统。
(2)学会文献检索的基本方法和综合运用文献的能力。
(3)通过课程设计培养严谨的科学态度,认真的工作作风和团队协作精神。
应当提交的文件:
(1)课程设计学年论文。
(2)课程设计附件(相关图纸、设备配置清单、报告等)。
ICMP扫描程序的设计与实现
学生姓名:
帅哥指导教师:
刘青
摘要:
我们常用Ping程序来判断一个特定的主机是否处于活动状态.该程序发送一个ICMP回应请求报文给主机,然后等待返回的ICMP报文回应应答就可以知道自己是否能成功的访问到那台机器.本次课程设计涉及到MFC的应用,要在充分了解套接字的实现以及IP,ICMP的格式,功能等的基础上才能实现此设计。
关键字:
ICMP;扫描;程序;协议;
附录及源代码15
1引言
1.1课程设计目的
IP协议的优点是简单,但缺少差错控制和查询机制,而网际控制报文协议(ICMP具有补充IP功能的作用。
在网络管理中,常常要确定当前网络在红处于活动状态的主机,这时可以通过ICMP的回送和回送响应消息来完成这项工作。
这课程设计的目的就是编制程序,利用ICMP数据包,发现网络中的活动主机,即ping消息的请求和应答。
通过课程设计,熟悉ICMP报文的结构,对ICMP协议有更好的理解和认识,培养综合运用网络知识解决实际问题能力。
1.2课程设计要求
设计程序,其功能是发送ICMP数据包,以获取指定望段中的活动主机,并将结果显示在标准输出设备上程序的具体要求如下:
1.用命令形式运行
scanhost为程序名;start_ip为被搜索网段;end_ip为被搜索网段的结束IP地址。
如在命令行输入scanhost192.168.0.1192.168.0.100
2.输出格式
活动主机1的IP地址
活动主机2的IP地址
活动主机n的IP地址
2概要设计
2.1设计原理
本程序使用的原始套接字生成ICMP请求/应答报文来进行活动主机的探查。
这个程序使用的是回送请求和应答消息。
程序的大致思想是把ICMP的数据报类型设置为回送请求,将它发送给网络上的一个IP地址,如果这个IP地址已经被占用的话,那么使用位于这个IP地址的主机上的TCP/IP软件就能接受到这个ICMP回送请求,从而返回一个ICMP回送请求(类型号为0)信息。
信息封装在一个IP包中,我们需要解析该IP包,从中找到ICMP数据信息,相反,如果这个IP地址没有人使用,那么发送的ICMP回送请求在设定的延时内就不可能得到响应。
2.2数据结构设计
2.2.1.IP头部数据结构
typedefstructiphdr{
unsignedintheadlen:
4;//ip头长度
unsignedintwersion:
4;//ip版本号
unsignedchartos;//服务类型
unsignedshorttotallen;//ip包总长度
unsignedshortid;//ip号
unsignedshortflag;//标记
unsignedcharttl;//生存时间
unsignedcharprot;//协议(UDPTCP)
unsignedshortchecksum;//校验和
unsignedintsourceip;//源ip
unsignedintdestip;//目的ip
}IpHeader;
2.2.2.ICMP头部数据结构
typedefstructicmphdr{
BYTEtype;//icmp类型码,回送请求的类型码为8
BYTEcode;//子类型码,保存与特定ICMP报文类型相关的细节信息
USHORTchecksum;//校验和
USHORTid;//ICMP报文id号
USHORTseq;//ICMP数据报的序列号
}Icmpheader;
2.3系统流程图
2.3.1.主流程图(图1)
2.3.2.子流程图(图2)
3详细设计
3.1ICMP报文分析
ICMP是一种差错和控制报文协议,用于传输错误报告和控制信息。
ICMP报文分为头部和数据部分。
ICMP报文封装在IP数据报中传输。
IP报头中的类型为1时,表示报文的数据部分为ICMP报文。
虽然ICMP报文由IP报文传输,但是并不能认为ICMP是IP的上层协议,而是IP协议的有机补充。
把ICMP报文放在IP包中,是要利用IP的转发功能。
类型(TYPE)是一个字节,表示ICMP消息的类型。
代码(CODE)也是一个字节,表示报文类型的下一步信息。
校验和共有两个字节,提供对整个ICMP报文的校验和(和IP报文类型的进一步信息)。
校验和共两个字节,提供对整个ICMP报文的校验和。
按照协议的功能来分,ICMP报文可以分为
[1].ICMP差错报文
包括目的不可达报告,超时报告,参数出错报告。
[2].ICMP控制报文
包括拥塞控制和源抑制报文,路游控制和重定向报文
[3].ICMP测试报文
包括请求应答报文,时戳请求应答报文。
本课程设计就是使用ICMP请求/应答报文来测试目的主机是否存在,请求者想某特定的主机发送请求,其中包含任选的数据。
目的主机收到请求后,发送应答报文。
在同一时刻,一台机器可以同时向多台主机发送请求报文。
ICMP报文格式如图3所示,ICMP回送报文格式如下图4所示。
类型
代码
校验和
数据区(变长)
类型(8,0)码(0)
校验和
标志位
序号
任选数据
图3.ICMP报文格式图4.ICMP回应报文格式
3.2程序功能分析
在初始化原始套接字之后,本程序就要开始在一个IP网段内寻找活动主机。
因为要寻找活动的主机可能很多,为节省时间可以采用多线程编程。
结合核心代码对程序的具体进行分析。
3.2.1使用原始套接字
为了实现发送/监听ICMP抱文,必须使用原始套接字,创建原始套接字的代码如下:
SOCKETsockraw;
sockraw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,wsa_flag_overlapped);
在WSASocket函数中,我们使用IPPROTO_ICMP表示接受ICMP数据包,为了使用发送接受超时设置(设置SO_RCVTIMEO或SO_SNDTIMEO),必须将标志位置为WSA_FLAG_OVERLAPPED。
然后调用setsockopt函数设置读取迟延。
在setsockopt函数中,sockraw是之前创建的原始套接字,设置SOL_SOCKET表明使用基本套接字处理ICMP抱文。
设置SO_RCVTIMEO表示使用接受超时设置,SOSNDTIMEO表示使用发送超时设置,在这里,超时时间均设置为1000ms。
3.2.2定义IP头部和ICMP头部数据结构
由于socket发送/捕获的是IP包,因此要分别定义IP头部的数据结构和ICMP头部的数据结构。
IP头部的数据结构和ICMP头部的数据结构在概要设计中已有分析。
3.2.3填充并发送请求类型的ICMP报文
#defineICMP_ECHO8//请求回送
#defineDEF_PACKET_SIZE32//缺省数据报长度
#defineMAX_PACKET1024//最大数据报长度
#charicmp_data[MAX_PACKET];//ICMP数据报最大可能长度
Memset(icmp_data,0,MAX_PACKET)//将数据报清空初始化
Intdatasize=DEF_PACKET_SIZE;//ICMP数据报报文体的额缺省长度
Datasize+=sizeof(icmpHeader);//加上ICMP数据头部
icmp_header*icmp_hdr;
char*datapart;
icmp_hdr=(icmpheader*)icmp_data;
icmp_hdr->type=icmp_echo;//设置类型
icmp_hdr->id=(ushort)getcurrentthreadid();//设置其ID号为当前线程号
datapart=icmp_data+sizeof(icmpheader);//计算出ICMP数据报的数据部分
memset(datapart,'A',datasize-sizeof(icmphearder));//填入数据
((IcmpHeader*)icmp_data)->seq=0;//序列号
((IcmpHeader*)icmp_data)->check_sum=0;//先将检验和置0
((IcmpHead*)icmp_data)->checksum=checksum(USHORT*)icmp_data,data_size);
Checksum为计算校验和的函数,设校验和初值为0,然后对数据每16位求异或,结果取反,便得校验和。
其代码如下:
unsingedlongcksum=0;
while(size>1)
{cksum+=*buffer++;
size-=sizeof(ushort);}
if(size)
{cksum+=*(uchar)buffer;}
cksum=(cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16);
return(ushort)(~cksum);
填充ICMP报文之后,应在ICMP报文之前加上IP报头并发送出去。
可调用下面的代码发送数据包。
注意,这里的dest是填入目的主机的IP地址的一个sockaddr_in数据结构,IP_STRING是目的的主机的IP地址字符串。
Structsockaddr_in_dest;
Dest.sin_family=AF_INET;
Dest.sin_addr.s_addr=inet_addr(IP_STRING);
Sendto(sockraw,icmp_data,datasize,0,(sockaddr*)&dest,sizeof(dest));
3.2.4解析数据包
如果所ping的目的主机存在,那么它会发出一个回送应答包。
这是一个IP包,受到后解析此数据包并获得其中的ICMP信息。
根据IP报头信息中的IP报头长度字段,就可以得到ICMP报文的真实地址。
ICMP数据包中的IP地址就是活动主机的IP。
代码分析如下:
#defineICMP_MIN8
#defineMAX_PING_PACKET_SIZE(MAX_PACKET+sizeof(IpHeader))
char*recvbuf=new[MAX_PING_PACKET_SIZE];
structsockaddr_indest,from,end;
intformlen=sizeof(from);
intbytes=recvfrom(sockraw,recvbuf,MAX_PACKET,0,(Structsockaddr*)&from,&fromlen)
ipheader*iphdr;
icmpheader*icmphdr;
unsignedshortiphdrlen;
iphdr=(ipheader*)buf;
iphdrlen=iphdr->headlen*4;//IP报头的长度
icmphdr=(icmpheader*)(buf+iphdrlen);//跳过IP头
//数据包太短丢弃
if(bytes//不是回送请求(ping应答),丢弃
if(icmphdr->type!
=icmp_echo_reply)return;
//ID不相符,丢弃
if(icmphdr->id!
=(USHORT)getcurrentthreadid())return;
//输出正在使用的IP地址
cout<<"活动主机"<sin_addr)<
4测试结果
4.1遇到问题
[1]找不到头文件。
因为头文件存放位置错误。
[2]变量没有定义。
因为变量没有定义和变量名书写写错。
[3]指针书写错误。
[4]宏参数列表错误。
[5]结构体指针传递错误。
Cannotcovertfrom‘structiphdr*’to‘structicmphar*ip’
Ipheader*iphdr.因为缺少成员运算符”.”。
4.2测试结果
经反复调试,运行正常,运行结果如下(图5)
5结束语
回顾过去的一个星期,有紧张,有忙碌,有苦恼,也有欢笑,在不断的改进与努力中,终于可以实现利用ICMP发现网络上的活动主机。
通过这次课程设计,我加深了对ICMP协议的理解,巩固了课堂知识,为以后学习网络协议打下基础。
在课程设计中,我和搭档李宝详配合的很融洽,相互帮助,共同进步。
在调试过程中难免要出现一些问题,为了能够快速地确定错误的原因,尽快的排除程序逻辑错误,通常把程序错误划分为三种类型:
语法错误、运行错误和逻辑错误。
在这次网络课程设计中,也发生了这样那样的错误,如变量没有定义、缺少头文件。
通过查阅文献资料、请教老师和同学讨论,以及自己认真地分析与思考,逐一对错误进行了调试,使程序基本能正常运行,大体上符合了设计的意图和设计的要求。
由于网络协议比较抽象,比较难学,也学得不深入,何况还要把所学知识运用到实践中来,真是一大难题,所以一开始时,真是有点一筹莫展,网上查有关资料却总觉得不搭干。
通过这次课程设计,我明白做什么事都要沉得下心,在搞任何研发工作时,遇到问题沉着冷静是特别重要的,千万不能有半点浮躁的心情。
在程序的调试过程中,出现问题是正常的,关键是如何去发现问题的根源,然后去解决它。
其实写程序并不是很花时间,改错才是最花时间的的事情。
还有一点特别重要的是,在设计过程中或者是改错的过程中遇到棘手的问题时,借助网络去解决的确是一种很好的选择。
一个星期的课程设计,我过的很充实,感觉每天都在学习,每天都在进步,在课程设计完成之际,我在此向所有关心我帮助我的刘老师和同学们致以最真诚的感谢。
在这次课程设计中,我从刘老师身上学到了很多东西,他认真负责,知识丰富,要求严格,无论在理论上还是在系统调试中,都给与我很大的帮助,使我得到很大的提高,这对于我以后的工作和学习都有一种巨大的帮助,在此再次感谢刘老师耐心的耐心辅导。
参考文献
[1]吴功宜,胡晓英等著.计算机网络课程设计.北京:
机械工业出版社。
2005
[2]周明天等,TCP/IP网络原理与技术.北京:
清华大学出版社.
[3]陈坚,陈伟.VisualC++网络高级编程[M].北京:
人民邮电出版社,2001.
[4]方路平,曹平,林毅,等采用IP多址广播技术的应用系统开发[J].计算机系统应用,2001
[5]蒋东兴.WindowsSockets网络程序设计大全[M].北京:
清华大学出版社,1999.
附录及源代码
1.scanhost.h
#pragmapack(4)
#pragmacomment(lib,"Ws2_32.lib")
#definewin32_LEAN_AND_MEAN
#include
#include
#include
#include
#include
#include
//THEIPHEADER
typedefstructiphdr{
unsignedintheadlen:
4;//ip头长度
unsignedintwersion:
4;//ip版本号
unsignedchartos;//服务类型
unsignedshorttotallen;//ip包总长度
unsignedshortid;//ip号
unsignedshortflag;//标记
unsignedcharttl;//生存时间
unsignedcharprot;//协议(UDPTCP)
unsignedshortchecksum;//校验和
unsignedintsourceip;//源ip
unsignedintdestip;//目的ip
}IpHeader;
//ICMPHEADER
typedefstructicmphdr{
BYTEtype;//icmp类型码,回送请求的类型码为8
BYTEcode;//子类型码,保存与特定ICMP报文类型相关的
//节信息
USHORTchecksum;//校验和
USHORTid;//ICMP报文id号
USHORTseq;//ICMP数据报的序列号
}Icmpheader;
#defineICMP_ECHO8//请求回送
#defineICMP_ECHO_REPLY0//请求回应
#defineICMP_MIN8//ICMP包头长度(最小ICMP包长度)
#defineSTATUS_FAILED0xFFFF//错误码
#defineDEF_PACKET_SIZE32//缺省数据报长度
#defineMAX_PACKET1024//最大数据报长度
#defineMAX_PING_PACKET_SIZE(MAX_PACKET+sizeof(IpHeader))//最大接受数据报长度
voidfill_icmp_date(char*,int);//填充ICMP包
USHORTchecksum(USHORT*,int);//校验和函数
voiddecode_resp(char*,int,structsockaddr_in*);//找到此数据报IP地址
DWORDWINAPIFindIp(LPVOIDpipaddrtemp);//线程调用子函数
2.scanhost.cpp
#include"scanhost.h"
WSADATAwsadata;
SOCKETsockraw;
structsockaddr_indest,from,end;
intfromlen=sizeof(from);
char*recvbuf=newchar[MAX_PING_PACKET_SIZE];
unsignedintaddr=0;
longthreadnumcounter=0,threadnumlimit=20;
long*aa=&threadnumcounter;
voidmain(intargc,char*argv[]){
if(argc!
=3){
cout<<"输入格式错误:
scanhoststart_ipend_ip"<return;
}
if(WSAStartup(MAKEWORD(2,1),&wsadata)!
=0){
cout<<"WSAStartupfailed:
"<ExitProcess(STATUS_FAILED);
}
//创建原始套接字
sockraw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
if(sockraw==INVALID_SOCKET){
cout<<"WSASocket()failed:
"<ExitProcess(STATUS_FAILED);
}
//设置读取延时
inttimeout=1000;
intbread=setsockopt(sockraw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR){
cout<<"failtosetrecvtimeout:
"<ExitProcess(STATUS_FAILED);
}
timeout=1000;
bread=setsockopt(sockraw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
if(bread==SOCKET_ERROR){
cout<<"failedtosetsendtimeout:
"<ExitProcess(STATUS_FAILED);
}
memset(&dest,0,sizeof(dest));
unsignedlongstartip,endip;
dest.sin_family=AF_INET;
dest.sin_addr.s_addr=inet_addr(argv[1]);
startip=inet_addr(argv[1]);
end.sin_family=AF_INET;
end.sin_addr.s_addr=inet_addr(argv[2]);
endip=inet_addr(argv[2]);
HANDLEhthread;
while(htonl(startip)<=htonl(endip)){
if(threadnumcounter>threadnumlimit){
Sleep(5000);
continue;
}
DWORDThreadid;
sockaddr_in*pipaddrtemp=new(sockaddr_in);
if(!
pipaddrtemp){
cout<<"memoryallocfailed"<return;
}
*pipaddrtemp=dest;
//创建新线程
clock_tstart;
start=clock();
hthread=CreateThread(NULL,NULL,FindIp,(LPVOID)pipaddrtemp,NULL,&Threadid);
longi=60000000L;
while