zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    leetcode 414
    Leetcode 495
    Leetcode 485题
    Python 24点(2)
    python 24点
    我的第一次作业
    Django
    multiprocessing模块
    遍历文档树
    shutil模块
  • 原文地址:https://www.cnblogs.com/ldh-linux/p/5219958.html
Copyright © 2011-2022 走看看