读书人

vc++Ping命令模拟进阶-怎么嗅探某个IP

发布时间: 2012-11-12 12:31:58 作者: rapoo

vc++Ping命令模拟进阶----如何嗅探某个IP段
上一篇讲了VC++ping命令模拟,那个只是个示例http://sxjkk.iteye.com/admin/blogs/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段的过程

读书人网 >C++

热点推荐