tcpdump命令详解
作者:
| 更新日期:工作两年来使用了无数次tcpdump,但是还是不熟悉很多参数,现在详细记录一下.
本文首发于公众号:天空的代码世界,微信号:tiankonguse
背景
工作两年来使用了无数次tcpdump,但是还是不熟悉很多参数,现在详细记录一下.
具体参数请参考man tcpdump
.
介绍
我们如果直接使用 tcpdump, 会抓到局域网的所有包, 这显示不是我们想要的, 所以我们需要里了解tcpdump的一些参数,来抓到自己想要的数据包.
这篇文章里我会记录 tcpdump 的这些知识点:
- 基本用法
- ip包过滤用法
- tcp包过滤用法
基本用法
指定包数量
-c
参数用于指定抓几个包.
tiankonguse:~ $ sudo tcpdump -iany -c 1
10:04:46.371552 IP 192.168.31.137.32981 > ti-in-f113.1e100.net.https: Flags [S], seq 637679323, win 29200, options [mss 1460,sackOK,TS val 955896 ecr 0,nop,wscale 7], length 0
指定网卡
-i
参数用于指定网卡, 默认网卡不是确定的(大多数时候是eth0), 所以一般需要手动指定.
如果我们确定数据包走哪个网卡的话, 直接指定对应的网卡即可, 通常是监听所有网卡-i any
.
tiankonguse:~ $ sudo tcpdump -iany -c 1 -vv
22:37:11.793327 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.31.242 tell XiaoQiang, length 28
显示ip和端口
上面的例子是一个ip给另一个ip发送数据, 但是我们只看到一个ip, 原因是tcpdump默认显示名字了.
-n
参数可以把能显示为数字的地方都显示为数字形式.
tiankonguse:~ $ sudo tcpdump -iany -c 1 -vv -n
22:38:39.989938 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.31.242 tell 192.168.31.1, length 28
过滤ip
如果我告诉大家我的ip是192.168.31.137
时, 大家就会有一个问题:怎么抓到与本机无关的数据包了呢.
是的,tcpdump默认抓的是整个局域网路过当前主机网卡的包.
那紧接着我们的问题是怎么根据ip来过滤自己感兴趣的数据包.
host
参数用于指定包的ip.
tiankonguse:~ $ sudo tcpdump -iany -c 1 -n host 192.168.31.137
23:43:01.238368 IP 192.168.31.137.53189 > 52.175.36.207.22: Flags [P.], seq 2371045085:2371045189, ack 3267945999, win 2643, options [nop,nop,TS val 5682688 ecr 346768222], length 104
有时候我们希望只显示来源ip或者回包ip是指定的值.
src host
用于指定来源ip, dst host
用于指定回包ip.
tiankonguse:~ $ sudo tcpdump -iany -c 1 -n src host 192.168.31.137
23:41:58.518326 IP 192.168.31.137.57938 > 64.233.188.188.5228: Flags [.], ack 3691415719, win 341, options [nop,nop,TS val 5667008 ecr 4279437604], length 0
tiankonguse:~ $ sudo tcpdump -iany -c 1 -n dst host 192.168.31.137
23:42:23.005982 IP 203.208.50.127.443 > 192.168.31.137.43815: Flags [.], ack 698499583, win 235, options [nop,nop,TS val 1794601452 ecr 5661867], length 0
过滤端口
过滤端口和过滤ip类似.
tcpdump port 80
tcpdump src port 80
tcpdump dst port 80
过滤协议
sudo tcpdump arp
sudo tcpdump ip
sudo tcpdump tcp
sudo tcpdump udp
sudo tcpdump icmp
过滤网络
上面提到tcpdump抓的是局域网的包, 那怎么过滤多个ip的包呢?
tcpdump net 192.168
tcpdump src net 192.168
tcpdump dst net 192.168
其他常用参数
有时候对于文本协议,我们可以使用-A
参数来直接显示出来, 这样方便我们直接查看或者使用命令进一步处理.
有时候我们的文本包很大, 我们会发现输出的包只有65535字节,此时可以使用-s
参数来指定buf大小.
-s0
代表不限制buf, 这时常用的参数.
-l
参数是使输出使用行buf, 这样遇到行结束符就马上输出内容.对于http包很有用.
tiankonguse:~ $ sudo tcpdump -iany -Anlps0 host 192.168.31.137 and dst port 80
23:37:43.947078 IP 192.168.31.137.54331 > 14.17.32.223.80: Flags [P.], seq 0:1094, ack 1, win 229, length 1094
E..n..@.@.l....... ..;.P+.:...I.P.......GET /fcgi-bin/dlib/dataout_ex?auto_id=1518&otype=json HTTP/1.1
Host: sns.video.qq.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.6
Cookie: ...
输出到文件
如果仅仅希望抓到的文本内容输出到文件, 直接使用重定向参数>
或>>
来写到文件即可.
tiankonguse:~ $ sudo tcpdump -iany -c 5 -Anlps0 host 192.168.31.137 and dst port 80 > test.txt
5 packets captured
tiankonguse:~ $ head -n20 test.txt
23:38:36.663699 IP 192.168.31.137.54334 > 14.17.32.223.80: Flags [P.], seq 0:1094, ack 1, win 229, length 1094
E..n..@.@......... ..>.P..=H_JE.P...3I..GET /fcgi-bin/dlib/dataout_ex?auto_id=1518&otype=json HTTP/1.1
...
如果希望包的所有信息都保存起来, 可以使用-w
参数重定向到文件中,这样可以使用对于的包解析软件查看或者后续使用tcpdump静态分析包内容.
使用-w
写到文件的内容可以使用-r
重新读入.
tiankonguse:~ $ sudo tcpdump -iany -c 5 -Anlps0 host 192.168.31.137 and dst port 80 -w test.pac
tiankonguse:~ $ sudo tcpdump -iany -c 5 -Anlps0 host 192.168.31.137 and dst port 80 -r test.pac
23:40:33.106822 IP 192.168.31.137.54343 > 14.17.32.223.80: Flags [P.], seq 0:1094, ack 1, win 229, length 1094
E..n..@.@.I....... ..G.P.....%..P.......GET /fcgi-bin/dlib/dataout_ex?auto_id=1518&otype=json HTTP/1.1
...
条件运算
有时候我们希望使用多个条件组合, 我们可以使用条件表达式来筛选自己想要的包.
- 非:
!
或not
- 且:
&&
或and
- 或:
||
或or
比如希望抓来源ip是192.168.31.137
的回包ip不是192.168.31.136
的tcp
包.
tiankonguse:~ $ sudo tcpdump -iany -c 5 -Anlps0 "tcp and (src host 192.168.31.137) and (not dst host 192.168.31.136)"
23:59:17.712051 IP 192.168.31.137.38330 > 203.208.50.183.443: Flags [F.], seq 1523331540, ack 2978034757, win 245, options [nop,nop,TS val 5926806 ecr 1374226439], length 0
高级过滤
过滤理论
我们可以通过proto[x:y]
得到包中任何位置的值, 从而进行计算筛选.
proto[x:y]
含义为从第x
字节起数y
个字节.proto[x:y] & z = 0
:proto[x:y]
和z
的与操作为0proto[x:y] & z !=0
:proto[x:y]
和z
的与操作不为0proto[x:y] & z = z
:proto[x:y]
和z
的与操作为z
proto[x:y] = z
:proto[x:y]
等于z
支持的操作符有下面几个:
>
大于<
小于>=
大于或者等于<=
小于或等于=
等于!=
不等于
IP包头
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding | <-- optional
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DATA ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
通常第一个字节的二进制为01000101
.
0100
代表IP的版本4, 0101
代表包头大小为`5 x 32`比特,也就是20字节.
当是IPV6
的情况时,版本信息肯定是超过01000101
,如果是带有IP
选项设置的IPV4
也有可能在版本信息中超过01000101
.
所以如果我们想检查包是否为IPV4
且不带选项的话, 可以判断IP
包的第一字节是否等于01000101 = 69
.
tiankonguse:~ $ sudo tcpdump -iany -c 2 -Anlps0 "ip[0] = 69"
00:20:20.907118 IP 192.168.31.214.17500 > 255.255.255.255.17500: UDP, length 132
E.......@...........D\D\....{"host_int": 270851488261285451571361433399569786517, "version": [2, 0], "displayname": "", "port": 17500, "namespaces": [28932611]}
00:20:38.624365 IP 192.168.31.1.43285 > 239.255.255.250.1900: UDP, length 374
E.....@................l.~..NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=60
LOCATION: http://192.168.31.1:5351/rootDesc.xml
SERVER: MiWiFi/x UPnP/1.1 MiniUPnPd/1.9
NT: upnp:rootdevice
USN: uuid:3f705d5b-6c54-4b73-94bf-be06dced73d4::upnp:rootdevice
NTS: ssdp:alive
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 1
BOOTID.UPNP.ORG: 1
CONFIGID.UPNP.ORG: 1337
当然,数字也可以使用十六进制的形式.
tiankonguse:~ $ sudo tcpdump -iany -c 5 -Anlps0 "ip[0] = 0x45"
00:52:03.311907 IP 192.168.31.214.60108 > 239.255.255.250.1900: UDP, length 175
E.........2............l.._.M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 1
ST: urn:dial-multiscreen-org:service:dial:1
USER-AGENT: Google Chrome/52.0.2743.116 Mac OS X
DF:Don’t Fragment
IP协议的首部“标志”中标志(flag) 占 3 位,目前只有前两位有意义。
标志字段的最低位是 MF (More Fragment)。
MF =1
表示后面“还有分片”。MF = 0
表示最后一个分片。
标志字段中间的一位是 DF (Don’t Fragment) 只有当 DF =0 时才允许分片。
现在看下如何让我们知道是否有分片发生,当发送设备的MTU大于沿途设备的MTU时,就要分片
分片标志可以在IP头的第七和第八字节找到
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
TCP header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |C|E|U|A|P|R|S|F| |
| Offset| Res. |W|C|R|C|S|S|Y|I| Window |
| | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
过滤来源ip端口是80的数据包.
sudo tcpdump -iany -c 5 -Anlps0 "tcp[0:2] = 80"
过滤区间端口
sudo tcpdump -iany -c 5 -Anlps0 "portrange 80-8080"
TCP协议是有状态的,这些状态在TCP包的第14字节.
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|C|E|U|A|P|R|S|F|
|W|C|R|C|S|S|Y|I|
|R|E|G|K|H|T|N|N|
+-+-+-+-+-+-+-+-+
TCP的三次握手过程
- Source sends SYN 发起方发SYN
- Destination answers with SYN, ACK 目的方回答SYN+ACK
- Source sends ACK 发起方发ACK
例如想筛选传输数据的包,可以这样:
sudo tcpdump -iany -c 1 -n 'port 80 and tcp[13] & 8 = 8'
01:07:21.003121 IP 192.168.31.137.54649 > 14.17.32.223.80: Flags [P.], seq 3921785767:3921786861, ack 3457503231, win 229, length 1094
当然,对于这些flag可以直接使用名字的.
- FIN: tcp-fin
- SYN: tcp-syn
- PSH: tcp-push
tiankonguse:~ $ sudo tcpdump -iany -c 1 -n 'port 80 and tcp[tcpflags] & tcp-push == tcp-push'
01:14:09.648088 IP 192.168.31.137.54691 > 14.17.32.223.80: Flags [P.], seq 2379636258:2379637352, ack 4207633297, win 229, length 1094
tcpdump文档中给的抓有数据的http的实例是这样:
tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
ip[2:2]
代表整个包大小ip[0]&0xf)<<2
代表ip包头大小(ip[2:2] - ((ip[0]&0xf)<<2))
就代表ip包的数据部分大小,既tcp整个包大小((tcp[12]&0xf0)>>2)
代表tcp数据偏移量, 当偏移量和包大小不等时,说明带的有附加数据.
UDP header
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| | |
| Length | Checksum |
+--------+--------+--------+--------+
| |
| DATA ... |
+-----------------------------------+
这个比较简单,没什么讲的, 直接使用tcp的基本过滤规则过滤即可.
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。