上一篇讲了VC++ping命令模拟,那个只是个示例点击打开链接
这次我将代码整合后,做一个实战的练习,扫描一个IP段,返回所有活动的IP
要完成这个练习,以下有几个步骤,作为程序员的我们就用代码说话
首先要做到如何证明一个IP是否在活动,代码如下
BOOL IpCheck::IsIPActive(char *addr) { // 目的IP地址,即要Ping的IP地址 char *szDestIp = addr; // 127.0.0.1 // 创建原始套节字 WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); // 设置接收超时 SetTimeout(sRaw, 100, TRUE); // 设置目的地址 SOCKADDR_IN dest; dest.sin_family = AF_INET; dest.sin_port = htons(0); dest.sin_addr.S_un.S_addr = inet_addr(szDestIp); // 创建ICMP封包 char buff[sizeof(ICMP_HDR) + 32]; ICMP_HDR* pIcmp = (ICMP_HDR*)buff; // 填写ICMP封包数据,请求一个ICMP回显 pIcmp->icmp_type = 8; pIcmp->icmp_code = 0; pIcmp->icmp_id = (USHORT)GetCurrentProcessId(); pIcmp->icmp_checksum = 0; pIcmp->icmp_sequence = 0; // 填充数据部分,可以为任意 memset(&buff[sizeof(ICMP_HDR)], 'E', 32); // 开始发送和接收ICMP封包 USHORT nSeq = 0; char recvBuf[1024]; SOCKADDR_IN from; int nLen = sizeof(from); static int nCount = 0; int nRet; pIcmp->icmp_checksum = 0; pIcmp->icmp_timestamp = GetTickCount(); pIcmp->icmp_sequence = nSeq++; pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32); nRet = sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest)); if(nRet == SOCKET_ERROR) { printf(" sendto() failed: %d \n", ::WSAGetLastError()); return -1; } nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen); if(nRet == SOCKET_ERROR) { if(WSAGetLastError() == WSAETIMEDOUT) { printf("%s timed out\n", addr); return FALSE; } printf("recvfrom() failed: %d\n", WSAGetLastError()); return FALSE; } // 下面开始解析接收到的ICMP封包 int nTick = ::GetTickCount(); if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR)) { printf(" Too few bytes from %s \n", inet_ntoa(from.sin_addr)); } // 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头 // (ICMP_HDR*)(recvBuf + sizeof(IPHeader)); ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); if(pRecvIcmp->icmp_type == ICMP_TYPE_REACH_FAIL) // 回显 { printf("%s reach fail, type %d recvd \n", addr, pRecvIcmp->icmp_type); return FALSE; } if(pRecvIcmp->icmp_id != GetCurrentProcessId()) { printf(" someone else's packet! \n"); return FALSE; } printf("从 %s 返回 %d 字节:", addr,nRet); printf(" 数据包序列号 = %d. \t", pRecvIcmp->icmp_sequence); printf(" 延时大小: %d ms", nTick - pRecvIcmp->icmp_timestamp); printf(" \n"); s_array->Insert(addr); WSACleanup(); closesocket(sRaw); return TRUE; }
这个是扫描一个指定的IP地址,但是我们想要扫描多个IP地址的话,必须对IP地址进行转化,转化为整数就可以进行累加操作
unsigned long __fastcall IpCheck::InvertIp(unsigned long NormalIp) { unsigned char b1,b2,b3,b4; b1 = NormalIp & 0x00FF; b2 = (NormalIp >> 8) & 0x00FF; b3 = (NormalIp >> 16) & 0x00FF; b4 = (NormalIp >> 24) & 0x00FF; return (b1 <<24) | (b2 << 16) | (b3 << 8) | b4; } //多地址扫描 void IpCheck::MutiScan(char *s_ip, char *e_ip) { in_addr ia; unsigned long FirstIp,SecondIp; int Delta; char* Addr; FirstIp = inet_addr(s_ip); //任意的开始地址 SecondIp = inet_addr(e_ip); //任意的结束地址 //转换成能直接递增和递减的地址 FirstIp = InvertIp(FirstIp); SecondIp = InvertIp(SecondIp); Delta = SecondIp - FirstIp; s_array->InitBuf(Delta + 1); for(int i = 0; i <= Delta;i++) { ia.S_un.S_addr = InvertIp(FirstIp++); Addr = inet_ntoa(ia); //扫描 IsIPActive(Addr); } }
当我们可以扫描IP后,如何记录和打印所有活动的IP呢?一般人可能都想到用STL,我个人以前用STL有点阴影,所以自己写了一个字符串数组存储数据结构。自己写的心里有数,灵活性高,可以随时写些满足自己需求的代码,我就贴出这个存储类
StringArray.h
#pragma once //这个是自己写的一个char型指针的字符串存储数据结构,使用的是数组,没有用STL是因为还没习惯使用STL,曾经给我带来阴影 //自己写的灵活性大,方便自己使用的 class StringArray { public: StringArray(void); StringArray(int length); ~StringArray(void); void InitBuf(int length); int Insert(char *str); char *GetString(int index); char **WrapBuffer(); int GetLength(); void Release(); private: char **strbuf; int length; int count; };
StringArray.cpp
#include "StdAfx.h" #include "StringArray.h" StringArray::StringArray(void) { length = 0; strbuf = NULL; count = 0; } StringArray::StringArray(int length) { this->length = length; strbuf = (char **)malloc(sizeof(char *) * length); count = 0; } StringArray::~StringArray(void) { Release(); } void StringArray::InitBuf(int length) { this->length = length; strbuf = (char **)malloc(sizeof(char *) * length); for(int i = 0;i < length;i++) strbuf[i] = NULL; } int StringArray::Insert(char *str) { if(str == NULL || count == length) return DEFAULT_ERROR; strbuf[count] = (char *)malloc(sizeof(char) * (strlen(str) + 1)); if(strbuf[count] == NULL) return DEFAULT_ERROR; memset(strbuf[count], 0, strlen(str) + 1); memcpy(strbuf[count], str, strlen(str)); strbuf[count][strlen(str)] = '\0'; count++; return DEFAULT_SUCCESS; } char *StringArray::GetString(int index) { if(index > length || index < -1) return NULL; return strbuf[index]; } int StringArray::GetLength() { return length; } char **StringArray::WrapBuffer() { return strbuf; } void StringArray::Release() { if(strbuf != NULL) { for(int i = 0;i < length;i++) { if(strbuf[i] != NULL) { free(strbuf[i]); strbuf[i] = NULL; } } free(strbuf); strbuf = NULL; } }
有了这些我们就可以实现主函数了
// SCanIP.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { if(argc > 3) printf("Error parameter:argc > 3\n"); char *param1 = NULL; char *param2 = NULL; IpCheck si; if(argv[1] != NULL && argv[2] != NULL) { param1 = w2c(argv[1]); param2 = w2c(argv[2]); si.MutiScan(param1, param2); } else if(argv[1] != NULL) { param1 = w2c(argv[1]); si.MutiScan(param1, param1); } else { printf("Error parameter\n"); return 0; } char **rs = si.GetIps(); int count = 0; printf("存在的IP为:\n"); for(int i = 0;i < si.IpCounts();i++) { if(rs[i] == NULL) break; printf("%s\t\t", rs[i]); if(count == 3) { count = 0; printf("\n"); } count++; } if(param1 !=NULL) free(param1); if(param2 !=NULL) free(param2); //system("pause"); return 0; }
过程是扫描指定的IP段,最后显示出所有存在的IP
比如:在CMD下输入SCanIP.exe 10.72.0.0 10.72.0.255
在一段时间内会扫描某个网段
效果如下图
以上就是扫描一个IP段的过程