ping命令是使用非常普遍的用于诊断网络连通性的工具,ping命令使用了icmp协议,icmp的完整拼写是Internet Control Message Protocol,即网络控制消息协议。
在ICMP中关于用来探测网络连通性的部分有两个,一个是类型,一个是代码,通过这两个代码完成了echo request和echo reply,也就是请求和应答。当类型为8,代码为0时,icmp进行的是echo request,当类型为0,代码也为0时,icmp进行的是echo reply。
通过抓包可以很容易的得到验证,打开Wireshark,然后在协议过滤的位置输入“icmp”,也就是查看关于ICMP的包。设置好Wireshark后,在cmd下ping其他主机的地址,然后观察Wireshark中的抓包情况,分别查看echo request和echo reply的包如下:
上面的图片是echo request的包,可以看到type为8,code为0;下面的图片是echo reply的包,可以看到type为0,code也为0。
关于ICMP的过多的细节就不讨论了,下面通过C来介绍ICMP的填充。
ICMP的定义如下:
// ICMP Header typedef struct _ICMPHEADER { unsigned char i_type; unsigned char i_code; unsigned short i_chsum; unsigned short i_id; unsigned short i_seq; unsigned long i_timestamp; unsigned char i_data[32]; }ICMPHEADER, *PICMPHEADER;
上面就是ICMP在C语言中的定义,下面介绍ICMP结构体的填充,代码如下:
// 填充ICMP包头信息 memset((LPVOID)&icmpHeader, ‘A‘, sizeof(icmpHeader)); icmpHeader.i_type = 8; // icmp echo icmpHeader.i_code = 0; icmpHeader.i_chsum = 0; icmpHeader.i_id = (unsigned short)GetCurrentProcessId(); icmpHeader.i_seq = 0; icmpHeader.i_timestamp = (unsigned long)::GetTickCount();
代码是基于Windows系统的,在代码中id使用了当前进程的ID,时间戳使用了GetTickCount()这个API函数来进行填充。在上面的填充当中,有一个位置是错误的,就是对i_chsum进行的赋值,该值是一个校验和,在进行校验和计算前,将该值赋值为0进行计算。计算校验和算法的代码如下:
unsigned short *buf = (unsigned short *)&icmpHeader; // 计算校验和 int nLeft = sizeof(icmpHeader); int sum = 0; unsigned short crc = 0; // 每两个字节进行循环相加 while ( nLeft > 1 ) { sum = sum + *(buf ++); nLeft -= 2; } // 如果还有一个字节没加,也加上 if ( nLeft == 1 ) { *(unsigned char *)(&crc) = *(unsigned char *)buf; sum += crc; } // 因为是16位,如果相加后超过16位 // 那么高16位与低16位相加 sum = (sum >> 16) + (sum & 0xffff); // 上一步相加如果再次超过16位 // 那么在进行一次高16位与低16位相加 sum += (sum >> 16); // 取反得到最终的CRC crc = ~sum; // 填充校验和 icmpHeader.i_chsum = crc;
校验和的是16位的无符号整型,将每个字进行相加保存到一个32位的正向当中,然后让这个32位的高16位与低16位进行相加,然后取反即是最后的校验和。将校验和填充至ICMP结构体中。
以上就是关于ICMP的填充,与ICMP校验和的计算过程,至于实现ping的话,那么使用socket创建原始套接字将构造好的ICMP进行发送即可。
本文出自 “无觉的BLoG” 博客,请务必保留此出处http://wujue.blog.51cto.com/11999347/1877520
原文地址:http://wujue.blog.51cto.com/11999347/1877520