网络嗅探器

网络嗅探器:把网卡设置成混杂模式,并可实现对网络上传输的数据包的捕获与分析。

原理:

  通常的套接字程序只能响应与自己MAC地址相匹配的 或者是 广播形式发出的数据帧,对于其他形式的数据帧网络接口采取的动作是直接丢弃

  为了使网卡接收所有经过他的封包,要将其设置成混杂模式,通过原始套接字来实现。

设置混杂模式:

  创建原始套接字,

  绑定到一个明确的本地地址,

  向套接字发送SIO_RCVALL控制命令,

  接收所有的IP包

代码实现步骤:

  1 创建原始套接字

  2 绑定到明确地址

  3 这是SIO_RCVALL控制代码

  4 进入循环,调用recv函数接收经过本地网卡的IP封包。

主程序代码如下

void main()
{
    //创建原始套接字
    SOCKET sRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
    //获取本地IP地址
    char szHostName[56];
    SOCKADDR_IN addr_in;
    struct hostent *pHost;
    gethostname(szHostName,56);
    if((pHost=gethostbyname((char*)szHostName))==NULL)
        return;
    //套接字绑定
    addr_in.sin_family = AF_INET;
    addr_in.sin_port = htons(0);
    memcpy(&addr_in.sin_addr.S_un.S_addr,pHost->h_addr_list[0],pHost->h_length);
    printf("Binding to interface:%s\n",::inet_ntoa(addr_in.sin_addr));
    if(bind(sRaw,(sockaddr*)&addr_in,sizeof(addr_in))==SOCKET_ERROR)
        return;
    //设置SIO_RECVALL控制代码
    DWORD dwValue = 1;
    if(ioctlsocket(sRaw,SIO_RCVALL,&dwValue)!=0)
        return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
    //开始接收封包
    char buff[1024];
    int nRet;
    while(true)
    {
        nRet = recv(sRaw,buff,1024,0);
        if(nRet>0)
        {
            DecodeIPPacket(buff);
        }
    }
    closesocket(sRaw);
}

程序接收到IP封包后,调用自定义的DecodeIPPacket进行解包。取出封包中的协议头,向用户打印出协议信息。

解析IP头代码

void DecodeIPPacket(char *pData)
{
    IPHeader *pIPHdr = (IPHeader*)pData;
    in_addr source,dest;
    char szSourceIp[32],szDestIp[32];
    printf("\n\n------------------------------------------------\n");
    //从IP头中取出源IP和目的IP
    source.S_un.S_addr = pIPHdr->ipSource;
    dest.S_un.S_addr = pIPhdr->ipDestination;

    strcpy(szSourceIp,::inet_ntoa(source));
    strcpy(szDestIp,::inet_ntoa(dest));
    printf("        %s->%s\n",szSourceIp,szDestIp);
    //IP长度
    int nHeaderLen = (pIPHdr->iphVerLen & 0xf)*sizeof(ULONG);
    switch(pIPHdr->ipProtocol)
    {
    case IPPROTO_TCP:
        DecodeTCPPacket(pData+nHeaderLen);
        break;
    case IPPROTO_UDP:
        break;
    case IPPROTO_ICMP:
        break;
    }
}

解析TCP头代码如下:取出端口号,输出

void DecodeTCPPacket(char *pData)
{
    TCPHeader &pTCPHdr = (TCPHeader*)pData;
    printf("Port:%d->%d\n",ntohs(pTCPHdr->sourcePort),ntohs(pTCOHdr->destinationPort));
    switch(::ntohs(pTCPHdr->destinationPort))
    {
    case 21:
        break;
    case 80:
        break;
    case 8080:
        break;
    }
}

VS下完整代码:

initsock.h:

#include <winsock2.h>
#pragma comment(lib, "WS2_32")    // 链接到WS2_32.lib

class CInitSock        
{
public:
    CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
    {
        // 初始化WS2_32.dll
        WSADATA wsaData;
        WORD sockVersion = MAKEWORD(minorVer, majorVer);
        if(::WSAStartup(sockVersion, &wsaData) != 0)
        {
            exit(0);
        }
    }
    ~CInitSock()
    {    
        ::WSACleanup();    
    }
};

protoinfo.h:

//////////////////////////////////////////////////
// protoinfo.h文件

/*

定义协议格式
定义协议中使用的宏

 */


#ifndef __PROTOINFO_H__
#define __PROTOINFO_H__


#define ETHERTYPE_IP    0x0800
#define ETHERTYPE_ARP   0x0806

typedef struct _ETHeader         // 14字节的以太头
{
    UCHAR    dhost[6];            // 目的MAC地址destination mac address
    UCHAR    shost[6];            // 源MAC地址source mac address
    USHORT    type;                // 下层协议类型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等
} ETHeader, *PETHeader;


#define ARPHRD_ETHER     1

// ARP协议opcodes
#define    ARPOP_REQUEST    1        // ARP 请求    
#define    ARPOP_REPLY        2        // ARP 响应


typedef struct _ARPHeader        // 28字节的ARP头
{
    USHORT    hrd;                //    硬件地址空间,以太网中为ARPHRD_ETHER
    USHORT    eth_type;            //  以太网类型,ETHERTYPE_IP ??
    UCHAR    maclen;                //    MAC地址的长度,为6
    UCHAR    iplen;                //    IP地址的长度,为4
    USHORT    opcode;                //    操作代码,ARPOP_REQUEST为请求,ARPOP_REPLY为响应
    UCHAR    smac[6];            //    源MAC地址
    UCHAR    saddr[4];            //    源IP地址
    UCHAR    dmac[6];            //    目的MAC地址
    UCHAR    daddr[4];            //    目的IP地址
} ARPHeader, *PARPHeader;


// 协议
#define PROTO_ICMP    1
#define PROTO_IGMP    2
#define PROTO_TCP     6
#define PROTO_UDP     17

typedef struct _IPHeader        // 20字节的IP头
{
    UCHAR     iphVerLen;      // 版本号和头长度(各占4位)
    UCHAR     ipTOS;          // 服务类型 
    USHORT    ipLength;       // 封包总长度,即整个IP报的长度
    USHORT    ipID;              // 封包标识,惟一标识发送的每一个数据报
    USHORT    ipFlags;          // 标志
    UCHAR     ipTTL;          // 生存时间,就是TTL
    UCHAR     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等
    USHORT    ipChecksum;     // 校验和
    ULONG     ipSource;       // 源IP地址
    ULONG     ipDestination;  // 目标IP地址
} IPHeader, *PIPHeader; 


// 定义TCP标志
#define   TCP_FIN   0x01
#define   TCP_SYN   0x02
#define   TCP_RST   0x04
#define   TCP_PSH   0x08
#define   TCP_ACK   0x10
#define   TCP_URG   0x20
#define   TCP_ACE   0x40
#define   TCP_CWR   0x80

typedef struct _TCPHeader        // 20字节的TCP头
{
    USHORT    sourcePort;            // 16位源端口号
    USHORT    destinationPort;    // 16位目的端口号
    ULONG    sequenceNumber;        // 32位序列号
    ULONG    acknowledgeNumber;    // 32位确认号
    UCHAR    dataoffset;            // 高4位表示数据偏移
    UCHAR    flags;                // 6位标志位
                                //FIN - 0x01
                                //SYN - 0x02
                                //RST - 0x04 
                                //PUSH- 0x08
                                //ACK- 0x10
                                //URG- 0x20
                                //ACE- 0x40
                                //CWR- 0x80

    USHORT    windows;            // 16位窗口大小
    USHORT    checksum;            // 16位校验和
    USHORT    urgentPointer;        // 16位紧急数据偏移量 
} TCPHeader, *PTCPHeader;

typedef struct _UDPHeader
{
    USHORT            sourcePort;        // 源端口号        
    USHORT            destinationPort;// 目的端口号        
    USHORT            len;            // 封包长度
    USHORT            checksum;        // 校验和
} UDPHeader, *PUDPHeader;

#endif // __PROTOINFO_H__
#include "../common/initsock.h"
#include "../common/protoinfo.h" 

#include <stdio.h>
#include <mstcpip.h>

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

CInitSock theSock;

void DecodeTCPPacket(char *pData)
{
    TCPHeader *pTCPHdr = (TCPHeader *)pData;

    printf(" Port: %d -> %d \n", ntohs(pTCPHdr->sourcePort), ntohs(pTCPHdr->destinationPort));
    
    // 下面还可以根据目的端口号进一步解析应用层协议
    switch(::ntohs(pTCPHdr->destinationPort))
    {
    case 21:
        break;
    case 80:
    case 8080:
        break;
    }
}

void DecodeIPPacket(char *pData)
{
    IPHeader *pIPHdr = (IPHeader*)pData;    
    in_addr source, dest;
    char szSourceIp[32], szDestIp[32]; 

    printf("\n\n-------------------------------\n");

    // 从IP头中取出源IP地址和目的IP地址
    source.S_un.S_addr = pIPHdr->ipSource;
    dest.S_un.S_addr = pIPHdr->ipDestination;
    strcpy(szSourceIp, ::inet_ntoa(source));
    strcpy(szDestIp, ::inet_ntoa(dest));

    printf("    %s -> %s \n", szSourceIp, szDestIp);
    // IP头长度
    int nHeaderLen = (pIPHdr->iphVerLen & 0xf) * sizeof(ULONG);

    switch(pIPHdr->ipProtocol)
    {
    case IPPROTO_TCP: // TCP协议
        DecodeTCPPacket(pData + nHeaderLen);
        break;
    case IPPROTO_UDP:
        break;
    case IPPROTO_ICMP:
        break; 
    }
}


void main()
{
    // 创建原始套节字
    SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

    // 获取本地IP地址
    char szHostName[56];
    SOCKADDR_IN addr_in;
    struct  hostent *pHost;
    gethostname(szHostName, 56);
    if((pHost = gethostbyname((char*)szHostName)) == NULL)    
        return ;

    // 在调用ioctl之前,套节字必须绑定
    addr_in.sin_family  = AF_INET;
    addr_in.sin_port    = htons(0);
    memcpy(&addr_in.sin_addr.S_un.S_addr, pHost->h_addr_list[0], pHost->h_length);

    printf(" Binding to interface : %s \n", ::inet_ntoa(addr_in.sin_addr));
    if(bind(sRaw, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
        return;

    // 设置SIO_RCVALL控制代码,以便接收所有的IP包    
    DWORD dwValue = 1;
    if(ioctlsocket(sRaw, SIO_RCVALL, &dwValue) != 0)    
        return ;
    
    // 开始接收封包
    char buff[1024];
    int nRet;
    while(TRUE)
    {
        nRet = recv(sRaw, buff, 1024, 0);
        if(nRet > 0)
        {
            DecodeIPPacket(buff);
        }
    }
    closesocket(sRaw);
}

运行结果

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏蓝天

批量远程执行shell命令工具

使用示例(使用了默认用户root,和默认端口号22): ./mooon_ssh --h=192.168.4.1,192.168.4.2 -P=password...

1002
来自专栏Kotlin入门系列

win7基础 cmd 查看当前已经启动的服务列表

4126
来自专栏CDN及云技术分享

Openssl状态机的实现

Openssl是通过“握手“建立加密信道,在该信道双方的身份都是合法的,并且传输数据都是密文传输。Openssl握手通过客户端和服务端互相交换信息计算出secr...

3433
来自专栏FreeBuf

SniffAir:无线渗透测试框架

SniffAir是一个开源的无线安全框架,可帮助你轻松解析被动收集的无线数据并发起复杂的无线渗透测试。此外,它还可以处理大型的或多个pcap文件,执行交叉检查和...

902
来自专栏俗人笔记

PHP中检测IP是否是内网IP的三个方法

2463
来自专栏linux驱动个人学习

dump_stack 分析使用

dump_stack是用来回溯内核运行的信息的,打印内核信息堆栈段; dump_stack原型: void dump_stack(void); 1、使用这个功能...

3205
来自专栏Hongten

python开发_csv(Comma Separated Values)_逗号分隔值_常用导入导出格式_完整版_博主推荐

====================================================

571
来自专栏猿天地

Spring Cloud Gateway 限流操作

API网关作为所有请求的入口,请求量大,我们可以通过对并发访问的请求进行限速来保护系统的可用性。

1853
来自专栏蓝天

上传单个文件到多台机器工具

872
来自专栏雨尘分享

TCP 看我就够了

3305

扫码关注云+社区