仓库源文站点原文


key: 70 title: 使用 tcpdump 抓包 tags: [tools, network]

aside: false

tcpdump 是一个很实用的抓包工具. 一直以来我都只是复制网上的常用命令, 对其使用逻辑缺乏理解. 最近我仔细阅读了它的 manual, 总结一下 tcpdump 的用法.

命令格式

如果使用 tcpdump --help 查看它的使用方法, 总是会得到一大堆参数选项, 至于如何使用还是一头雾水. tcpdump 的用法实际是这样的:

$ tcpdump [选项] [表达式]

tcpdump 会读取网络中的数据, 解析协议, 然后与表达式相匹配. 如果能匹配上, 则用指定的方式输出数据包的内容. 选项则用于指定如何从网络中读取数据 (如指定网络接口) 以及如何输出抓取到的数据.

在深入了解选项和表达式语法前, 先看个简单的例子. 选项 -A 表示用 ASCII 以文本的形式打印数据包的内容, -i 指定网络接口; 表达式 tcp && port 80 表示抓取协议为 tcp, 且端口为 80 的数据包.

$ tcpdump -i eth0 -A 'tcp && port 80'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

这个使用如果我们在这个机器上执行 curl http://luyuhuang.tech 就可以看到 tcpdump 打印出:

16:43:22.947734 IP 172.29.57.43.41858 > luyuhuang.tech.http: Flags [S], seq 3607076262, win 64240, options [mss 1460,sackOK,TS val 2356831936 ecr 0,nop,wscale 7], length 0
E..<..@.@.....9++..+...P.............&.........
.zf.........
16:43:22.961963 IP luyuhuang.tech.http > 172.29.57.43.41858: Flags [S.], seq 1991848100, ack 3607076263, win 65160, options [mss 1424,sackOK,TS val 1839405528 ecr 2356831936,nop,wscale 7], length 0
E..<..@.1...+..+..9+.P..v.0.........g..........
m....zf.....
16:43:22.962003 IP 172.29.57.43.41858 > luyuhuang.tech.http: Flags [.], ack 1, win 502, options [nop,nop,TS val 2356831951 ecr 1839405528], length 0
E..4..@.@.....9++..+...P....v.0............
.zf.m...

上面是 TCP 的三次握手. 每个包会先打印一行基础信息, 称为 "dump line", 如当前时间, 通讯双方的 IP 地址和端口, TCP 的标志位, 序列号, 以及选项等内容. 接下来是包体内容, 以文本形式打印. 如果不是 ASCII 码则打印为 .. 接下来就是 HTTP 请求:

16:43:22.962049 IP 172.29.57.43.41858 > luyuhuang.tech.http: Flags [P.], seq 1:79, ack 1, win 502, options [nop,nop,TS val 2356831951 ecr 1839405528], length 78: HTTP: GET / HTTP/1.1
E.....@.@..g..9++..+...P....v.0......l.....
.zf.m...GET / HTTP/1.1
Host: luyuhuang.tech
User-Agent: curl/7.68.0
Accept: */*


16:43:22.975713 IP luyuhuang.tech.http > 172.29.57.43.41858: Flags [.], ack 79, win 509, options [nop,nop,TS val 1839405541 ecr 2356831951], length 0
E..4..@.1...+..+..9+.P..v.0................
m....zf.
16:43:22.975715 IP luyuhuang.tech.http > 172.29.57.43.41858: Flags [P.], seq 1:368, ack 79, win 509, options [nop,nop,TS val 1839405542 ecr 2356831951], length 367: HTTP: HTTP/1.1 301 Moved Permanently
E.....@.1...+..+..9+.P..v.0..........7.....
m....zf.HTTP/1.1 301 Moved Permanently
Server: nginx/1.20.2
Date: Sat, 26 Nov 2022 08:43:22 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://luyuhuang.tech/

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.20.2</center>
</body>
</html>

16:43:22.975748 IP 172.29.57.43.41858 > luyuhuang.tech.http: Flags [.], ack 368, win 501, options [nop,nop,TS val 2356831964 ecr 1839405542], length 0
E..4..@.@.....9++..+...P....v.2............
.zf.m...

我们可以看到文本形式的 HTTP 请求 GET / HTTP/1.1, 接着是服务器发的 ACK. 然后是服务器发的响应报文 HTTP/1.1 301 Moved Permanently, 最后是客户端发的 ACK.

常用选项

tcpdump 的选项很多, 这里我们只介绍常用的一些选项. 其它的等真正要用的时候再去查 manual 或者 Google 也不迟.

表达式语法

表达式告诉 tcpdump 抓取哪些报文, 它由一个或多个基本表达式组成, 支持用 &&, || 这样的布尔运算符组合它们. 基本表达式的格式为一个或多个修饰词 + ID. 修饰词是预定义的关键字, 如 tcp, host, port 等; ID 则是相应的值, 通常是数字, 地址或名字. 修饰词有三种

  1. 类型修饰词, 表示 ID 所指的类型. 它可以是 host, net, port, portrange 等. 例如 host localhost, net 128.3, port 20, portrange 6000-6008. 如果没有指定类型, 则默认是 host.
  2. 方向修饰词, 指定数据传输的方向. 可以是 srcdst. 因为类型字段通常会区分传输方向, 例如 IP 分组中有源地址和目标地址, TCP 报文段中有源端口和目标端口. 使用方向修饰词可以限定匹配那个方向的类型字段. 如果没有指定方向修饰词, 则匹配双向的类型字段.
  3. 协议修饰词, 指定协议. 可以是 tcp, udp, ip, ip6, arp, ether 等. 因为一些协议有相同的类型字段, 例如 TCP 和 UDP 都有端口. 使用协议修饰词可以限定抓取的协议. 如果没有指定协议修饰词, 则会抓取所有有这个类型字段的协议.

举几个基本表达式的例子

基本表达式可以用逻辑运算符组合起来. tcpdump 的逻辑运算符有与, 或, 非, 可以写作 &&, ||!, 或者 and, ornot. 可以用括号改变运算优先级, 例如 host luyuhuang.tech && (port 80 || port 443).

在组合表达式中, 有时可以省略修饰词. 如果一个基本表达式只提供 ID 而没有修饰词, 则认为它的修饰词与前一个基本表达式相同. 例如表达式 port 22 or 80 or 443, 其中 80443 没有修饰词, 则认为它们的修饰词为 port. 因此这个表达式等价于 port 22 or port 80 or port 443.

下面列出一些修饰词的用法

可以认为一个基本表达式就是在表达某层协议某个字段值为多少. 清楚这一点就很容易理解 tcpdump 的语法了.

   ip    src host   192.168.1.1
|------|----------|--------------|
  协议:    字段:         值:
  TCP     源地址     192.168.1.1

   tcp   dst port     8080
|------|----------|-----------|
  协议:    字段:        值:
  TCP     目标端口     8080

   ip     proto     igmp
|------|---------|-----------|
  协议:    字段:      值:
   IP     协议号     IGMP(2)

高级用法

tcpdump 还支持比较协议中的某些字节, 抓取满足条件的报文. tcpdump 提供一种称为包数据访问器 (packet data accessor) 的语法, 用于获取指定的字节:

PROTO [ POS : SIZE ]

PROTO 表示协议, 可以是 ether, ppp, ip, arp, rarp, tcp, udp, icmp, ip6 等; POS 表示自这层协议起始第几个字节; SIZE 表示在这个位置取几个字节, 其值可以是 1, 2 或 4. 若省略 SIZE 则表示取一个字节. 包数据访问器的值为一个 32 位无符号整数.

包数据访问器可以执行一些算术运算 (+, -, *, /, %, &, |, ^, <<, >>), 然后执行比较运算 (>, <, >=, <=, =, !=). 例如:

例子

这里举一些常用的例子.

扩展阅读

以上的内容基本上足够举一反三, 了解 tcpdump 的使用. 如果想知道更多选项的用法, 可以参考 man tcpdump; 如果需要深入学习表达式, 可以参考 man pcap-filter.