tcpdump - dump traffic on a network
根据使用者的定义对网络上的数据包进行截获的包分析工具。 tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。
tcpdump的报文获取功能是通过libpcap库实现。tcpdump需要root权限才可执行,不然攻击者有普通账户权限就能获取很多信息啊!
1 | 获取版本信息时会显示libpcap对应版本信息 |
原理
linux下的抓包是通过注册一种虚拟的底层网络协议
来完成对网络报文(准确的说是网络设备)消息的处理权。当网卡接收到一个网络报文之后,它会遍历系统中所有已经注册的网络协议,例如以太网协议、x25协议处理模块来尝试进行报文的解析处理,这一点和一些文件系统的挂载相似,就是让系统中所有的已经注册的文件系统来进行尝试挂载,如果哪一个认为自己可以处理,那么就完成挂载。
当抓包模块把自己伪装成一个网络协议的时候,系统在收到报文的时候就会给这个伪协议一次机会,让它来对网卡收到的报文进行一次处理,此时该模块就会趁机对报文进行窥探,也就是把这个报文完完整整的复制一份,假装是自己接收到的报文,汇报给抓包模块。
如何实现
包传递流程:网卡–>内存–>内核态–>用户程序;tcpdump程序运行在用户态,那如何实现从内核态的抓包?
此时通过libpcap库
来实现的,tcpdump调用libpcap的api函数,由libpcap进入到内核态到链路层来抓包,如下图:
BPF是过滤器,可以根据用户设置用于数据包过滤减少应用程序的数据包的包数和字节数从而提高性能。
BufferQ是缓存供应用程序读取的数据包;可以说tcpdump底层原理其实就是libpcap的实现原理。
而libpcap在linux系统链路层中抓包是通过PF_PACKET套接字来实现的(不同的系统其实现机制是由差异的),该方法在创建的时候,可以指定第二参数为SOCK_DGRAM或者SOCK_RAW,影响是否扣除链路层的首部。
libpcap在内核收发包的接口处将skb_clone()拿走的包。
libpcap
最早的编译系统和过滤引擎是在tcpdump项目中的,后来为了编译其他抓包的应用,将其独立出来。现在libpcap提供独立于平台的库和API,来满足执行网络嗅探。
tcpdump.c正式使用libpcap里的函数完成两个最关键的动作:获取捕获报文的接口,和捕获报文并将报文交给callback。
libpcap支持“伯克利包过滤(BPF)”语法。BPF能够通过比较第2、3、4层协议中各个数据字段值的方法对流量进行过滤。Libpcap的使用逻辑如下图:
libpcap函数
函数分类:tcpdmp_pcap官方
1、读包打开句柄
2、抓包选择链路层
3、抓包函数
4、过滤器
5、选定抓包方向(进还是出)
6、抓统计信息
7、将包写入文件打开句柄
8、写包
9、注入包
10、报告错误
11、获取库版本信息
常见函数
- pcap_lookupdev 如果分组捕获设备未曾指定(-i命令行选项),该函数选择一个设备。
- pcap_open_offine 打开一个保存的文件。
- pcap_setfilter 设置过滤器
- pcap_open_live 打开选择的设备。
- pcap_next 接收一个包
- pcap_dump 将包写入到pcap_dump_t结构体
- pcap_loopupnet 返回分组捕获设备的网络地址和子网掩码,然后在调用pcap_compile时必须指定这个子网掩码。
- pcap_compile 把cmd字符数组中构造的过滤器字符串编译成一个过滤器程序,存放在fcode中。
- pcap_setfilter 把编译出来的过滤器程序装载到分组捕获设备,同时引发用该过滤器选取的分组的捕获。
- pcap_datalink 返回分组捕获设备的数据链路类型。
协议注册
对于以上介绍的协议,也只有在需要的时候才会注册,因为它毕竟增加了系统报文的处理速度并且会消耗大量的系统skb。当抓包开始的时候,它会创建一个对应的网络套接口,这种套接口的类型就是af_packet
类型。相关实现参考C源码:af_packet.c。
1 | static int packet_create(struct socket *sock, int protocol) |
Options
1 | tcpdump -n -vvvv -i ens33 host 172.16.144.141 and port 80 -w web.pcap |
Wireshark打开
常见命令
这部分简单演示了tcpdump的使用,如需常见语法可直接查看基本语法
1 | "GET"的十六进制是 47455420 |
1 | "SSH-"的十六进制是 0x5353482D |
1 | 截获DNS请求数据 |
1 | 截获大于600字节 |
过滤器
过滤器的关键词有:host、port、 net、src、 dst、icmp、tcp、udp、http
逻辑关键词:and、or、not
包头过滤
1
tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0
数据分析
1 | tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes |
172.16.144.1.64074 > 172.16.144.141.http
这里源地址(172.16.144.1.64074
)到目的地址(172.16.144.141.http
)
Flags [S]
是包的标志,[S]
表示是SYC
:
1 | [S] – SYN (开始连接) |
为了提高网络效率,一个包也可以包含标志的组合,比如[S.]
,[FP.]
seq
:同步序列编号,Synchronize Sequence Numbers;ack
:确认编号,Acknowledgement Number;win
:滑动窗口大小length
:承载的数据(payload)长度length,如果没有数据则为0
基本语法
1.过滤主机
抓取所有经过ens33,目的或源地址是172.16.144.141的网络数据
1 | tcpdump -i ens33 host 172.16.144.141 |
指定源地址
1 | tcpdump -i ens33 src host 172.16.144.141 |
指定目的地址
1 | tcpdump -i ens33 dst host 172.16.144.1 |
2.过滤端口
抓取所有经过ens33,目的或源端口是80的网络数据
1 | tcpdump -i ens33 port 80 |
指定源端口
1 | tcpdump -i ens33 src port 80 |
指定目的端口
1 | tcpdump -i ens33 dst port 80 |
3.网络过滤
1 | tcpdump -i ens33 net 172.16 |
4.协议过滤
1 | tcpdump -i ens33 arp |
5.表达式
1 | 非 : ! or "not" (去掉双引号) |
抓取所有经过ens33,目的地址是172.16.144.141或172.16.141.1端口是80的TCP数据
1 | tcpdump -i ens33 '((tcp) and (port 80) and ((dst host 172.16.144.141) or (dst host 172.16.141.1)))' |
抓取所有经过ens33,目标MAC地址是00:0c:29:aa:b2:93的ICMP数据
1 | tcpdump -i ens33 '((icmp) and ((ether dst host 00:0c:29:aa:b2:93)))' |
抓取所有经过ens33,目的ip是172.16,但目的主机不是172.16.144.1的TCP数据
1 | tcpdump -i ens33 '((tcp) and ((dst net 172.16) and (not dst host 172.16.144.141)))' |
6.运维
1 | tcpdump -s 0 -w filename |
-c
参数对于运维人员来说也比较常用,因为流量比较大的服务器,靠人工CTRL+C还是抓的太多,甚至导致服务器宕机,于是可以用-c
参数指定抓多少个包。
1 | tcpdump -nn -i ens33 'tcp[tcpflags] = tcp-syn' -c 10000 |
7.高级
高级包头过滤可参考这里
常见问题
- 如果数据包中出现很多的
cksum 0xxxx incorrect
错误:是因为操作系统为了提高网络效率不再计算校验码,而是交给网卡计算
参考
1 | Linux抓包原理: |
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章