windows下库文件,可以读取windows网卡中数据包,并进行解析。
操作如下:
vs2019添加winpcap库
1、https://www.winpcap.org/devel.htm下载winpcap SDK包
2、winpcap SDK包解压出来之后,有lib跟include目录
3、将include、lib添加到vs2019项目环境下
4、vs项目中增加头文件与lib库路径
5、上代码
#define WIN32
#define HAVE_REMOTE
#include "pcap.h"
#include
#pragma comment(lib,"wpcap.lib")
#pragma comment(lib,"packet.lib")
#pragma comment(lib,"ws2_32.lib")
/* 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 {
u_char ver_ihl; // 版本 (4 bits) + 首部长度 (4 bits)
u_char tos; // 服务类型(Type of service)
u_short tlen; // 总长(Total length)
u_short identification; // 标识(Identification)
u_short flags_fo; // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
u_char ttl; // 存活时间(Time to live)
u_char proto; // 协议(Protocol)
u_short crc; // 首部校验和(Header checksum)
ip_address saddr; // 源地址(Source address)
ip_address daddr; // 目的地址(Destination address)
u_int op_pad; // 选项与填充(Option + Padding)
}ip_header;
// UDP 首部
typedef struct udp_header
{
u_short sport; // 源端口(Source port)
u_short dport; // 目的端口(Destination port)
u_short len; // UDP数据包长度(Datagram length)
u_short crc; // 校验和(Checksum)
}udp_header;
//TCP首部
typedef struct tcp_header
{
unsigned short src_port; //源端口号
unsigned short dst_port; //目的端口号
unsigned int seq_no; //序列号
unsigned int ack_no; //确认号
#if LITTLE_ENDIAN
unsigned char reserved_1 : 4; //保留6位中的4位首部长度
unsigned char thl : 4; //tcp头部长度
unsigned char flag : 6; //6位标志
unsigned char reseverd_2 : 2; //保留6位中的2位
#else
unsigned char thl : 4; //tcp头部长度
unsigned char reserved_1 : 4; //保留6位中的4位首部长度
unsigned char reseverd_2 : 2; //保留6位中的2位
unsigned char flag : 6; //6位标志
#endif
unsigned short wnd_size; //16位窗口大小
unsigned short chk_sum; //16位TCP检验和
unsigned short urgt_p; //16为紧急指针
}tcp_hdr;
/* 回调函数原型 */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
int main()
{
pcap_if_t* alldevs;
pcap_if_t* d;
int inum;
int i = 0;
pcap_t* adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
//char packet_filter[] = "ip and udp";
char packet_filter[] = "ip";
struct bpf_program fcode;
/* 获得设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
/* 打印列表 */
for (d = alldevs; d; d = d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}
printf("Enter the interface number (1-%d):", i);
scanf("%d", &inum);
if (inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 跳转到已选设备 */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);
/* 打开适配器 */
if ((adhandle = pcap_open(d->name, // 设备名
65536, // 要捕捉的数据包的部分
// 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
1000, // 读取超时时间
NULL, // 远程机器验证
errbuf // 错误缓冲池
)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 检查数据链路层,为了简单,我们只考虑以太网 */
if (pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
if (d->addresses != NULL)
/* 获得接口第一个地址的掩码 */
netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
else
/* 如果接口没有地址,那么我们假设一个C类的掩码 */
netmask = 0xffffff;
//编译过滤器
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
{
fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
//设置过滤器
if (pcap_setfilter(adhandle, &fcode) < 0)
{
fprintf(stderr, "\nError setting the filter.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s...\n", d->name);
/* 释放设备列表 */
pcap_freealldevs(alldevs);
/* 开始捕捉 */
pcap_loop(adhandle, 0, packet_handler, NULL);
return 0;
}
/* 回调函数,当收到每一个数据包时会被libpcap所调用 */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
struct tm* ltime;
char timestr[16];
ip_header* ih;
udp_header* uh;
tcp_header* th;
u_int ip_len;
u_short sport, dport;
time_t local_tv_sec;
/* 将时间戳转换成可识别的格式 */
local_tv_sec = header->ts.tv_sec;
ltime = localtime(&local_tv_sec);
strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);
/* 打印数据包的时间戳和长度 */
printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
/* 获得IP数据包头部的位置 */
ih = (ip_header*)(pkt_data + 14); //以太网头部长度 14字节为mac头长度
//printf("proto:%d\n", ih->proto);
printf(" src : %d.%d.%d.%d, dst : %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);
if (IPPROTO_TCP == ih->proto)
{
ip_len = (ih->ver_ihl & 0xf) * 4;
u_short tlen = ntohs(ih->tlen);
printf("tlen:%d\n", tlen);
th = (tcp_header*)((u_char*)ih + ip_len);
sport = ntohs(th->src_port);
dport = ntohs(th->dst_port);
printf(" tcp sport:%d, dport:%d\n", sport, dport);
}
if (IPPROTO_UDP == ih->proto)
{
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header*)((u_char*)ih + ip_len);
sport = ntohs(uh->sport);
dport = ntohs(uh->dport);
printf(" udp sport:%d, dport:%d\n", sport, dport);
}
}
6、执行结果:需要先手动选择网卡,然后会打印经过此网卡的tcp、udp数据包
int pcap_findalldevs_ex(char* source, struct pcap_rmtauth *auth,
pcap_if_t** alldevs, char* errbuf );
功能:查询所有网络设备
返回值:0表示查找成功,-1表示查找失败
参数说明:
source:
指定是本地适配器或者远程适配器
本地适配器:'rpcap://'
远程适配器:'rpcap://host:port'
抓包文件。'file://c:/myfolder/'.
ip首部长度 ip_len = (ih->ver_ihl & 0xf) * 4;
参考:
winpcap技术文档:http://www.ferrisxu.com/WinPcap/html/index.html
IP首部长度:https://blog.csdn.net/fl_dream/article/details/78761713