TCPDUMP 高级规则使用

概述

在了解 tcpdump 的高级规则之前, 需要对 IP, TCP 和 UDP 的报文首部有大致的了解, 实际上很多网络工具的使用都是基于报文首部的结构做相应的操作. 在了解报文结构后, 也可以按需实现私有的功能, 比如抓取匹配的请求, 再做相应的处理, Snapper 就是根据 TCP 首部信息实现的一个简单的 DoS 防御工具, 详见 https://github.com/vr000m/Snapper, 依次推论, 可以实现更细致的功能, 比如 HTTP 请求过滤, SQL 白名单等; 后续部分则参考一些链接对一些规则进行详细说明.

Edit

协议首部

Edit

IP 首部

IP header: http://tools.ietf.org/html/rfc791#section-3.1

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

                    Example Internet Datagram Header

ip 首部中每行 4 个字节的 32 bit 值以大端(big endian)字节序传输, 先是 0~7 bit, 其次 8~15 最后 24~31 bit, TCP/IP 首部中所有的二进制整数在网络中都以这种次序传输, 所以 big endian 又称网络字节序, 一些机器采用小端(little endian)传输, 则在网络传输前需要把首部换成网络字节序.

IHL: 4bit


IP 首部长度(Internet Header Length), 表示 32 bit 字的数目, 4 bit 最大为 15(1111), 所有首部长度最长为 15*32/8 = 60 byte, 值得注意的是 IHL 的最小值应该是 5 byte(出去选项和数据).

Type of service: 8 bit


服务类型指定了服务质量需要的一些参数; 这些参数用来指示服务在网络传输中需要的一些实际参数, 比如一些服务需要需要更稳定的传输, 一些服务不能有较大的延迟等,都可以通过这些参数预先声明.

Total Length:  16 bits


数据报的总长度. 它包含了首部长度(IHL)和数据长度, 单位为字节. 16 bit 可以允许最大传输 65535 字节.

Identification:  16 bits


标识字段唯一的标识主机发送的每一分数据报, 通常每发送一个报文其值就加 1.

Flags:  3 bits


用于各种控制的标记符号, Bit 0 是保留的, 必须为 0, Bit 1 标识是否有碎片(DF), Bit 2 标识是否为最后一个碎片(MF).

Fragment Offset:  13 bits


碎片的偏移位置. 单位为 8 字节(64 bits), 第一个碎片的偏移为 0.

Time to Live:  8 bits


生存时间(TTL), 设置了数据报可以经过的最多路由器数, 源主机初始设置通常为 32 或 64, 由内核参数 net.ipv4.ip_default_ttl 指定. 每经过一个路由器处理就减去 1, 当该字段的值为 0时,数据报就被丢弃,并发送 ICMP 报文通知源主机.

Protocol:  8 bits


标识数据报中数据部分的协议, 比如TCP, UDP ICMP 等.

Header Checksum:  16 bits


IP首部的校验和, 一些字段的内容可能会改变(TTL等), 校验和用来重新计算并验证网络传输过程中的每端的 header. 校验和的算法是: 首先将校验和的字段置为 0, 再对首部中每个 16 bit (16 位为一组) 进行二进制反码求和, 结果存在该字段中.

Source Address:  32 bits


来源 ip 地址.

Destination Address:  32 bits


目的 ip 地址

Options:  variable


可选项.数据报中的可变长可选信息, 比如可以定义安全, 记录路径, 网络时间戳等.

Padding:  variable


用来填充首部, 保证首部长度时 32bit 的整数倍.

Edit

UDP 首部

http://tools.ietf.org/html/rfc768

udp 数据报封装成 ip 数据报格式:

                 +------------+------------+-----------+
                 | IP header  | UDP header | UDP data  |
                 +------------+------------+-----------+
                    20 bytes      8 bytes

udp header 格式:

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

                      User Datagram Header Format
Source Port: 16 bits
Destination  Port: 16 bits


源端口是可选的, 可以用来表示发送进程, 没有源端口就用 0 表示; 目的端口表示接收进程, TCP 端口和 UDP 端口是相互独立的.

Length: 16 bits


表示 UDP 首部和 UDP 数据的字节长度, 最小值为 8 字节(首部是 8 bytes). 可以为奇数字节.

Checksum: 16 bits


校验和覆盖 UDP 伪首部(pseudo), UDP 首部和 UDP 数据. 为了计算校验和, UDP 和 TCP 都包含 12 字节的伪首部, 伪首部包含 IP 首部的一些字段, 目的是为了保证数据的正确传输. 校验方法和 IP 首部类似, 不过由于 UDP 长度可以为奇数字节, 必要的时候在最后增加填充字节. 校验和包含的结构如下:

                  0      7 8     15 16    23 24    31
                 +--------+--------+--------+--------+  ------
                 |          source address           |
                 +--------+--------+--------+--------+
                 |        destination address        |  pseudo Header
                 +--------+--------+--------+--------+
                 |  zero  |protocol|   UDP length    |
                 +--------+--------+--------+--------+  ------
                 |     Source      |   Destination   |
                 |      Port       |      Port       |
                 +--------+--------+--------+--------+  UDP Header
                 |                 |                 |
                 |     Length      |    Checksum     |
                 +--------+--------+--------+--------+  ------
                 |
                 |          data octets ... (padding)
                 +---------------- ...
Edit

TCP 首部

http://tools.ietf.org/html/rfc793
http://tools.ietf.org/html/rfc3540

tcp 数据报封装成 ip 数据报格式:

   +---------------+-------------+------------+
   |  IP header    | TCP header  |  TCP data  |
   +---------------+-------------+------------+
       20 bytes        20 bytes

tcp 首部格式:

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 |     |N|C|E|U|A|P|R|S|F|                               |
   | Offset| Rsvd|S|W|C|R|C|S|S|Y|I|            Window             |
   |       |     | |R|E|G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            TCP Header Format
Source Port: 16 bits
Destination Port: 16 bits


每个 tcp 数据报都包含来源和目的端口号, 用来寻找发送和接收的应用进程, 这两个信息和 IP 首部中的来源和目的 IP 唯一确定一个 TCP 连接.

Sequence Number:  32 bits


序号用来表示 TCP 发端向手段松松的数据字节流, 它用来表示该 TCP 报文中的第一个数据字节. 在 SYN 为 1 的情况下序号为 ISN + 1, ISN为连接的初始序号. 序号到达 2^32 - 1 后从 0 开始.

Acknowledgment Number:  32 bits


当 ACK 控制位为 1 时, 确认序号包含发送确认的一段所期望收到的下一个序号.

Data Offset:  4 bits


表示首部中 32 bit 的数量, 需要这个字段是因为 Options 字段是可变的.

Reserved:  3 bits


早期版本是 6 bit, 保留位以待将来使用, 必须都置为 0.

Control Bits:  6 bits (from left to right)
    ECN:  Explicit Congestion Notification. 3 bits. (NS, CWR, ECE)
    URG:  Urgent Pointer field significant
    ACK:  Acknowledgment field significant
    PSH:  Push Function
    RST:  Reset the connection
    SYN:  Synchronize sequence numbers
    FIN:  No more data from sender
Window:  16 bits


通过声明窗口大小(单位字节)来进行 TCP 流量控制.

Checksum:  16 bits


类似 UDP, 校验和覆盖了 IP 伪首部, TCP 首部和 TCP 数据.由发端计算并存储, 由接收端进行验证, 伪首部结构如下:

                     +--------+--------+--------+--------+
                     |           Source Address          |
                     +--------+--------+--------+--------+
                     |         Destination Address       |
                     +--------+--------+--------+--------+
                     |  zero  |  PTCL  |    TCP Length   |
                     +--------+--------+--------+--------+
Urgent Pointer:  16 bits


当 URG 标志为 1 的时候该字段才有效. 紧急指针是一个正的偏移量, 和序号字段中的值相加表示紧急数据最后一个字节的序号.

Options:  variable


可选项, 最常见的可选字段是最长报文大小(MSS, Maximum Segment Size).指明本端所能接收的最大长度报文段. 常见的包含:

      Kind     Length    Meaning
      ----     ------    -------
       0         -       End of option list.
       1         -       No-Operation.
       2         4       Maximum Segment Size.
Edit

链路层封装

链路层封装, 通过抓包方式分析 IP/TCP 数据的时候, 可以跳过链路层的 14 字节, 直接分析 IP 数据报文.

   +--------+---------+-------------+---------------+--------+
   |  DST   |   SRC   |  Ether type |       Data    |   CRC  |
   +--------+---------+-------------+---------------+--------+
     6bytes    6bytes     2bytes     (46~1500)bytes   4bytes

DST: 目标 MAC 地址;
SRC: 来源 MAC 地址;
Ether type: 定义后续数据的类型;
CRC: 帧内后续数据的循环冗余校验;

Edit

TCPDUMP 使用

Edit

基础语法

了解上面的结构后, 看看 tcpdump 的高级用法:

https://wiki.wireshark.org/CaptureFilters
http://www.packetlevel.ch/html/tcpdumpf.html
http://www.wains.be/pub/networking/tcpdump_advanced_filters.txt

基本使用可参考上述链接的示例;

tcpdump 支持的表达式:

否  :  ! 或 not
与  : && 或 and
或  : || 或 or

多个条件可以使用括号包起来, 比如:

# tcpdump -i eth1 '(dst host 192.168.1.1 and port 80)'

协议 header 过滤规则:

proto[x:y]           : 从第 x 个位置开始取 y 字节, ip[2:2] 表示取 IP 首部中的第 3,4 字节(x 从 0 开始计算);
proto[x:y] & z = 0   : proto[x:y] 的结果和 z 进行 与 运算, 匹配最终结果为 0的报文;

操作符包括: >, <, >=, <=, =, !=

Edit

IP 规则

结合 IP 首部的信息看看下面的示例, ip[0] 即表示 IP 首部第一个字节, 一共 8 bit, Version 为 0100 即表示 IPv4, IHL 最小的长度应该是 IP 首部的长度 20 字节, 也就是 5 * 32 bits, 所以 IHL 最小值应该是 0101, 01000101 的十进制就是 69. 如果我们要抓取 IP 首部中有 Options 的数据报, 只需要使用一次规则就可以:

# tcpdump -i eth1 'ip[0] > 69'

上面还可以用位操作符的方式进行抓取, 与操作:

0100 0101
0000 1111 (十六进制 0xf, 十进制 15)
==========
0000 0101 (十进制 5)


所以使用以下规则也能满足条件:

# tcpdump -i eth1 'ip[0] & 15 > 5'
# tcpdump -i eth1 'ip[0] & 0xf > 5'

0xf -- 0000 1111
0xf0 -- 1111 0000

如果想知道数据报中是否有碎片, 可以抓取 IP 首部中的 Flags 字段, bit 0 必须为 0, bit 1 为 0 表示可能有碎片, bit 2 为 0 表示最后一个碎片. 所以如果我们可以使用规则匹配:

# tcpdump -i eth1 'ip[6] = 64'    # 没有碎片
# tcpdump -i eth1 'ip[6] = 32'    # 有碎片, 但不匹配最后的碎片
# tcpdump -i eth1 '((ip[6:2] > 0) and (not ip[6] = 64))'   # 匹配最后的碎片

另外想测试碎片信息可以使用 ping 命令检测:

# ping -M want -s 3000 192.168.1.1

ip[2:2] 对应 IP 首部的 total length, 比如以下可以抓取大于 600 字节的数据报:

# tcpdump -i eth1 'ip[2:2] > 600'

ip[8] 对应 IP 首部的 TTL, 如果我们机器的网络访问在 5 跳以内, 可以通过以下规则抓取本网内的数据:

# tcpdump -i eth1 'ip[8] < 5'
Edit

TCP 规则

同理, 在 TCP header 中, tcp[0:2] 即表示来源端口, tcp[2:2] 表示目的端口, 如果要抓取一个范围内的端口, 可以使用以下规则:

# tcpdump -i eth1 '(tcp[0:2] > 1500 and tcp[0:2] < 1550)'


上述等同 tcpdump -i eth1 'tcp portrange 1501-1549'
tcp 三次握手的过程中:

1. 发端发送 SYN;
2. 接收端回应 SYN, ACK;
3. 发送端发送 ACK;


如果只抓取 SYN 报文, 则对应控制位 00000010, 对应十进制 2, 想抓取 SYN + ACK, 则对应 00010010, 对应十进制 18, 如果要匹配 SYN 或 SYN + ACK 报文, 以按位运算应该是:

0001 0010
0000 0010
=========
0000 0010

整个匹配规则可以写成:

# tcpdump -i eth1 'tcp[13] = 2'
# tcpdump -i eth1 'tcp[13] = 18'
# tcpdump -i eth1 'tcp[13] & 2 = 2'


类似的, 要抓取 FIN 报文, 使用规则 'tcp[13] & 1 = 1', 抓取 RST 复位报文, 使用规则 'tcp[13] & 4 = 4', tcpdump 本身也支持标记过滤, 'tcp[tcpflags] == tcp-ack'.

如果要抓取数据相关的报文, 比如 HTTP 的 "GET " 请求, 则需要在 TCP 首部中找到数据部分, 再匹配 'G', 'E', 'T' 和 ' ', 分别对应十六进制 0x47455420, 'tcp[12:1] & 0xf0 >> 4' 得到的结果是 1000, 对应 TCP 首部中的 Data offset 4bit 信息, 即可以得到 TCP 首部长度, 4位信息是表示 32 bit 的数目, 换成字节的话应该是 1000 << 2, 所以'tcp[12:1] & 0xf0) >> 4' 等同 TCP 报文中数据的起始位置, 最后要抓取 HTTP GET请求的规则应该是:

# tcpdump -i eth1 'port 80 and tcp[((tcp[12:1] & 0xf0) >> 4):4] = 0x47455420'
Edit

UDP 首部

UDP 过滤规则相对简单:

udp[0:2]    source port
udp[2:2]    destination port
udp[4:2]    datagram length
udp[6:2]    UDP checksum

更多规则可参考 http://www.packetlevel.ch/html/tcpdumpf.html

Edit

参考

http://tools.ietf.org/html/rfc791#section-3.1
http://tools.ietf.org/html/rfc768
http://tools.ietf.org/html/rfc793
http://tools.ietf.org/html/rfc3540
https://wiki.wireshark.org/CaptureFilters
http://www.packetlevel.ch/html/tcpdumpf.html
http://www.wains.be/pub/networking/tcpdump_advanced_filters.txt

原文地址:https://www.cnblogs.com/ldh-linux/p/5219958.html