WinPcap
WinPcap是Windows平台下访问网络数据链路层的开源库。WinPcap允许应用程序绕开网络协议栈来捕获与传递网络数据包,并具有额外的有用特性,包括内核层的数据包过滤、一个 网络统计引擎与支持远程数据包捕获。Winpcap提供了一个强大的编程接口,为win32应用程序提供访问网络底层的能力。
Wpcap.dll库
主要结构体
struct_pcap_if_t
1 | Struct pcap_if{ |
网络设备结构,表示一个网络接口设备.
参数:
next:指向下一个元素的指针, 如果是NULL,表示链表结束。
name:winpacap为网络接口卡分配的名字,作为一个参数传递给pcap_open_live(),用户打开网卡。
description:适配器的描述addresses:指向接口的地址列表的第一个元素。
flags:标志是是否回送网卡
struct_pcap_addr
1 | struct pcap_addr { |
表示接口地址。
参数:
next:指向下一个元素的指针;
addr:IP地址;
netmask:网络掩码;
broadaddr:广播地址;
dstaddr:P2P目的地址。
struct pcap_pkthdr
1 | struct pcap_pkthdr{ |
这个结构体是由pcap_loop自己填充的,用来取得一些关于数据包的信息所以,在callback函数当中只有第一个user指针是可以留给用户使用的,如果你想给callback传递自己参数,那就只能通过pcap_loop的最后一个参数user来实现。
参数:
ts:时间戳;
caplen:已捕获部分的长度;
len:该包的脱机长度。
struct sockaddr_in
1 | struct sockaddr_in { |
接口地址的表示形式
struct in_addr
1 | struct in_addr { |
表示一个32位的IPv4地址。in_addr_t一般为32位的unsigned int,其字节顺序为网络字节序,即该无符号数采用大端字节序。其中每8位表示一个IP地址中的一个数值。
主要函数
int pcap_findalldevs_ex
1 | int pcap_findalldevs_ex( |
功能:获得当前所有可用的网络设备(网卡)的列表,并且这个列表可以被pcap_open()打开。
返回值:成功返回0,alldevs返回设备列表,alldevs不会为NULL。否则返回-1,那就是说系统没有任何接口可以列举的。出错的消息在errbuf里面返回。
参数:
source: 指定从哪获取网络接口设备列表,例如”rpcap://”,表示本地适配器;
auth:保存连接到远程主机上授权信息,查询本机时为NULL;
alldevs:指向pcap_if_t结构的指针;
errbuff:存放错误信息。
pcap_t *pcap_open
1 | pcap_t *pcap_open( |
功能:打开一个通用的源。
返回值:指向’pcap_t’的指针,可以用作以下调用(pcap_compile()等)的参数,并指定打开的WinPcap会话。如果出现问题,它返回NULL,’errbuf’变量保留错误消息。
参数:
source:包含要打开的源名称;
snplen:保留的包的长度;
flags:保留捕获数据包可能需要的几个标志;
read_timeout:以毫秒为单位读取超市;
auth:保存连接到远程主机上授权信息,查询本机时为NULL;
errbuf:指向用户分配的缓冲区的指针,该缓冲区将在该函数失败的情况下包含错误。
Pcap_sendpacket
1 | Pcap_sendpacket( |
功能:发送一个数据包。
返回值:返回值为0说明数据包已经成功的发送了,否则返回-1。
参数:
p:将要在它上面发送数据的适配器;
buf:一个包含将要发送的数据缓冲区;
size:缓冲区的长度。
void pcap_freealldevs
1 | void pcap_freealldevs( |
功能:由函数pcap_findalldevs_ex或pcap_findalldevs函数返回的网络适配器设备链表,必须调用pcap_freealldevs函数释放。
int pcap_loop
1 | int pcap_loop( |
功能:捕获数据包,不会响应pcap_open_live()函数设置的超时时间
返回值:成功返回0,失败返回1;
参数:
p:是由pcap_open_live()返回的所打的网卡的指针;
cnt:用于设置所捕获数据包的个数;
pcap_handler:是与void packet_handler()使用的一个参数,即回调函数(回调函数就是一个通过函数指针调用的函数。)的名称; 回调函数原型为pcap_callback;
user:值一般为NULL。
pcap_callback
1 | pcap_callback( |
功能:解析数据包
参数:
pcap_content:表示的捕获到的数据包的内容;
argument:是从函数pcap_loop()传递过来的。注意:这里的参数就是指 pcap_loop中的 *user 参数;
pcap_pkthdr:表示捕获到的数据包基本信息,包括时间,长度等信息。
计算机网络中的数据传递过程分析
了解计算机网络协议是本课题研究的基础,要实现发包、抓包的功能,必须先梳理清楚数据在网络中的传递。数据发送过程阐述如下:
在发送主机端,一个应用层报文被传送给传输层。传输层收到报文之后,在报文上附上附加信息,即所谓的传输层首部信息,该首部信息将被接收端的传输层使用。应用层报文和传输层首部信息一起构成了传输层报文段,传输层报文段因此封装了应用层报文。
传输层则向网络层传递该报文段,网络层增加了网络层首部信息,比如源和目的端系统的地址等,由此产生了网络层数据报。
该数据报接下来被传递给链路层,链路层增加它自己的链路层首部信息,创建了链路层帧。
所以,我们看到在每一层,一个分组都具有两种类型的字段:首部字段和有效载荷字段。而有效载荷即来自于上一层的分组。
简言之,发送端就是对应用层数据一层一层加头的过程,到接收端后,接收端再一层一层去掉头部信息,然后交给对应的应用程序。
程序设计
数据包结构体设计
ARP报文(网络层)
ARP(地址解析协议),是根据IP地址获取物理地址的一个TCP/IP协议,主要作用是通过IP地址来获取MAC地址。
本机向局域网内主机发送ARP包,ARP包内包含了目的IP,源IP,目的MAC,源MAC,其中目的MAC地址为广播地址,FF-FF-FF-FF-FF-FF,即向局域网内所有主机发送一个ARP请求,那么其他主机收到这个请求之后则会向请求来源返回一个数据包。在这个返回的数据包中包含了自身的MAC地址。那么本机收到这些返回的数据包进行解析之后便会得到局域网内所有主机的MAC地址。
构造ARP报文结构:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25//14字节以太网首部
struct EthernetHeader
{
u_char DestMAC[6]; //目的MAC地址 6字节
u_char SourMAC[6]; //源MAC地址 6字节
u_short EthType; //上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp 2字节
};
//28字节ARP帧结构
struct ArpHeader
{
unsigned short hdType; //硬件类型
unsigned short proType; //协议类型
unsigned char hdSize; //硬件地址长度
unsigned char proSize; //协议地址长度
unsigned short op; //操作类型,ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。
u_char smac[6]; //源MAC地址
u_char sip[4]; //源IP地址
u_char dmac[6]; //目的MAC地址
u_char dip[4]; //目的IP地址
};
//定义整个arp报文包,总长度42字节
struct ArpPacket {
EthernetHeader ed;
ArpHeader ah;
};
以太网数据报(数据链路层)
以太网是目前使用最广泛的局域网技术。以太网技术作为数据链路层的一种简单、高效的技术,以其为核心,与其它物理层技术相结合,形成以太网技术接入体系。1
2
3
4
5
6
7//以太网协议头
struct ether_header
{
u_int8_t ether_dhost[6]; //目的Mac地址
u_int8_t ether_shost[6]; //源Mac地址
u_int16_t ether_type; //协议类型
};
IP数据报(网络层)
IP协议提供不可靠无连接的数据报传输服务,IP层提供的服务是通过IP层对数据报的封装与拆封来实现的。IP数据报的格式分为报头区和数据区两大部分,其中报头区是为了正确传输高层数据而加的各种控制信息,数据区包括高层协议需要传输的数据。1
2
3
4
5
6
7
8
9
10
11
12
13struct ip_header
{
u_int8_t ip_version:4,ip_header_length:4; //版本4 + 首部长度4
u_int8_t ip_tos; //服务类型
u_int16_t ip_length; //总长度
u_int16_t ip_id; //标识
u_int16_t ip_off; //偏移
u_int8_t ip_ttl; //生存时间
u_int8_t ip_protocol; //协议类型
u_int16_t ip_checksum;
struct in_addr ip_souce_address; //32位的IPv4地址,in_addr_t一般为32位的unsigned int,
struct in_addr ip_destination_address; //其字节顺序为网络字节序,即该无符号数采用大端字节序,其中每8位表示一个IP地址中的一个数值。
};
TCP协议(运输层)
TCP协议的数据报头的解析,其长度为20个字节。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38//TCP协议头
#define __LITTLE_ENDIAN_BITFIELD
//小端字节序
struct tcphdr
{
u_int16_t source_port; //源地址端口
u_int16_t dest_port; //目的地址端口
u_int32_t seq; //序列号
u_int32_t ack_seq; //确认序列号
#if defined(__LITTLE_ENDIAN_BITFIELD)
u_int16_t res1:4, //保留
doff:4, //偏移
fin:1, //关闭连接标志
syn:1, //请求连接标志
rst:1, //重置连接标志
psh:1, //接收方尽快将数据放到应用层标志
ack:1, //确认序号标志
urg:1, //紧急指针标志
ece:1, //拥塞标志位
cwr:1; //拥塞标志位
#elif defined(__BIG_ENDIAN_BITFIELD)
u_int16_t doff:4, //偏移
res1:4, //保留
cwr:1, //拥塞标志位
ece:1, //拥塞标志位
urg:1, //紧急指针标志
ack:1, //确认序号标志
psh:1, //接收方尽快将数据放到应用层标志
rst:1, //重置连接标志
syn:1, //请求连接标志
fin:1; //关闭连接标志
#else
u_int16_t flag;
#endif
u_int16_t window; //滑动窗口大小
u_int16_t check; //校验和
u_int16_t urg_ptr; //紧急字段指针
}
UDP协议(运输层)
1 | //UDP协议头 |
函数子功能设计
打印获取的网络适配器信息
1 | void ifprint(pcap_if_t *d) |
将数字类型的IP地址转换成点分十进制
1 | char * iptos(u_long in) |
以太网协议分析
1 | //1.pcap_loop中的user参数;2.捕获到的基本信息;3.loop捕获到的数据包内容 |
IP协议分析
1 | void ip_protool_packet_callback(u_char *argument,const struct pcap_pkthdr* packet_header,const u_char* packet_content) |
TCP协议分析
1 | void tcp_protool_packet_callback(u_char *argument,const struct pcap_pkthdr* packet_header,const u_char* packet_content) |
UDP协议分析
1 | void udp_protool_packet_callback(u_char *argument,const struct pcap_pkthdr* packet_header,const u_char* packet_content) |
发送ARP数据包
发包流程简述图
捕获并分析数据包
抓包流程简述图