vc++Ping命令模拟进阶----如何嗅探某个IP段

来源:岁月联盟 编辑:exp 时间:2011-08-21

这次我将代码整合后,做一个实战的练习,扫描一个IP段,返回所有活动的IP

要完成这个练习,以下有几个步骤,作为程序员的我们就用代码说话
首先要做到如何证明一个IP是否在活动,代码如下
Java代码 
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地址进行转化,转化为整数就可以进行累加操作
Java代码 
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
Java代码 
#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
Java代码 
#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; 
    } 


有了这些我们就可以实现主函数了

Java代码 
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段的过程