ICMP协议简介:
ICMP协议是TCP/IP 族的一个子协议,属于网络层协议。全称:Internet Control Message Protocol。(网络控制消息协议)。ICMP协议是一种面向无连接的协议,用于传输出错报告控制信息。用于检测网络存活状态
使用:
1、用于主机扫描技术:ICMP Echo扫描技术
实现原理:ICMP的ping机制在判断主机是否开机在线非常有效。向目标主机发送ICMP Echo Request(type 8)数据包,等待回复的ICMP Echo Reply(type 0)。如果能收到回复,则表示主机处于存活状态;否则表明主机不在线或发送或者返回包被对方过滤掉了。
优点:简单,系统支持
确定:容易被防火墙限制。通过设置防火墙规则可以屏蔽IMCP数据包
2、系统识别
同样,通过发送ping指令,将返回的TTL值进行分析,获取到对方操作系统的类别,具体操作系统类型TTL表如下:
操作系统 默认TTL值
windows NT 107
Windows2000 108
Windows 9x 127 或 128
Linux 240或241
Solaris 252
Lrix 240

具体代码实现:
ping.h

#pragma once//在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h
//要么将#include <winsock2.h>放在#include<windows.h>前面或者直接去掉#include<windows.h>/*
system32中有icmp.dll文件,其中包含函数:
IcmpCreateFile()
IcmpSendEcho(8个参数)
IcmpCloseHandle
*/#include <winsock2.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "WS2_32")    // 链接到WS2_32.lib#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0struct IPHeader
{BYTE m_byVerHLen; //4位版本+4位首部长度BYTE m_byTOS; //服务类型USHORT m_usTotalLen; //总长度USHORT m_usID; //标识USHORT m_usFlagFragOffset; //3位标志+13位片偏移BYTE m_byTTL; //TTLBYTE m_byProtocol; //协议USHORT m_usHChecksum; //首部检验和ULONG m_ulSrcIP; //源IP地址ULONG m_ulDestIP; //目的IP地址
};struct ICMPHeader
{BYTE m_byType; //类型BYTE m_byCode; //代码USHORT m_usChecksum; //检验和 USHORT m_usID; //标识符USHORT m_usSeq; //序号ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)
};struct PingReply
{USHORT m_usSeq;DWORD m_dwRoundTripTime;DWORD m_dwBytes;DWORD m_dwTTL;
};class CPing
{
public:CPing();~CPing();BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);BOOL Ping(const char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);
private:BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);USHORT CalCheckSum(USHORT *pBuffer, int nSize);ULONG GetTickCountCalibrate();
private:SOCKET m_sockRaw;WSAEVENT m_event;USHORT m_usCurrentProcID;char *m_szICMPData;BOOL m_bIsInitSucc;
private:static USHORT s_usPacketSeq;
};

ping.cpp

#include "ping.h"
#include <iostream>
USHORT CPing::s_usPacketSeq = 0;CPing::CPing() :m_szICMPData(NULL),m_bIsInitSucc(FALSE)
{WSADATA WSAData;//WSAStartup(MAKEWORD(2, 2), &WSAData);if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0){/*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/printf("WSAStartup() failed: %d\n", GetLastError());return;}m_event = WSACreateEvent();m_usCurrentProcID = (USHORT)GetCurrentProcessId();//setsockopt(m_sockRaw);/*if ((m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0)) != SOCKET_ERROR){WSAEventSelect(m_sockRaw, m_event, FD_READ);m_bIsInitSucc = TRUE;m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));if (m_szICMPData == NULL){m_bIsInitSucc = FALSE;}}*/m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);if (m_sockRaw == INVALID_SOCKET){std::cerr << "WSASocket() failed:" << WSAGetLastError ()<< std::endl;  //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。}else{WSAEventSelect(m_sockRaw, m_event, FD_READ);m_bIsInitSucc = TRUE;m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));if (m_szICMPData == NULL){m_bIsInitSucc = FALSE;}}
}CPing::~CPing()
{WSACleanup();if (NULL != m_szICMPData){free(m_szICMPData);m_szICMPData = NULL;}
}BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{return PingCore(dwDestIP, pPingReply, dwTimeout);
}BOOL CPing::Ping(const char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{if (NULL != szDestIP){DWORD dwIP;int nRet = inet_pton(AF_INET, szDestIP, &dwIP);if (nRet != 1){printf("inet_pton: szDestIP IPV4 error:%d", GetLastError());return false;}return PingCore(/*inet_addr(szDestIP)*/dwIP, pPingReply, dwTimeout);}return FALSE;
}BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{//判断初始化是否成功if (!m_bIsInitSucc){return FALSE;}//配置SOCKETsockaddr_in sockaddrDest;sockaddrDest.sin_family = AF_INET;sockaddrDest.sin_addr.s_addr = dwDestIP;int nSockaddrDestSize = sizeof(sockaddrDest);//构建ICMP包int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);ULONG ulSendTimestamp = GetTickCountCalibrate();USHORT usSeq = ++s_usPacketSeq;memset(m_szICMPData, 0, nICMPDataSize);ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;pICMPHeader->m_byType = ECHO_REQUEST;pICMPHeader->m_byCode = 0;pICMPHeader->m_usID = m_usCurrentProcID;pICMPHeader->m_usSeq = usSeq;pICMPHeader->m_ulTimeStamp = ulSendTimestamp;pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);//发送ICMP报文if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR){return FALSE;}//判断是否需要接收相应报文if (pPingReply == NULL){return TRUE;}char recvbuf[256] = { "\0" };while (TRUE){//接收响应报文if (WSAWaitForMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT){WSANETWORKEVENTS netEvent;WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);if (netEvent.lNetworkEvents & FD_READ){ULONG nRecvTimestamp = GetTickCountCalibrate();int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);if (nPacketSize != SOCKET_ERROR){IPHeader *pIPHeader = (IPHeader*)recvbuf;USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文&& pICMPHeader->m_byType == ECHO_REPLY //是ICMP响应报文&& pICMPHeader->m_usSeq == usSeq //是本次请求报文的响应报文){pPingReply->m_usSeq = usSeq;pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);pPingReply->m_dwTTL = pIPHeader->m_byTTL;return TRUE;}}}}//超时if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout){return FALSE;}}
}USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
{unsigned long ulCheckSum = 0;while (nSize > 1){ulCheckSum += *pBuffer++;nSize -= sizeof(USHORT);}if (nSize){ulCheckSum += *(UCHAR*)pBuffer;}ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);ulCheckSum += (ulCheckSum >> 16);return (USHORT)(~ulCheckSum);
}ULONG CPing::GetTickCountCalibrate()
{static ULONG s_ulFirstCallTick = 0;static LONGLONG s_ullFirstCallTickMS = 0;SYSTEMTIME systemtime;FILETIME filetime;GetLocalTime(&systemtime);SystemTimeToFileTime(&systemtime, &filetime);LARGE_INTEGER liCurrentTime;liCurrentTime.HighPart = filetime.dwHighDateTime;liCurrentTime.LowPart = filetime.dwLowDateTime;LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;if (s_ulFirstCallTick == 0){s_ulFirstCallTick = GetTickCount();}if (s_ullFirstCallTickMS == 0){s_ullFirstCallTickMS = llCurrentTimeMS;}return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}

main.cpp

/*
ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,属于网络层协议, 用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用*/#include <winsock2.h>
#include <stdio.h>
#include "ping.h"
#include "CommDef.h"
int main(void)
{CPing objPing;const char *szDestIP = "127.0.0.1";PingReply reply;printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE);while (TRUE){objPing.Ping(szDestIP, &reply);char szOSType[32] = { 0 };switch (reply.m_dwTTL){case TTL_LINUX1:case TTL_LINUX2:sprintf_s(szOSType, "LINUX");break;case TTL_WINDOWS_2000:sprintf_s(szOSType, "Windows 2000");break;case TTL_WINDOWS_9X1:case TTL_WINDODWS_9X2:sprintf_s(szOSType, "Windows9X");break;case TTL_SOLARIS:sprintf_s(szOSType, "Solaris");break;default:sprintf_s(szOSType, "Unknown");break;}printf("Reply from %s: bytes=%d time=%ldms TTL=%ld OSType=%s\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL, szOSType);Sleep(500);}return 0;
}