一、背景
二、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,方可编译成功。