tcpdump命令详解

作者: | 更新日期:

工作两年来使用了无数次tcpdump,但是还是不熟悉很多参数,现在详细记录一下.

背景

工作两年来使用了无数次tcpdump,但是还是不熟悉很多参数,现在详细记录一下.
具体参数请参考man tcpdump.

介绍

我们如果直接使用 tcpdump, 会抓到局域网的所有包, 这显示不是我们想要的, 所以我们需要里了解tcpdump的一些参数,来抓到自己想要的数据包.

这篇文章里我会记录 tcpdump 的这些知识点:

基本用法

指定包数量

-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
...

条件运算

有时候我们希望使用多个条件组合, 我们可以使用条件表达式来筛选自己想要的包.

比如希望抓来源ip是192.168.31.137的回包ip不是192.168.31.136tcp包.

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]得到包中任何位置的值, 从而进行计算筛选.

支持的操作符有下面几个:

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的三次握手过程

  1. Source sends SYN 发起方发SYN
  2. Destination answers with SYN, ACK 目的方回答SYN+ACK
  3. 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可以直接使用名字的.

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)'

UDP header

  0      7 8     15 16    23 24    31  
 +--------+--------+--------+--------+
 |     Source      |   Destination   |
 |      Port       |      Port       |
 +--------+--------+--------+--------+
 |                 |                 |
 |     Length      |    Checksum     |
 +--------+--------+--------+--------+
 |                                   |
 |              DATA ...             |
 +-----------------------------------+

这个比较简单,没什么讲的, 直接使用tcp的基本过滤规则过滤即可.

点击查看评论

关注公众号,接收最新消息

关注小密圈,学习各种算法

tiankonguse +
穿越