网络技术与应用第二次实验报告
[TOC]
实验内容说明
作业题目:综合性实验(1) 利用WinPcap编程捕获数据包
作业说明:
(1)了解WinPcap(或LibPcap、NPcap)的架构。
(2)学习WinPcap(或LibPcap、NPcap)的设备列表获取方法、网卡设备打开方法,以及数据包捕获方法。(3)学习多线程(或多进程)程序编写方法。
(4)通过WinPcap(或LibPcap、NPcap)编程,实现本机的数据包的捕获,显示以太帧的源地址、目的地址和帧类型/长度。
(5)捕获的数据报不要求硬盘存储,但应以简单明了的方式在屏幕上显示。必显字段包括源MAC地址、目的MAC地址、帧类型/长度。
(6)编写的程序应结构清晰,具有较好的可读性。
实验准备
(1)WinPcap架构。
WinPcap是一个win32平台下的数据报捕获体系架构,它的主要功能是进行数据报捕获和网络分析。它包含了
内核级别的包过滤:在操作系统内核中运行Netgroup Packet Fileter(NPF)设备驱动程序来与网络接口驱动直接交互。NPF提供了数据包捕获与发送的基本特性,同时也提供了一个可编程的过滤系统。可以用于限制一个会话,只捕获特定的网络数据包。
低层次的动态链接库(packet.dll):提供底层API,可以用来直接访问驱动程序的函数,提供一个独立于微软的不同操作系统的编程接口。
高级别系统无关的函数库(wpcap.dll):提供与其它系统下的pcap程序(如libpcap)的兼容性。
本次实验将利用WinPcap高级别系统无关函数库中提供的函数对流经网卡的数据包进行捕获。
一般利用WinPcap捕获数据报需要经过3个步骤
- 获取设备列表
- 打开网络接口
- 在打开的网络接口卡上捕获网络数据包
(2)WinPcap的设备列表获取方法、网卡设备打开方法,以及数据包捕获方法。
==获取设备列表==
函数原型与参数解析:
int pcap_findalldevs_ex( |
返回值:
调用发生错误时,返回-1,具体错误信息输出到errbuf中
调用成功时返回0
pcap_if_t结构
结构定义:
Typedef struct pcap_if pcap_if_t; |
释放设备列表
函数原型:
void pcap_freealldevs(pcap_if_t* alldevs); |
==打开网络接口==
在对某一个网络接口卡进行监听之前,首先需要将其打开
函数原型与参数解析:
pcap_t *pcap_open( |
返回值:
调用出错时返回NULL
成功时返回一个指向pcap_t的指针
==在打开的网络接口卡上捕获网络数据包==
函数原型与参数解析:
int pcap_next_ex( |
返回值
正确捕获到数据包,返回1,pkt_header保存数据包基本信息,pkt_data指向捕获数据包的完整数据
没有捕获到信息,返回0,超过参数read_timeout限制,此时pkt_header和pkt_data均不可用
调用过程中发生错误,返回-1
(3)多线程(或多进程)编程
函数原型与参数解析:
HANDLE |
样例:
//全局变量 |
//主函数 |
DWORD WINAPI handlerRequest(LPVOID lparam) |
实验步骤
目的:通过WinPcap(或LibPcap、NPcap)编程,实现本机的数据包的捕获,显示以太帧的源地址、目的地址和帧类型/长度。
环境的配置与头文件和库
在官网http://www.winpcap.org下载WinPca软件和相应驱动程序```developer's pack```,执行exe文件并解压压缩包
在目录WpdPack\Include\pcap目录下的pcap.h中添加一行
#define WIN32
- 创建项目,资源管理器的
资源文件
->添加
->导入现有项
,添加WpdPack目录下的两个lib文件:Packet.lib和wpcap.lib
- 创建项目,资源管理器的
在
项目
->属性
->C/C++
中将winpcap\WpdPack\Include
添加到附加包含目录
将WPCAP和HAVE_REMOTE两个标号添加到预处理器定义中
在程序中引入相应的包和库。注意,
#include "pcap.h"
后不需要再引入iostream
、stdio.h
等头文件include "pcap.h"
数据结构的定义,数据分为帧首部和IP首部,其中帧首部的目的地址、源地址和帧长度/类型是我们本次实验需要捕获并显示的内容。
并且,C语言变量以4bytes对齐,不足4bytes的变量编译器自动填充,所以,为了方便数据类型的转换,设置为以1字节对齐。例如,对于类型
FrameHeader_t
,如果以1bytes对齐,一个该类型变量将占用6+6+2=14bytes,若以4bytes对齐,将补齐为16字节。
typedef struct FrameHeader_t {//帧首部
BYTE DesMAC[6];//目的地址
BYTE SrcMAC[6];//源地址
WORD FrameType;//帧类型
}frameHeader_t;
typedef struct IPHeader_t {//IP首部
BYTE Ver_HLen;
BYTE TOS;
WORD TotalLen;
WORD ID;
WORD Flag_Segment;
BYTE TTL;
BYTE Protocol;
WORD Checksum;
ULONG SrcIP;
ULONG DstIP;
}IPHeader_t;
typedef struct Data_t {//包含帧首部和IP首部的数据包
FrameHeader_t FrameHeader;
IPHeader_t IPHeader;
}Data_t;获取本机设备列表
//PCAP_SRC_IF_STRING为const char* 型,需要转换为char*类型
char *pcap_src_if_string = new char[strlen(PCAP_SRC_IF_STRING)];
strcpy(pcap_src_if_string, PCAP_SRC_IF_STRING);void find_alldevs() //获取本机的设备列表
{
//该函数第一个参数类型为char*类型
//使用pcap_findalldevs_ex函数,将设备列表存储到alldevs中
if (pcap_findalldevs_ex(pcap_src_if_string, NULL, &alldevs, errbuf) == -1)
{
//如果出现错误,则打印错误信息
printf("%s", "error");
}
}用
pcap_open
函数打开特定网络接口卡//指定网卡名称
pcap_t* p = pcap_open(name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 100, NULL, errbuf);
if (p == NULL)
{
printf("error");
return;
}利用函数
pcap_next_ex
捕捉网卡信息,并将数据内容信息存储再pkt_data中,如果成功捕获,则返回1pcap_pkthdr* pkt_header;
const u_char* pkt_data;
int rtn = pcap_next_ex(p, &pkt_header, &pkt_data);为了便于信息提取,需要将捕获到的char*类型数据
pkt_data
转换为Data_t
类型//printf("%s\t%s\n", d->name, d->description);
Data_t* IPPacket;
//WORD RecvChecksum;
IPPacket = (Data_t*)pkt_data;对于源MAC地址和目的MAC地址的打印,由于FrameHeader.DesMAC和SrcMAC为byte类型数组,需要将其元素以16进制数的形式打印
//输出目的MAC地址
printf( "目的MAC地址:");
for (int i = 0; i < 6; i++)
{
printf("%02x", IPPacket->FrameHeader.DesMAC[i]);
}
printf( " 源MAC地址:");
//输出源MAC地址
for (int i = 0; i < 6; i++)
{
printf("%02x", IPPacket->FrameHeader.SrcMAC[i]);
}由于网络序和主机b序采用的大小端约定不同,所以对于WORD类型的帧类型/长度,需要使用ntohs函数转换
printf( " 帧类型/长度:");
//ntohs((u_short)IPPacket->FrameHeader.FrameType);
printf("%02x", ntohs(IPPacket->FrameHeader.FrameType));
printf("H\n");为了同时监听所有网卡,需要使用多线程编程
//线程函数定义
DWORD WINAPI handlerRequest(LPVOID lparam)
{
char* name = (char*)lparam;
get_info(name);//监听网卡,捕获信息相关函数
return 0;
}//线程函数调用
while (1)
{for (pcap_if_t* d = alldevs; d != NULL; d = d->next)
{
hThread = CreateThread(NULL, NULL, handlerRequest, LPVOID(d->namec), 0, &dwThreadId);
//延迟,减少无用的线程以减小CPU负担
WaitForSingleObject(hThread, 500);
}
}关闭网卡
pcap_freealldevs(alldevs);
实验结果
其中
- 类型/长度=0800H,指定是IP类型数据报
- 目的MAC地址=ff:ff:ff:ff:ff:ff,是指广播发送