基于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,方可编译成功。

 

  

原文地址:https://www.cnblogs.com/jiangson/p/11890626.html