赞
踩
本次实验是基于ubuntu虚拟机。
(1)准备实验环境;
(2)libpcap相关函数;
(3)掌握数据包的抓包过程
Libpcap简介
Libpcap是unix/linux平台下的网络数据包捕获函数包,大多数网络监控软件都以它为基础。Libpcap可以在绝大多数类unix平台下工作。Libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。
此安装是基于ubuntu虚拟机进行libpcap的安装。
sudo apt-get install build-essential
2、安装GNU M4:
在这里下载压缩包:ftp.gnu.org/gnu/m4/ ,然后解压并安装:
tar -zxvf m4-1.4.9.tar.gz
cd m4-1.4.9
sudo ./configure
sudo make
sudo make install
3、安装flex:
sudo apt-get install flex
4、安装bison:
sudo apt-get install bison
5、安装Libpcap:
先在官网下载压缩包: http://www.tcpdump.org/,然后解压并安装:
tar -zxvf libpcap-1.10.3.tar.gz
cd libpcap-1.10.3
sudo ./configure
sudo make
sudo make install
find /usr -name “libpcap*so*”和locate pcap.h
先在官网下载安装压缩包:https://download.qt.io/archive/qt/
按照下图选择压缩包进行安装:
然后解压并安装:
我们来编写一个简单的Hello World!程序来看看是否安装成功。
先创建项目,如下图所示:
创建好编写代码:
- #include <stdio.h>
-
- int main()
- {
- printf(“Hello World! \n”);
- return 0;
- }
点击运行,弹出弹框,输入密码得到Hello World!
QT安装成功:)
Libpcap工作原理
libpcap主要由两部分组成,网络分接头(network tap)和数据过滤器(packet filter)。网络分接头从网络设备驱动程序中收集数据,进行拷贝,过滤器决定是否接收该数据包。libpcap利用BSD packet filter(BPF)算法对网卡接收到的链路层数据包进行过滤,BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给BPF过滤器,过滤器根据用户定义的规则决定是否接收该数据包,以及需要拷贝该数据包的哪些内容,然后将过滤后的数据交给过滤器关联的上层应用程序。
libpcap的包捕获机制就是在数据链路层加一个旁路处理,当一个数据包到达网络接口时,libpcap首先利用已经创建的套接字从数据链路层驱动程序中获得该数据包的拷贝,再将数据包发给BPF过滤器,BPF过滤器根据用户定义的规则将数据包逐一匹配,匹配成功的放入内核缓冲区,匹配失败直接丢弃。如果没有设置过滤规则,那么多有的数据包都将放入内核缓冲区并且传递给用户层缓冲区。
libpcap主要接口函数API
1、pcap_t *pcap_open_live(char *device, int snaplen,
int promisc, int to_ms, char *ebuf)
获得用于捕获网络数据包的数据包捕获描述字。
device参数为指定打开的网络设备名。
snaplen参数定义捕获数据的最大字节数。
promisc指定是否将网络接口置于混杂模式。
to_ms参数指定超时时间(毫秒)。
ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。
打开以前保存捕获数据包的文件,用于读取。
fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输入。
ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。
打开用于保存捕获数据包的文件,用于写入。
fname参数为"-"时表示标准输出。出错时返回NULL。
p参数为调用pcap_open_offline()或pcap_open_live()函数后返回的pcap结构指针。
fname参数指定打开的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消息。
用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。
5、int pcap_lookupnet(char *device, bpf_u_int32 *netp,
bpf_u_int32 *maskp, char *errbuf)
获得指定网络设备的网络号和掩码。
netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。
6、int pcap_lookupnet(char *device, bpf_u_int32 *netp,
bpf_u_int32 *maskp, char *errbuf)
获得指定网络设备的网络号和掩码。netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。
7、int pcap_dispatch(pcap_t *p, int cnt,
pcap_handler callback, u_char *user)
捕获并处理数据包。
cnt参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取到EOF;超时读取。
callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。
8、int pcap_loop(pcap_t *p, int cnt,
pcap_handler callback, u_char *user)
功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。
9、void pcap_dump(u_char *user, struct pcap_pkthdr *h,
u_char *sp)
向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。
10、int pcap_compile(pcap_t *p, struct bpf_program *fp,
char *str, int optimize, bpf_u_int32 netmask)
将str参数指定的字符串编译到过滤程序中。
fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。
optimize参数控制结果代码的优化。
netmask参数指定本地网络的网络掩码。
指定一个过滤程序。fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。
12、int pcap_datalink(pcap_t *p)
返回数据链路层类型,例如DLT_EN10MB。
13、int pcap_snapshot(pcap_t *p)
返回pcap_open_live被调用后的snapshot参数值。
14、int pcap_is_swapped(pcap_t *p)
返回当前系统主机字节与被打开文件的字节顺序是否不同。
15、int pcap_major_version(pcap_t *p)
返回写入被打开文件所使用的pcap函数的主版本号。
16、int pcap_minor_version(pcap_t *p)
返回写入被打开文件所使用的pcap函数的辅版本号。
17、int pcap_stats(pcap_t *p, struct pcap_stat *ps)
向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计,则返回-1,且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。
18、FILE *pcap_file(pcap_t *p)
返回被打开文件的文件名。
19、int pcap_fileno(pcap_t *p)
返回被打开文件的文件描述字号码。
20、void pcap_perror(pcap_t *p, char *prefix)
在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。
21、char *pcap_geterr(pcap_t *p)
返回最后一个pcap库错误消息。
22、char *pcap_strerror(int error)
如果strerror()函数不可用,则可调用pcap_strerror函数替代。
23、void pcap_close(pcap_t *p)
关闭p参数相应的文件,并释放资源。
24、pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *file)
函数返回pcap_dumper_t类型的指针,file是文件名,可以是绝对路径,例如:/home/iona/packet.pcap;
25、void pcap_dump_close(pcap_dumper_t *p);
用来关闭pcap_dump_open打开的文件,入参是pcap_dump_open返回的指针
26、int pcap_dump_flush(pcap_dumper_t *p)
刷新缓冲区,把捕获的数据包从缓冲区真正拷贝到文件;
27、void pcap_dump(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
输出数据到文件,与pcap_loop的第二个参数回调函数void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet) 形式完全相同,可以直接当pcap_loop的第二个参数
头文件定义:
/*pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息*/
struct pcap_pkthdr {
struct timeval ts; /* 时间戳 */
bpf_u_int32 caplen; /* 已捕捉部分的长度 */
bpf_u_int32 len; /* 该包的脱机长度 */
};
/* 以太网帧头部 */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
u_short ether_type; /* IP:0x0800;IPV6:0x86DD; ARP:0x0806;RARP:0x8035 */
};
/* IP数据包的头部 */
struct sniff_ip {
#if BYTE_ORDER == LITTLE_ENDIAN
u_int ip_hl:4, /* 头部长度 */
ip_v:4; /* 版本号 */
#if BYTE_ORDER == BIG_ENDIAN
u_int ip_v:4, /* 版本号 */
ip_hl:4; /* 头部长度 */
#endif
#endif /* not _IP_VHL */
u_char ip_tos; /* 服务的类型 */
u_short ip_len; /* 总长度 */
u_short ip_id; /*包标志号 */
u_short ip_off; /* 碎片偏移 */
#define IP_RF 0x8000 /* 保留的碎片标志 */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* 多碎片标志*/
#define IP_OFFMASK 0x1fff /*分段位 */
u_char ip_ttl; /* 数据包的生存时间 */
u_char ip_p; /* 所使用的协议:1 ICMP;2 IGMP;4 IP;6 TCP;17 UDP;89 OSPF */
u_short ip_sum; /* 校验和 */
struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
};
/* TCP 数据包的头部 */
struct sniff_tcp {
u_short th_sport; /* 源端口 */
u_short th_dport; /* 目的端口 */
tcp_seq th_seq; /* 包序号 */
tcp_seq th_ack; /* 确认序号 */
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:4, /* 还没有用到 */
th_off:4; /* 数据偏移 */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:4, /* 数据偏移*/
th_x2:4; /*还没有用到 */
#endif
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
u_short th_win; /* TCP滑动窗口 */
u_short th_sum; /* 头部校验和 */
u_short th_urp; /* 紧急服务位 */
};
/* UDP header */
struct sniff_udp
{
uint16_t sport; /* source port */
uint16_t dport; /* destination port */
uint16_t udp_length;
uint16_t udp_sum; /* checksum */
};
捕获数据包的代码展示:
- #include <pcap.h>
- #include <time.h>
- #include <stdio.h>
- #include <stdlib.h>
-
-
- void processPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
- {
- int *count = (int *)arg;
-
- printf("Packet Count: %d\n", ++(*count));
- printf("Received Packet Size: %d\n", pkthdr->len);
- printf("Payload:\n");
-
- int i;
- for(i=0; i < pkthdr->len; ++i)
- {
- printf("%02x ", packet[i]);
- if ((i + 1) % 16 == 0)
- {
- printf("\n");
- }
- }
- pcap_dump(arg,pkthdr, packet);
- printf( "Received Packet Size: %d \n",pkthdr->len);
- printf("\n\n");
- return;
- }
-
- int main()
- {
- char errBuf[PCAP_ERRBUF_SIZE], * devStr;
-
- devStr = "ens33";
-
- pcap_t * device = pcap_open_live(devStr,65535,1,0,errBuf);
- if (!device)
- {
- printf("error: pcap_open_live(): %s\n", errBuf);
- exit(1);
- }
-
- struct bpf_program filter;
- pcap_compile(device,&filter,"tcp",1,0);
- pcap_setfilter (device,&filter);
- pcap_dumper_t* out_pcap;
- out_pcap = pcap_dump_open(device, "rosymoonlight.pcap");
- pcap_loop(device,5,processPacket,(u_char * )out_pcap);
- pcap_dump_flush(out_pcap);
- pcap_dump_close(out_pcap);
- pcap_close(device);
- return 0;
- }
在QT创建一个项目,把代码写上去测试一下:
发现报错:
打开项目里的.pro文件,在最下面添加pcap的库:
LIBS += -L/usr/local/lib -lpcap
然后点进代码.c文件再运行一下,弹出一个弹窗,输入密码以后,随便打开一个网页(我打开了微博的网页),数据包成功捕获!
我们也可以在终端创建.c文件,输入代码进行抓包
1、输入命令:vim test.c创建一个C文件,输入代码以后输入:wq保存退出
2、输入命令:gcc test.c -o test -lpcap生成gcc文件
3、输入命令:./test 再打开一个网页(我依旧打开的是微博),成功捕获到数据包!
最后我们可以打开wireshark查看捕获到的数据包:)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。