c语言ping命令windows,Windows C语言 icmp ping命令探测主机存活 笔记

之前在VS2015 2017 2019一直编译不通过,不知道是一些什么奇奇怪怪的问题,但过了几天,编译又成功了,很奇怪,但至少目前是没问题的,先贴一下MSDN上面给的示例代码吧#include 

#include 

#include 

#include 

#pragma comment(lib, "iphlpapi.lib")

#pragma comment(lib, "ws2_32.lib")

int __cdecl main(int argc, char **argv)  {

// Declare and initialize variables

HANDLE hIcmpFile;

unsigned long ipaddr = INADDR_NONE;

DWORD dwRetVal = 0;

char SendData[32] = "Data Buffer";

LPVOID ReplyBuffer = NULL;

DWORD ReplySize = 0;

// Validate the parameters

if (argc != 2) {

printf("usage: %s IP address\n", argv[0]);

return 1;

}

ipaddr = inet_addr(argv[1]);

if (ipaddr == INADDR_NONE) {

printf("usage: %s IP address\n", argv[0]);

return 1;

}

hIcmpFile = IcmpCreateFile();

if (hIcmpFile == INVALID_HANDLE_VALUE) {

printf("\tUnable to open handle.\n");

printf("IcmpCreatefile returned error: %ld\n", GetLastError() );

return 1;

}

ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);

ReplyBuffer = (VOID*) malloc(ReplySize);

if (ReplyBuffer == NULL) {

printf("\tUnable to allocate memory\n");

return 1;

}

dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),

NULL, ReplyBuffer, ReplySize, 1000);

if (dwRetVal != 0) {

PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;

struct in_addr ReplyAddr;

ReplyAddr.S_un.S_addr = pEchoReply->Address;

printf("\tSent icmp message to %s\n", argv[1]);

if (dwRetVal > 1) {

printf("\tReceived %ld icmp message responses\n", dwRetVal);

printf("\tInformation from the first response:\n");

}

else {

printf("\tReceived %ld icmp message response\n", dwRetVal);

printf("\tInformation from this response:\n");

}

printf("\t  Received from %s\n", inet_ntoa( ReplyAddr ) );

printf("\t  Status = %ld\n",

pEchoReply->Status);

printf("\t  Roundtrip time = %ld milliseconds\n",

pEchoReply->RoundTripTime);

}

else {

printf("\tCall to IcmpSendEcho failed.\n");

printf("\tIcmpSendEcho returned error: %ld\n", GetLastError() );

return 1;

}

return 0;

}

这个程序的核心就是一个IcmpSendEcho函数,IcmpSendEcho函数像目标主机发送一个IPv4的ICMP响应请求。这个函数在IcmpAPI.h头文件中定义如下IPHLPAPI_DLL_LINKAGE

DWORD

WINAPI

IcmpSendEcho(

_In_                HANDLE                   IcmpHandle,

_In_                IPAddr                   DestinationAddress,

_In_reads_bytes_(RequestSize)   LPVOID                   RequestData,

_In_                WORD                    RequestSize,

_In_opt_              PIP_OPTION_INFORMATION   RequestOptions,

_Out_writes_bytes_(ReplySize)   LPVOID                   ReplyBuffer,

_In_range_(>=, sizeof(ICMP_ECHO_REPLY) + RequestSize + 8)

DWORD                    ReplySize,

_In_                DWORD                    Timeout

);

有点凌乱,MSDN官网上面的更简洁一点IPHLPAPI_DLL_LINKAGE DWORD IcmpSendEcho(

HANDLE                 IcmpHandle,

IPAddr                 DestinationAddress,

LPVOID                 RequestData,

WORD                   RequestSize,

PIP_OPTION_INFORMATION RequestOptions,

LPVOID                 ReplyBuffer,

DWORD                  ReplySize,

DWORD                  Timeout

);

参数介绍

1、IcmpHandle

是一个句柄,HANDLE声明,返回IcmpCreateFile()函数的打开的ICMP文件句柄

2、DestinationAddress

目标IPv4地址,采用IPAddr结构的形式,也就是inet_addr(ip),如inet_addr("127.0.0.1")的返回值,作用是将点分十六进制转换为小端序形式的十六进制数。

3、RequestData

请求数据的地址指针,也就是发送的字符串数组名,一般发啥过去,返回的就是啥

4、RequestSize

RequestData的大小,一般在strlen(RequestData)的基础上+1,不加也可以,如示例代码中直接使用sizeof(RequestData)的返回值

5、RequestOptions

指向请求的IP标头选项的指针,形式为IP_OPTION_INFORMATION结构。在64位平台上,此参数采用IP_OPTION_INFORMATION32结构的形式。如果不需要指定IP标头选项,则此参数可以为NULL。这个需要注意的是,64位平台IP_OPTION_INFORMATION结构是有后32的,而32位的没有

这个结构体定义如下typedef struct ip_option_information {

UCHAR  Ttl;

UCHAR  Tos;

UCHAR  Flags;

UCHAR  OptionsSize;

PUCHAR OptionsData;

} IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION;

描述了被包括在IP分组的报头选项,也就是ICMP的报头结构,其中OptionData对应发送的数据,size对应大小

6、ReplyBuffer

一个缓冲区,用于保存对请求的所有答复。返回时,缓冲区包含ICMP_ECHO_REPLY结构数组, 后跟答复的选项和数据。缓冲区应足够大,以容纳至少一个 ICMP_ECHO_REPLY结构以及数据的RequestSize字节。

也就是说,回复的数据中包含一个ICMP_ECHO_REPLY结构体和一个RequestData,其中ICMP_ECHO_REPLY结构定义如下typedef struct icmp_echo_reply {

IPAddr                       Address;

ULONG                        Status;

ULONG                        RoundTripTime;

USHORT                       DataSize;

USHORT                       Reserved;

PVOID                        Data;  struct ip_option_information Options;} ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY;

Data也就是返回的实际数据,这里可以用wireshark抓包验证,结构体中重点要说的是IPAddr,也就是地址,这里返回的是一个小端序数,如下示例代码输出char ip[] = "127.0.0.1";

unsigned long ipHex = inet_addr(ip);

struct in_addr hex;

hex.S_un.S_addr = ipHex;

char ipStr[20];

memset(ipStr, 0, 20);

memcpy(ipStr, inet_ntoa(hex), strlen(inet_ntoa(hex)));

printf("ipHex = %x\n", ipHex);

printf("ipStr = %s\n", ipStr);

输出如图

1da22f714a28e871020f5a5cdd129029.gif

9878f05f3c4ca84c22f677930ed7df37.png

7、ReplySize

分配的答复缓冲区大小(以字节为单位)。缓冲区应足够大,以容纳至少一个 ICMP_ECHO_REPLY结构以及数据的RequestSize字节。此缓冲区还应该足够大,以容纳8个更多字节的数据(ICMP错误消息的大小)。

8、Timeout

等待答复的时间(以毫秒为单位)。

到这里,MSDN官网上面的代码介绍基本完毕,稍修改输出便可实现cmd终端的ping功能输出,那么如何修改至用于探测主机存活呢?

首先,在真实环境中,一般不需要考虑IcmpCreateFile创建文件失败或者ReplyBuffer申请内存空间失败,如果遇到这种情况,直接认为主机不存在,不存活,而且ReplyBuffer的大小是可以直接计算出来的,甚至都不需要malloc函数来申请内存空间,其次,只是探测主机是否存活,并非重复造轮子一个ping,所以发送的数据也可以尽可能的简短,输出也不需要,直接修改为一个函数即可,同时为批量探测主机存活提供接口,如下#include 

#include 

#include 

#include 

#pragma comment(lib, "iphlpapi.lib")

#pragma comment(lib, "ws2_32.lib")

BOOL icmpProbe(char* ip)

{

HANDLE hIcmpFile;

unsigned long ipaddr = INADDR_NONE;

DWORD dwRetVal = 0;

char SendData[32] = "icmpTest";

LPVOID ReplyBuffer = NULL;

DWORD ReplySize = 0;

ipaddr = inet_addr(ip);

hIcmpFile = IcmpCreateFile();

if (hIcmpFile == INVALID_HANDLE_VALUE) {

return FALSE;

}

ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);

ReplyBuffer = (VOID*)malloc(ReplySize);

if (ReplyBuffer == NULL) {

return FALSE;

}

dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 1000);

if (dwRetVal != 0) {

return TRUE;

}

else {

return FALSE;

}

}

int  main(int argc, char** argv)

{

if (argc == 1)

{

printf("Usage: %s IPv4 address\n\n", argv[0]);

exit(0);

}

unsigned long  ipaddr = inet_addr(argv[1]);

if (ipaddr == INADDR_NONE) {

printf("[-] %s is not a IPv4 address, please input a valid IPv4 address\n\n", argv[1]);

exit(0);

}

if (icmpProbe(argv[1]))

{

printf("[+] %s alive!\n\n", argv[1]);

}

else

{

printf("[-] %s not alive!\n\n", argv[1]);

}

return 0;

}

暂时先到这,vs2019编译是通过的,但还是希望下次编译二次开发不会出啥奇奇怪怪的问题,至于如何用这它来实现批量探测主机是否存活,批量解析IP地址,后续补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值