上一篇讲了
VC++ping命令模拟,那个只是个示例http://sxjkk.iteye.com/admin/b
logs/1151740
这次我将代码整合后,做一个实战的练习,扫描一个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;
}
}
有了这些我们就可以实现主函数了
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段的过程
- SCanIP.rar (931.5 KB)
- 下载次数: 0