ping程序分析报告word文档良心出品.docx
《ping程序分析报告word文档良心出品.docx》由会员分享,可在线阅读,更多相关《ping程序分析报告word文档良心出品.docx(19页珍藏版)》请在冰豆网上搜索。
ping程序分析报告word文档良心出品
ping程序分析报告
姓名:
班级:
学号:
1、Ping功能简介…………………………………………………1
2、程序流程图
(1)主函数流程图…………………………………………………3
(2)创建套接字流程图……………………………………………3
(3)建立IP选项头部流程图……………………………………4
(4)创建SockRaw套接字的接收/发送时限属性流程图…………4
(5)判断终端的主机名获取信息流程图…………………………5
(6)分配堆内存流程图……………………………………………5
(7)接收/发送ICMP数据包流程图………………………………6
(8)清空Socket库所占内存……………………………………7
(9)传参解析函数流程图…………………………………………7
(10)解析IP选项函数流程图……………………………………8
3、源代码清单……………………………………………………8
4、心得体会………………………………………………………16
1、ping功能简介
Ping是DOS命令,一般用于检测网络通与不通,也叫时延,其值越大,速度越慢
PING(PacketInternetGrope),因特网包探索器,用于测试网络连接量的程序。
Ping发送一个ICMP回声请求消息给目的地并报告是否收到所希望的ICMP回声应答。
它是用来检查网络是否通畅或者网络连接速度的命令。
作为一个生活在网络上的管理员或者黑客来说,ping命令是第一个必须掌握的DOS命令,它所利用的原理是这样的:
网络上的机器都有唯一确定的IP地址,我们给目标IP地址发送一个数据包,对方就要返回一个同样大小的数据包,根据返回的数据包我们可以确定目标主机的存在,可以初步判断目标主机的操作系统等。
Ping是Windows系列自带的一个可执行命令。
利用它可以检查网络是否能够连通,用好它可以很好地帮助我们分析判定网络故障。
应用格式:
PingIP地址。
该命令还可以加许多参数使用,具体是键入Ping按回车即可看到详细说明。
ping指的是端对端连通,通常用来作为可用性的检查,
但是某些病毒木马会强行大量远程执行ping命令抢占你的网络资源,导致系统变慢,网速变慢。
2、程序流程图
图1main函数流程图
图2创建套接字流程图
图3建立IP选项头部流程图
图4创建SockRaw套接字的接收/发送时限属性流程图
图5判断终端的主机名获取信息流程图
图6分配堆内存流程图
图7接收/发送ICMP数据包流程图
图8清空Socket库所占内存
图9传参解析函数流程图
图10解析IP首部函数流程图
3、源代码清单
//ModuleName:
Ping.c
#include"windows.h"
#include"winsock.h"
#include"stdio.h"
#defineIP_RECORD_ROUTE0x7//IP记录路由
#defineICMP_ECHO8//ICMP回显
#defineICMP_ECHOREPLY0//ICMP回显应答
#defineICMP_MIN8//ICMP数据包最小长度
#defineDEF_PACKET_SIZE32//差错报文长度
#defineMAX_PACKET0x10000//ICMP包最大长度
#defineMAX_IP_HDR_SIZE60//IP首部最大字节数
//IP头文件定义
typedefstruct_iphdr
{
unsignedinth_len:
4;//头部长度4字节
unsignedintversion:
4;//IP版本号IPv4
unsignedchartos;//服务类型
unsignedshorttotal_len;//数据包总长度
unsignedshortident;//ID标识
unsignedshortfrag_and_flags;//3位标志,13位片偏移
unsignedcharttl;//生存期
unsignedcharproto;//协议类型
unsignedshortchecksum;//IP头部的检验和
unsignedintsourceIP;//源地址
unsignedintdestIP;//目的地址
}IpHeader;
//ICMP头部定义
typedefstruct_icmphdr
{
BYTEi_type;//ICMP类型(8位)
BYTEi_code;//代码类型(8位)
USHORTi_cksum;//头部及数据检验和(16位)
USHORTi_id;ID//标识
USHORTi_seq;//序列号
ULONGtimestamp;//时间戳
}IcmpHeader;
//IP选项首部定义
typedefstruct_ipoptionhdr
{
unsignedcharcode;//IP选项的类型
unsignedcharlen;//RR选项总字节长度
unsignedcharptr;//指针字段
unsignedlongaddr[9];//IP地址清单
}IpOptionHeader;
BOOLbRecordRoute;
intdatasize;
char*lpdest;
//定义3个全局变量
//使用信息
voidusage(char*progname)
{
printf("usage:
ping-r[datasize]\n");
printf("-rrecordroute\n");
printf("hostremotemachinetoping\n");
printf("datasizecanbeupto0x10000Byte\n");
ExitProcess(-1);//结束进程
}
//ICMP首部初始化
voidFillICMPData(char*icmp_data,intdatasize)
{
IcmpHeader*icmp_hdr=NULL;
char*datapart=NULL;//指针定义及初始化
icmp_hdr=(IcmpHeader*)icmp_data;
icmp_hdr->i_type=ICMP_ECHO;//ICMP回显请求
icmp_hdr->i_code=0;
icmp_hdr->i_id=(USHORT)GetCurrentProcessId();//取得当前进程号
icmp_hdr->i_cksum=0;//检验和字段置0
icmp_hdr->i_seq=0;
datapart=icmp_data+sizeof(IcmpHeader);//datapart指针指向数据报文开头
memset(datapart,'E',datasize-sizeof(IcmpHeader));//填充数据段
}
//计算检验和
USHORTchecksum(USHORT*buffer,intsize)
{
unsignedlongcksum=0;//检验和字段置0
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(USHORT);
}
if(size)
{
cksum+=*(UCHAR*)buffer;
}
cksum=(cksum>>16)+(cksum&0xffff);//将检验和字段高16位右移16位再与低16位相加
cksum+=(cksum>>16);//将所加的检验和再与剩余低16位相加
return(USHORT)(~cksum);//检验和取反,并返回
}
//解析IP选项
voidDecodeIPOptions(char*buf,intbytes)
{
IpOptionHeader*ipopt=NULL;
IN_ADDRinaddr;//声明结构体
inti;
HOSTENT*host=NULL;
ipopt=(IpOptionHeader*)(buf+20);//去掉IP首部,指针指向数据选项首部
printf("RR:
");
for(i=0;i<(ipopt->ptr/4)-1;i++)
{
inaddr.S_un.S_addr=ipopt->addr[i];
if(i!
=0)
{
printf("");
}
host=gethostbyaddr((char*)&inaddr.S_un.S_addr,sizeof(inaddr.S_un.S_addr),AF_INET);//通过IP地址获得主机信息
if(host)
{
printf("(%-15s)%s\n",inet_ntoa(inaddr),host->h_name);
//打印IP地址和主机名
}
else
{
printf("(%-15s)\n",inet_ntoa(inaddr));//打印IP地址
}
}
return;
}
//解析ICMP首部函数
voidDecodeICMPHeader(char*buf,intbytes,structsockaddr_in*from)
{
IpHeader*iphdr=NULL;
IcmpHeader*icmphdr=NULL;
unsignedshortiphdrlen;
DWORDtick;//毫秒级数
staticinticmpcount=0;
iphdr=(IpHeader*)buf;
iphdrlen=iphdr->h_len*4;//IP首部实际长度
tick=GetTickCount();//获得毫秒级数
if((iphdrlen==MAX_IP_HDR_SIZE)&&(!
icmpcount))//判断是否为一个IP数据包
{
DecodeIPOptions(buf,bytes);//调用IP选项解析函数
}
if(bytes{
printf("Toofewbytesfrom%s\n",
inet_ntoa(from->sin_addr));
}
icmphdr=(IcmpHeader*)(buf+iphdrlen);//指针指向ICMP报文首部
if(icmphdr->i_type!
=ICMP_ECHOREPLY)//判断ICMP类型是否为ICMP回显应答
{
printf("nonechotype%drecvd\n",icmphdr->i_type);//输出其类型
return;
}
if(icmphdr->i_id!
=(USHORT)GetCurrentProcessId())//获得当前进程的ID,判断是否为ICMP标识
{
printf("someoneelse'spacket!
\n");
return;
}
printf("%dbytesfrom%s:
",bytes,inet_ntoa(from->sin_addr));
printf("icmp_seq=%d.",icmphdr->i_seq);//输出ICMP序列号
printf("time:
%dms",tick-icmphdr->timestamp);//打印时间戳
printf("\n");
icmpcount++;
return;
}
//传参解析函数
voidValidateArgs(intargc,char**argv)
{
inti;
bRecordRoute=FALSE;赋初值
lpdest=NULL;
datasize=DEF_PACKET_SIZE;//初始化datasize,使其等于差错报文长度
for(i=1;i{
if((argv[i][0]=='-')||(argv[i][0]=='/'))//判断数组argv第i行第0列是否为‘-’或‘/’
{
switch(tolower(argv[i][1]))//将数组argv第i行第1列的字符转化为小写字母
{
case'r':
//记录路由选项
bRecordRoute=TRUE;
break;
default:
usage(argv[0]);调用usage函数
break;
}
}
elseif(isdigit(argv[i][0]))//判断数组argv第i行第0列是否为数字
{
datasize=atoi(argv[i]);//将数组argv第i行的字符转化成长整型数
}
else
{
lpdest=argv[i];
}
}
}
//main函数
intmain(intargc,char**argv)//argc代表命令行中的参数个数,**argv是指向字符串的指针
{
char*icmp_data=NULL;
char*recvbuf=NULL;
USHORTseq_no=0;
//定义及初始化
structsockaddr_indest={'\0'};
structsockaddr_infrom={'\0'};
structhostent*hp=NULL;
//声明结构体
intbread=0;
intret=0;
intfromlen=sizeof(from);
inttimeout=1000;
unsignedintaddr=0;
//定义及初始化
WSADATAwsaData;//声明数据结构
SOCKETsockRaw=INVALID_SOCKET;
IpOptionHeaderipopt={'\0'};
if(WSAStartup(MAKEWORD(2,2),&wsaData)!
=0)//指明程序请求使用的socket版本,返回请求的版本信息
{
printf("WSAStartup()failed:
%d\n",GetLastError());//获得当前的进程错误号,并输出“请求版本信息失败”
return2;
}
ValidateArgs(argc,argv);//调用传参解析函数
sockRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//创建sockRaw套接字
if(sockRaw==INVALID_SOCKET)//创建失败
{
printf("WSASocket()failed:
%d\n",WSAGetLastError());
return3;//返回值3,并退出main函数
}
if(bRecordRoute)
{
ZeroMemory(&ipopt,sizeof(ipopt));
ipopt.code=IP_RECORD_ROUTE;
ipopt.ptr=4;//指向第一个地址列表
ipopt.len=39;
ret=setsockopt(sockRaw,IPPROTO_IP,IP_OPTIONS,
(char*)&ipopt,sizeof(ipopt));//设置sockRaw套接字属性
if(ret==SOCKET_ERROR)//设置属性失败
{
printf("setsockopt(IP_OPTIONS)failed:
%d\n",
WSAGetLastError());//取得当前错误进程号,并输出“属性设置失败”
}
}
bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,
(char*)&timeout,sizeof(timeout));//设置套接字接收时限属性
if(bread==SOCKET_ERROR)//设置失败
{
printf("setsockopt(SO_RCVTIMEO)failed:
%d\n",
WSAGetLastError());
return-1;返回值-1,并退出main函数
}
timeout=1000;
bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,
(char*)&timeout,sizeof(timeout));//设置套接字发送时限属性
if(bread==SOCKET_ERROR)//设置发送时限失败
{
printf("setsockopt(SO_SNDTIMEO)failed:
%d\n",
WSAGetLastError());
return-1;返回值-1,并退出main函数
}
memset(&dest,0,sizeof(dest));//填充数据段,
dest.sin_family=AF_INET;//指定地址族
if((dest.sin_addr.s_addr=inet_addr(lpdest))==INADDR_NONE)
//网络地址是无效的地址
{
if((hp=gethostbyname(lpdest))!
=NULL)
{
memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
dest.sin_family=hp->h_addrtype;将hp->h_addrtype的类型赋给dest.sin_family
printf("dest.sin_addr=%s\n",inet_ntoa(dest.sin_addr));//输出IP地址
}
else
{
printf("gethostbyname()failed:
%d\n",
WSAGetLastError());
return-1;退出main函数,并返回值-1
}
}
datasize+=sizeof(IcmpHeader);
icmp_data=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);//分配堆内存
recvbuf=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
if(!
icmp_data)//分配失败
{
printf("HeapAlloc()failed:
%d\n",GetLastError());//获得当前错误进程号,输出“堆内存分配失败”
return-1;//退出main函数,并返回值-1
}
memset(icmp_data,0,MAX_PACKET);//填充数据段
FillICMPData(icmp_data,datasize);//调用ICMP初始化函数
while
(1)
{
staticintnCount=0;//初始化
intbwrote;
if(nCount++==4)
{
break;结束整个循环
}
((IcmpHeader*)icmp_data)->i_cksum=0;
((IcmpHeader*)icmp_data)->timestamp=GetTickCount();//设定时间戳
((IcmpHeader*)icmp_data)->i_seq=seq_no++;//设置ICMP头部序列号
((IcmpHeader*)icmp_data)->i_cksum=
checksum((USHORT*)icmp_data,datasize);//调用计算检验和函数,将其返回值赋给i_cksum
bwrote=sendto(sockRaw,icmp_data,datasize,0,
(structsockaddr*)&dest,sizeof(dest));//发送ICMP数据包
if(bwrote==SOCKET_ERROR)//发送失败
{
if(WSAGetLastError()==WSAETIMEDOUT)//当前错误进程号为超时
{
printf("timedout\n");
continue;//结束本次循环
}
printf("sendto()failed:
%d\n",WSAGetLastError());
return-1;//退出main函数,并返回值-1
}
if(bwrote{
printf("Wrote%dbytes\n",bwrote);
}
bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(structsockaddr*)&from,&fromlen);//接收ICMP数据包
if(bread==SOCKET_ERROR)//接收失败
{
if(WSAGetLastError()==WSAETIMEDOUT)//当前错误进程号为超时
{
printf("timedout\n");
continue;//结束本次循环
}
printf("recvfrom()failed:
%d\n",W