• 基于WinPcap实现的Raw EtherNet 抓包、发包程序


    一、背景

      

    二、WinPcap中文技术文档

      http://www.ferrisxu.com/WinPcap/html/index.html

    二、需要使用到的动态库和外部头文件

      ① 库文件:Packet.dll、Packet.lib、wpcap.dll、wpcap.lib

      

       ② 头文件

      

     三、用vs创建工程(我这里使用的是vs2015)

      工程创建完毕需要配置工程属性

      ① 右键工程属性-->VC++目录-->找到包含目录、库目录,把刚才的库文件路径和头文件的路径添加进去,如下图所示

      

       ② 找到链接器--->附加依赖项,添加Packet.lib、wpcap.lib库文件

      

     四、示例代码

      ① 头文件

    /*****************************************************************************                                            *                                                                          *
    *  @file     RawEtherSniffer.h                                               *
    *  @brief    通过原始以太网解析FPGA发送的数据                                 *
    *  Details.                                                                  *
    *                                                                            *
    *  @author   jiang shuang                                                    *
    *  @email                                                                     *
    *  @version  1.0.0.0(版本号)                                                 *
    *  @date                                                                     *
    *  @license                                                                     *
    *                                                                            *
    *----------------------------------------------------------------------------*
    *  Remark         : Description                                              *
    *----------------------------------------------------------------------------*
    *  Change History :                                                          *
    *  <Date>     | <Version> | <Author>       | <Description>                   *
    *----------------------------------------------------------------------------*
    *  2019/09/10 | 1.0.0.0   | jiangshuang    | Create file                     *
    *----------------------------------------------------------------------------*
    *                                                                            *
    *****************************************************************************/
    #pragma once
    #define WIN32
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <pcap.h>
    
    class RawEtherTools
    {
    public:
        RawEtherTools();
        ~RawEtherTools();
    
        /**
        * @brief  以太网数据数据帧嗅探器
        * @input  无
        * @output 无
        * @return 无
        */
        void CaptureRawEtherFrame();
    
        int ethernet_protocol_packet_handle(u_char *argument,
            const struct pcap_pkthdr *packet_header,
            const u_char *packet_content);
    
    };

      ② cpp文件

    #define _CRT_SECURE_NO_WARNINGS
    #include "Tools.h"
    
    using namespace std;
    
    // 以太网协议格式的定义
    typedef struct ether_header {
        u_char ether_dhost[6];        // 目标MAC地址
        u_char ether_shost[6];        // 源MAC地址
        u_short ether_type;            // 以太网类型
    }ether_header;
    
    // 用户保存4字节的IP地址
    typedef struct ip_address {
        u_char byte1;
        u_char byte2;
        u_char byte3;
        u_char byte4;
    }ip_address;
    
    // 用于保存IPV4的首部
    typedef struct ip_header {
    #ifdef WORDS_BIGENDIAN
        u_char ip_version : 4, header_length : 4;
    #else
        u_char header_length : 4, ip_version : 4;
    #endif
    
        u_char ver_ihl;            // 版本以及首部长度,各4位
        u_char tos;                // 服务质量
        u_short tlen;            // 总长度
        u_short identification;    // 身份识别
        u_short offset;            // 分组偏移
        u_char ttl;                // 生命周期
        u_char proto;            // 协议类型
        u_short checksum;        // 包头测验码
        ip_address saddr;        // 源IP地址
        ip_address daddr;        // 目的IP地址
        u_int op_pad;            // 可选 填充字段
    }ip_header;
    
    RawEtherTools::RawEtherTools()
    {
    
    }
    
    
    RawEtherTools::~RawEtherTools()
    {
    
    }
    
    /**
    * @brief
    * @input  无
    * @output 无
    * @return 无
    */
    void RawEtherTools::CaptureRawEtherFrame()
    {
        struct pcap_pkthdr *header;
        pcap_if_t * allDevs;
        pcap_if_t * dev;
        u_int netmask;
        int inum;
        int i = 0;
        int res;
        const u_char *pkt_data;
        time_t local_tv_sec;
        struct tm *ltime;
        char timestr[16];
        ip_header *ih;
    
        char errbuf[PCAP_ERRBUF_SIZE];
        if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allDevs, errbuf) > 0)
        {
            printf("pcap_findallDevs_ex failed
    ");
        }
    
        for (dev = allDevs; dev; dev = dev->next) {
            printf("%d. %s", ++i, dev->name);
            if (dev->description) {
                printf("(%s)
    ", dev->description);
            }
            else {
                printf("No description available
    ");
            }
        }
    
        if (0 == i) {
            printf("
    No interface found!Make sure WinPcap is installed
    ");
            return;
        }
    
        printf("Enter the interface number(1-%d):", i);
    
        scanf_s("%d", &inum);
        if (inum < 1 || inum > i) {
            printf("
    Interface number out of range.
    ");
            pcap_freealldevs(allDevs);
            return;
        }
    
        for (dev = allDevs, i = 0; i < inum - 1; dev = dev->next, i++);
    
        pcap_t * handler;
    
        // 设备名,要捕捉的数据包的部分(65536保证能捕获到不同数据链路层上的每个数据包的全部内容),混杂模式,读取超时时间,错误缓冲池
        if ((handler = pcap_open_live(dev->name, 65536, 1, 1000, errbuf)) == NULL) {
            fprintf(stderr, "
    Unable to open the adapter.%s is not supported by WinPcap
    ", errbuf);
            pcap_freealldevs(allDevs);
            return;
        }
    
        // 检查数据链路层(只考虑了以太网)
        if (pcap_datalink(handler) != DLT_EN10MB) {
            fprintf(stderr, "
    This program works only on Ethernet networks.
    ");
            pcap_freealldevs(allDevs);
            return;
        }
    
        if (dev->addresses != NULL) {
            // 获得接口的第一个地址的掩码
            netmask = ((struct sockaddr_in*)(dev->addresses->netmask))->sin_addr.S_un.S_addr;
        }
        else {
            netmask = 0xffffff;
        }
    
        while ((res = pcap_next_ex(handler, &header, &pkt_data)) >= 0)
        {
            // 请求超时
            if (0 == res) {
                continue;
            }
    
            // 分析数据包
            int ret = ethernet_protocol_packet_handle(NULL, header, pkt_data);
            if (ret == -1)
                continue;
    
            // 将时间戳转换成可识别的格式
            local_tv_sec = header->ts.tv_sec;
            ltime = localtime(&local_tv_sec);
            strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
            ih = (ip_header *)(pkt_data + 14); //以太网头部长度
    
                                               // 输出时间和IP信息
                                               //printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
            printf(" len:%d ", header->len);
    
            printf("%d.%d.%d.%d -> %d.%d.%d.%d
    ",
                ih->saddr.byte1,
                ih->saddr.byte2,
                ih->saddr.byte3,
                ih->saddr.byte4,
                ih->daddr.byte1,
                ih->daddr.byte2,
                ih->daddr.byte3,
                ih->daddr.byte4);
            printf("%02x%02x%02x%02x -> %02x%02x%02x%02x
    ",
                ih->saddr.byte1,
                ih->saddr.byte2,
                ih->saddr.byte3,
                ih->saddr.byte4,
                ih->daddr.byte1,
                ih->daddr.byte2,
                ih->daddr.byte3,
                ih->daddr.byte4);
            //输出每个包的byte数据ws2_32.lib
            for (int k = 0; k < header->len; k++)
            {
                if (k % 16 == 0 && k != 0)//输出美观
                    printf("
    ");
                printf("%02x ", *(pkt_data + k));
            }
            printf("
    ");
        }
    
    
        if (-1 == res) {
            printf("Error reading the packet:%s
    ", pcap_geterr(handler));
            return;
        }
        pcap_freealldevs(allDevs);
    
    }
    
    /**
    * @brief  抓取以太网协议包
    * @input  无
    * @output 无
    * @return 无
    */
    int RawEtherTools::ethernet_protocol_packet_handle(u_char *argument,
        const struct pcap_pkthdr *packet_header,
        const u_char *packet_content)
    {
        u_short ethernet_type;                        // 以太网类型
        struct ether_header *ethernet_protocol;        // 以太网协议变量
        u_char *mac_string;                            // 以太网地址
    
        ethernet_protocol = (struct ether_header*)packet_content;// 获取以太网数据内容
        ethernet_type = ntohs(ethernet_protocol->ether_type);     // 获取以太网类型
    
    
        if (ethernet_type != 0x00FF)
        {
            return -1;
        }
        printf("Ethernet type is : %04x
    ", ethernet_type);
    
        // 获取以太网源地址
        mac_string = ethernet_protocol->ether_shost;
    
        printf(" MAC Source Address is === %02x:%02x:%02x:%02x:%02x:%02x",
            *mac_string,
            *(mac_string + 1),
            *(mac_string + 2),
            *(mac_string + 3),
            *(mac_string + 4),
            *(mac_string + 5)
        );
    
        // 获取以太网目的地址
        mac_string = ethernet_protocol->ether_dhost;
        printf(" MAC Target Address === %02x:%02x:%02x:%02x:%02x:%02x
    ",
            *mac_string,
            *(mac_string + 1),
            *(mac_string + 2),
            *(mac_string + 3),
            *(mac_string + 4),
            *(mac_string + 5)
        );
    
        printf("%d", sizeof(packet_content));
    
        return 0;
    }

      ③ Main.cpp

    #include <iostream>
    #include "Tools.h"
    using namespace std;
    
    int main()
    {
        RawEtherTools *raw = new RawEtherTools();
        raw->CaptureRawEtherFrame();
    
        system("pause");
        return 0;
    }

    五、编译程序

      ① 错误1 编译程序报错,如下图所示

       解决办法:

        ws2_32.lib文件,提供了对以下网络相关API的支持,若使用其中的API,则应该将ws2_32.lib加入工程

        在工程属性--->链接器--->附加依赖项,添加ws2_32.lib库文件

        

       ② 错误2 编译程序报错,如下图所示

      

         解决办法:    

          1.error C3861: “pcap_findalldevs_ex”: 找不到标识符

          2.error C2065: “PCAP_SRC_IF_STRING”: 未声明的标识符

            在WinPcap编程调试解决办法 中,需要项目属性-》配置属性-》C/C++-》预处理器-》预处理器定义中添加HAVE_REMOTE,方可编译成功。

     

      

  • 相关阅读:
    二分图的匹配(未完) yongmou
    树型动态规划 yongmou
    最大堆 yongmou
    C/C++ 编程环境搭建与配置 eclipse + cdt + MinGW yongmou
    ACM必备(学完一个就加亮一个)不多,就这些! yongmou
    joj 2558 Ocean Currents 特别的广搜 yongmou
    Android:自定义对话框
    ubuntu升级11.10后ADT无法安装解决办法
    处理GridView数据源(DataTable)为空行(转)
    ASP.NET无法连接到SQL Server的问题
  • 原文地址:https://www.cnblogs.com/jiangson/p/11890626.html
Copyright © 2020-2023  润新知