• Linux系统诊断必备技能之二:tcpdump抓包工具详解


    一、简述

    TcpDump可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。

    Linux作为网络服务器,特别是作为路由器和网关时,数据的采集和分析是不可少的。TcpDump是Linux中强大的网络数据采集分析工具之一。
    用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具。
    作为互联网上经典的的系统管理员必备工具,tcpdump以其强大的功能,灵活的截取策略,成为每个高级的系统管理员分析网络,排查问题等所必备的工具之一。
    tcpdump提供了源代码,公开了接口,因此具备很强的可扩展性,对于网络维护和入侵者都是非常有用的工具。tcpdump存在于基本的 FreeBSD系统中,由于它需要将网络接口设置为混杂模式,普通用户不能正常执行,但具备root权限的用户可以直接执行它来获取网络上的信息。因此系统中存在网络分析工具主要不是对本机安全的威胁,而是对网络上的其他计算机的安全存在威胁。
    基本上tcpdump的总的输出格式为:系统时间 来源主机.端口 > 目标主机.端口 数据包参数
    完整的英文文档:https://www.tcpdump.org/tcpdump_man.html
    tcpdump官网:http://www.tcpdump.org/
    This is the official web site of tcpdump, a powerful command-line packet analyzer;
    1、tcpdump命令格式
    tcpdump [ -DenNqvX ] [ -c count ] [ -F file ] [ -i interface ] [ -r file ]
            [ -s snaplen ] [ -w file ] [ expression ]
    
    抓包选项:
    -c:指定要抓取的包数量。注意,是最终要获取这么多个包。例如,指定"-c 10"将获取10个包,但可能已经处理了100个包,只不过只有10个包是满足条件的包。
    -i interface:指定tcpdump需要监听的接口。若未指定该选项,将从系统接口列表中搜寻编号最小的已配置好的接口(不包括loopback接口,要抓取loopback接口使用tcpdump -i lo),
                :一旦找到第一个符合条件的接口,搜寻马上结束。可以使用'any'关键字表示所有网络接口。
    -n:对地址以数字方式显式,否则显式为主机名,也就是说-n选项不做主机名解析。
    -nn:除了-n的作用外,还把端口显示为数值,否则显示端口服务名。
    -N:不打印出host的域名部分。例如tcpdump将会打印'nic'而不是'nic.ddn.mil'。
    -P:指定要抓取的包是流入还是流出的包。可以给定的值为"in"、"out"和"inout",默认为"inout"。
    -s len:设置tcpdump的数据包抓取长度为len,如果不设置默认将会是65535字节。对于要抓取的数据包较大时,长度设置不够可能会产生包截断,若出现包截断,
          :输出行中会出现"[|proto]"的标志(proto实际会显示为协议名)。但是抓取len越长,包的处理时间越长,并且会减少tcpdump可缓存的数据包的数量,
          :从而会导致数据包的丢失,所以在能抓取我们想要的包的前提下,抓取长度越小越好。
    
    输出选项:
    -e:输出的每行中都将包括数据链路层头部信息,例如源MAC和目标MAC。
    -q:快速打印输出。即打印很少的协议相关信息,从而输出行都比较简短。
    -X:输出包的头部数据,会以16进制和ASCII两种方式同时输出。
    -XX:输出包的头部数据,会以16进制和ASCII两种方式同时输出,更详细。
    -v:当分析和打印的时候,产生详细的输出。
    -vv:产生比-v更详细的输出。
    -vvv:产生比-vv更详细的输出。
    
    其他功能性选项:
    -D:列出可用于抓包的接口。将会列出接口的数值编号和接口名,它们都可以用于"-i"后。
    -F:从文件中读取抓包的表达式。若使用该选项,则命令行中给定的其他表达式都将失效。
    -w:将抓包数据输出到文件中而不是标准输出。可以同时配合"-G time"选项使得输出文件每time秒就自动切换到另一个文件。可通过"-r"选项载入这些文件以进行分析和打印。
    -r:从给定的数据包文件中读取数据。使用"-"表示从标准输入中读取。 

    常用选项有以下几个:

    a.tcpdump -D
    b.tcpdump -c num -i int -nn -XX -vvv
    

    2、tcpdump 几种关键字说明

    第一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0是一个网络地址,port 23 指明端口号是23。如果没有指定类型,缺省的类型是host.
    第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,这些关键字指明了传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 , dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是src or dst关键字。

    如果我们只需要列出送到80端口的数据包,用dst port;如果我们只希望看到返回80端口的数据包,用src port。 
    #tcpdump –i eth0 host hostname and dst port 80  目的端口是80
    或者
    #tcpdump –i eth0 host hostname and src port 80  源端口是80  一般是提供http的服务的主机
    如果条件很多的话  要在条件之前加and 或 or 或 not
    #tcpdump -i eth0 host ! 211.161.223.70 and ! 211.161.223.71 and dst port 80
    如果在ethernet 使用混杂模式 系统的日志将会记录
    May  7 20:03:46 localhost kernel: eth0: Promiscuous mode enabled.
    May  7 20:03:46 localhost kernel: device eth0 entered promiscuous mode
    May  7 20:03:57 localhost kernel: device eth0 left promiscuous mode
    tcpdump对截获的数据并没有进行彻底解码,数据包内的大部分内容是使用十六进制的形式直接打印输出的。显然这不利于分析网络故障,通常的解决办法是先使用带-w参数的tcpdump 截获数据并保存到文件中,然后再使用其他程序进行解码分析。当然也应该定义过滤规则,以避免捕获的数据包填满整个硬盘。

    第三种是协议的关键字,主要包括fddi,ip,arp,rarp,tcp,udp等类型。Fddi指明是在FDDI(分布式光纤数据接口网络)上的特定 的网络协议,实际上它是"ether"的别名,fddi和ether具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和 分析。其他的几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有协议的信息包。
    除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less,greater,还有三种逻辑运算,取非运算是 'not ' '! ', 与运算是'and','&&;或运算 是'or' ,'||';这些关键字可以组合起来构成强大的组合条件来满足人们的需要,下面举几个例子来说明。

    3、tcpdump 表达式

    表达式用于筛选输出哪些类型的数据包,如果没有给定表达式,所有的数据包都将输出,否则只输出表达式为true的包。在表达式中出现的shell元字符建议使用单引号包围。
    tcpdump的表达式由一个或多个"单元"组成,每个单元一般包含ID的修饰符和一个ID(数字或名称)。有三种修饰符:
    (1).type:指定ID的类型。
    可以给定的值有host/net/port/portrange。例如"host foo","net 128.3","port 20","portrange 6000-6008"。默认的type为host。
    (2).dir:指定ID的方向。
    可以给定的值包括src/dst/src or dst/src and dst,默认为src or dst。例如,"src foo"表示源主机为foo的数据包,"dst net 128.3"表示目标网络为128.3的数据包,"src or dst port 22"表示源或目的端口为22的数据包。
    (3).proto:通过给定协议限定匹配的数据包类型。
    常用的协议有tcp/udp/arp/ip/ether/icmp等,若未给定协议类型,则匹配所有可能的类型。例如"tcp port 21","udp portrange 7000-7009"。
    所以,一个基本的表达式单元格式为"proto dir type ID"

     

    除了使用修饰符和ID组成的表达式单元,还有上述2小节中提到的关键字表达式单元:gateway,broadcast,less,greater以及算术表达式。
    表达式单元之间可以使用操作符" and / && / or / || / not / ! "进行连接,从而组成复杂的条件表达式。如"host foo and not port ftp and not port ftp-data",这表示筛选的数据包要满足"主机为foo且端口不是ftp(端口21)和ftp-data(端口20)的包",常用端口和名字的对应关系可在linux系统中的/etc/service文件中找到。
    另外,同样的修饰符可省略,如"tcp dst port ftp or ftp-data or domain"与"tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain"意义相同,都表示包的协议为tcp且目的端口为ftp或ftp-data或domain(端口53)。
    使用括号"()"可以改变表达式的优先级,但需要注意的是括号会被shell解释,所以应该使用反斜线""转义为"()",在需要的时候,还需要包围在引号中。

     二、tcpdump 具体使用方法

    1、普通情况下,直接启动tcpdump将监视第一个网络界面上所有流过的数据包。

    常用使用方法有:

    A想要截获所有210.27.48.1 的主机收到的和发出的所有的数据包:
    #tcpdump host 210.27.48.1
    
    B想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令:(在命令行中适用 括号时,一定要
    #tcpdump host 210.27.48.1 and / (210.27.48.2 or 210.27.48.3 /)
    
    C如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
    #tcpdump ip host 210.27.48.1 and ! 210.27.48.2
    
    D如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
    #tcpdump tcp port 23 host 210.27.48.1
    
    E 对本机的udp 123 端口进行监视 123 为ntp的服务端口
    # tcpdump udp port 123
    
    F 系统将只对名为hostname的主机的通信数据包进行监视。主机名可以是本地主机,也可以是网络上的任何一台计算机。下面的命令可以读取主机hostname发送的所有数据: 
    #tcpdump -i eth0 src host hostname
    
    G 下面的命令可以监视所有送到主机hostname的数据包: 
    #tcpdump -i eth0 dst host hostname
    
    H  我们还可以监视通过指定网关的数据包: 
    #tcpdump -i eth0 gateway Gatewayname
    
    I 如果你还想监视编址到指定端口的TCP或UDP数据包,那么执行以下命令: 
    #tcpdump -i eth0 host hostname and port 80
    
    J 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包
    ,使用命令:
    #tcpdump ip host 210.27.48.1 and ! 210.27.48.2
    
    K 想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令
    :(在命令行中适用 括号时,一定要
    #tcpdump host 210.27.48.1 and / (210.27.48.2 or 210.27.48.3 /)
    
    L 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
     #tcpdump ip host 210.27.48.1 and ! 210.27.48.2
    
    M 如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
     #tcpdump tcp port 23 host 210.27.48.1

    三、tcpdump 实践经验

     1、查看DNS解析情况

    Linux系统要想正常访问互联网,需要正确配置DNS解析,现在我们已经配置公司内部DNS,我们想看一下DNS解析是否正常,就可以使用tcpdump来抓取DNS包,准备好两个xshell
    窗口,按照如下方式操作:

    1.1、命令:tcpdump -n -i any port 53

    尽量在root用户下使用tcpdump命令,-n表示不要把IP地址解析成域名,-i表示抓取哪块网卡的通信数据包,any表示任意一块,port是指定要抓取数据包的端口,DNS服务工作在53端口上,执行完毕之后,我们切换到第2个窗口,进行下一步;

    1.2、命令:ping -c10 baidu.com

     注解:-c10表示和ping次停下,这时候我们的计算机和百度产生的通信,窗口1的tcpdump就会监听到我们的通信数据,也就是所谓的抓包,切换到窗口1,看到的数据大致如下:

    在上图中我们可看到本地172.16.50.96,端口52892进程向公司本地DNS服务器 172.16.51.151请求告知baidu.com的IP地址是什么?DNS服务器成功给了我们答复,由此可以说明,我们的DNS工作
    正常。

    2、抓取主机指定网卡的数据包

    2.1、命令:tcpdump -i eth0 -c 10 -w data.pcap

    说明:-i 指定要抓取数据包的网卡名称
              -c 指定抓取包的个数;
              -w 保存到指定文件

    注意:可以看到,我们保存的my-packets.pcap是一种特殊文件,直接使用vim是无法查看的,可以把
    该文件拿到windows下,使用wireshark查看;

    四、高级过滤方式

    首先了解如何从包头过滤信息

    proto[x:y]          : 过滤从x字节开始的y字节数。比如ip[2:2]过滤出3、4字节(第一字节从0开始排)
    proto[x:y] & z = 0  : proto[x:y]和z的与操作为0
    proto[x:y] & z !=0  : proto[x:y]和z的与操作不为0
    proto[x:y] & z = z  : proto[x:y]和z的与操作为z
    proto[x:y] = z      : proto[x:y]等于z
    

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

    1、IP头(IPV4)

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

    中文:

    /*IP头定义,共20个字节*/
    typedef struct _IP_HEADER
    {
     char m_cVersionAndHeaderLen;       //版本信息(前4位),头长度(后4位)
     char m_cTypeOfService;            // 服务类型8位
     short m_sTotalLenOfPacket;        //数据包长度
     short m_sPacketID;              //数据包标识
     short m_sSliceinfo;               //分片使用
     char m_cTTL;                  //存活时间
     char m_cTypeOfProtocol;          //协议类型
     short m_sCheckSum;             //校验和
     unsigned int m_uiSourIp;          //源ip
     unsigned int m_uiDestIp;          //目的ip
    } __attribute__((packed))IP_HEADER, *PIP_HEADER ;
    

    a.版本:指IP协议的版本,通信双方使用的IP协议版本必须一致。一般的值为0100(IPv4),0110(IPv6)。
    b.首部长度:长度4比特。这个字段的作用是为了描述IP包头的长度,因为在IP包头中有变长的可选部分。该部分占4个bit位,单位为32bit(4个字节),即本区域值=IP头部长度(单位为bit)/(8*4,因此,一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。
    c.优先级与服务类型:长度8比特,定义了数据包传输的紧急程度以及时延、可靠性、传输成本等。
    d.总长度:16比特,以字节为单位描述IP包的总长度(包括头部和数据两部分),最大值为65535。第二行中标识符、标志和段偏移量通常联合使用,用于数据拆分时的分组和重组。
    e.标识符:对于上层发来的较大的数据包,往往需要拆分。路由器将一个大包进行拆分后,拆出来的所有部分被标上相同的值,该值即为标识符,用于告诉目的端哪些包属于同一个大包。
    f.标志:长度3比特。该字段第一位不使用。第二位是DF(Don't Fragment)位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF(More Fragments)位,当路由器对一个上层数据包分段,则路由器会在除了最后一个分段的IP包的包头中将MF位设为1。
    g.段偏移量:长度13比特,表示一个数据包在原先被拆分前的大包中的位置。接收端据此来还原和组装IP包。
    h.TTL:表示IP包的生存时间,长度8比特。长度8比特。当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包经过每一个沿途的路由器的时候,每个沿途的路由器会将IP包的TTL值减少1。如果TTL减少为0,则该IP包会被丢弃。这个字段可以防止由于路由环路而导致IP包在网络中不停被转发。
    i.协议号:长度8比特,标识上一层即传输层在本次数据传输中所使用的协议。比如6代表TCP,17代表UDP等
    j.首部校验和:长度16位。用来做IP头部的正确性检测,但不包含数据部分。 因为每个路由器要改变TTL的值,所以路由器会为每个通过的数据包重新计算这个值。
    k.源地址:长度32比特,标识IP包的起源地址。
    l.目标地址:长度32比特,表示IP包的目的地址。
    m.可选项:可变长字段,主要用于测试,由起源设备跟据需要改写。
    n.填充:因为IP包头长度(Header Length)部分的单位为32bit,所以IP包头的长度必须为32bit的整数倍。因此,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍。

    2、IP选项

    “一般”的IP头是20字节,但IP头有选项设置,不能直接从偏移21字节处读取数据。IP头有个长度字段可以知道头长度是否大于20字节。
    通常第一个字节的二进制值是:01000101,分成两个部分:
    0100 = 4 表示IP版本 0101 = 5 表示IP头32 bit的块数,5 x 32 bits = 160 bits or 20 bytes
    如果第一字节第二部分的值大于5,那么表示头有IP选项。
    下面介绍有过滤方法
    0100 0101 : 第一字节的二进制
    0000 1111 : 与操作
    <=========
    0000 0101 : 结果

    正确的过滤方法:

    tcpdump -i eth1 'ip[0] & 15 > 5'
    或
    tcpdump -i eth1 'ip[0] & 0x0f > 5'
    

    3、分片标记

    当发送端的MTU大于到目的路径链路上的MTU时就会被分片,分片信息在IP头的第七和第八字节:
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |Flags| Fragment Offset |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    Bit 0: 保留,必须是0
    Bit 1: (DF) 0 = 可能分片, 1 = 不分片
    Bit 2: (MF) 0 = 最后的分片, 1 = 还有分片
    Fragment Offset字段只有在分片的时候才使用。
    要抓带DF位标记的不分片的包,第七字节的值应该是:
    01000000 = 64

    tcpdump -i eth1 'ip[6] = 64'
    

    4、抓分片包  

    4.1、匹配MF,分片包

    tcpdump -i eth1 'ip[6] = 32'
    

    最后分片包的开始3位是0,但是有Fragment Offset字段。

    4.2、匹配分片和最后分片

    tcpdump -i eth1 '((ip[6:2] > 0) and (not ip[6] = 64))'
    

    测试分片可以用下面的命令:  

    ping -M want -s 3000 192.168.1.1
    

    5、匹配小TTL

    TTL字段在第九字节,并且正好是完整的一个字节,TTL最大值是255,二进制为11111111。
    可以用下面的命令验证一下:

    $ ping -M want -s 3000 -t 256 192.168.1.200
    ping: ttl 256 out of range
    
     +-+-+-+-+-+-+-+-+
     |  Time to Live |
     +-+-+-+-+-+-+-+-+

    a.在网关可以用下面的命令看看网络中谁在使用traceroute  

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

    6、抓大于X字节的包  

    a.大于600字节  

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

    7、更多的过滤方式  

    首先还是需要知道TCP基本结构  

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

    /*TCP头定义,共20个字节*/
    typedef struct _TCP_HEADER
    {
     short m_sSourPort;              // 源端口号16bit
     short m_sDestPort;              // 目的端口号16bit
     unsigned int m_uiSequNum;         // 序列号32bit
     unsigned int m_uiAcknowledgeNum;  // 确认号32bit
     short m_sHeaderLenAndFlag;        // 前4位:TCP头长度;中6位:保留;后6位:标志位
     short m_sWindowSize;            // 窗口大小16bit
     short m_sCheckSum;              // 检验和16bit
     short m_surgentPointer;           // 紧急数据偏移量16bit
    }__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
    /*TCP头中的选项定义
     
    kind(8bit)+Length(8bit,整个选项的长度,包含前两部分)+内容(如果有的话)
     
    KIND =
      1表示 无操作NOP,无后面的部分
     
      2表示 maximum segment   后面的LENGTH就是maximum segment选项的长度(以byte为单位,1+1+内容部分长度)
     
      3表示 windows scale     后面的LENGTH就是 windows scale选项的长度(以byte为单位,1+1+内容部分长度)
     
      4表示 SACK permitted    LENGTH为2,没有内容部分
     
      5表示这是一个SACK包     LENGTH为2,没有内容部分
     
      8表示时间戳,LENGTH为10,含8个字节的时间戳
    */
    
    16位源端口号和16位目的端口号。
    32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号,通过这个来确认发送的数据有序,比如现在序列号为1000,发送了1000,下一个序列号就是2000。
    32位确认号:用来响应TCP报文段,给收到的TCP报文段的序号加1,三握时还要携带自己的序号。
    4位头部长度:标识该TCP头部有多少个4字节,共表示最长15*4=60字节。同IP头部。
    6位保留。6位标志。URG(紧急指针是否有效)ACK(表示确认号是否有效)PSH(提示接收端应用程序应该立即从TCP接收缓冲区读走数据)RST(表示要求对方重新建立连接)SYN(表示请求建立一个连接)FIN(表示通知对方本端要关闭连接)
    16位窗口大小:TCP流量控制的一个手段,用来告诉对端TCP缓冲区还能容纳多少字节。
    16位校验和:由发送端填充,接收端对报文段执行CRC算法以检验TCP报文段在传输中是否损坏。
    16位紧急指针:一个正的偏移量,它和序号段的值相加表示最后一个紧急数据的下一字节的序号。
    标志位字段(U、A、P、R、S、F):占6比特。各比特的含义如下:
    
    URG:紧急指针(urgent pointer)有效。
    ACK:确认序号有效。
    PSH:接收方应该尽快将这个报文段交给应用层。
    RST:重建连接。
    SYN:发起一个连接。
    FIN:释放一个连接。
    窗口大小字段:占16比特。此字段用来进行流量控制。单位为字节数,这个值是本机期望一次接收的字节数。
    TCP校验和字段:占16比特。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。
    紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
    选项字段:占32比特。可能包括"窗口扩大因子"、"时间戳"等选项。  

    a.抓取源端口大于1024的TCP数据包

    tcpdump -i eth1 'tcp[0:2] > 1024'
    

    匹配TCP数据包的特殊标记  

    TCP标记定义在TCP头的第十四个字节
    +-+-+-+-+-+-+-+-+
    |C|E|U|A|P|R|S|F|
    |W|C|R|C|S|S|Y|I|
    |R|E|G|K|H|T|N|N|
    +-+-+-+-+-+-+-+-+
    只抓SYN包,第十四字节是二进制的00000010,也就是十进制的2

    tcpdump -i eth1 'tcp[13] = 2'
    抓SYN, ACK (00010010 or 18)
    tcpdump -i eth1 'tcp[13] = 18'
    抓SYN或者SYN-ACK
    tcpdump -i eth1 'tcp[13] & 2 = 2'
    抓PSH-ACK
    tcpdump -i eth1 'tcp[13] = 24'
    抓所有包含FIN标记的包(FIN通常和ACK一起,表示幽会完了,回见)
    tcpdump -i eth1 'tcp[13] & 1 = 1'
    抓RST
    tcpdump -i eth1 'tcp[13] & 4 = 4'
    

    8、常用的字段偏移名字

    8.1、tcpdump考虑了一些数字恐惧症者的需求,提供了部分常用的字段偏移名字:

    a.icmptype (ICMP类型字段)
    b.icmpcode (ICMP符号字段)
    c.tcpflags (TCP标记字段)

    ICMP类型值有:

    icmp-echoreply, icmp-unreach, icmp-sourcequench, icmp-redirect, icmp-echo, icmp-routeradvert, icmp-routersolicit, icmp-timxceed, icmp-paramprob, icmp-tstamp, icmp-tstampreply, icmp-ireq, icmp-ireqreply, icmp-maskreq, icmp-maskreply

    TCP标记值:
    tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-push, tcp-ack, tcp-urg
    这样上面按照TCP标记位抓包的就可以写直观的表达式了:

    a.只抓SYN包  

    tcpdump -i eth1 'tcp[tcpflags] = tcp-syn'  

    b.抓SYN、ACK  

    tcpdump -i eth1 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0'
    

    9、抓SMTP数据  

    tcpdump -i eth1 '((port 25) and (tcp[(tcp[12]>>2):4] = 0x4d41494c))'
    

    抓取数据区开始为"MAIL"的包,"MAIL"的十六进制为0x4d41494c。  

    10、抓HTTP GET数据

    tcpdump -i eth1 'tcp[(tcp[12]>>2):4] = 0x47455420'
    

    "GET "的十六进制是47455420  

    11、抓SSH返回 

    tcpdump -i eth1 'tcp[(tcp[12]>>2):4] = 0x5353482D'
    

    "SSH-"的十六进制是0x5353482D  

    tcpdump -i eth1 '(tcp[(tcp[12]>>2):4] = 0x5353482D) and (tcp[((tcp[12]>>2)+4):2] = 0x312E)'

    五、比较常用的方式 

    如果是为了查看数据内容,建议用tcpdump -s 0 -w filename把数据包都保存下来,然后用wireshark的Follow TCP Stream/Follow UDP Stream来查看整个会话的内容。-s 0是抓取完整数据包,否则默认只抓68字节。用tcpflow也可以方便的获取TCP会话内容,支持tcpdump的各种表达式。 

    1、UDP头

    0      7 8     15 16    23 24    31
     +--------+--------+--------+--------+
     |     Source      |   Destination   |
     |      Port       |      Port       |
     +--------+--------+--------+--------+
     |                 |                 |
     |     Length      |    Checksum     |
     +--------+--------+--------+--------+
     |                                   |
     |              DATA ...             |
     +-----------------------------------+
    /*UDP头定义,共8个字节*/
     
    typedef struct _UDP_HEADER
    {
     unsigned short m_usSourPort;       // 源端口号16bit
     unsigned short m_usDestPort;       // 目的端口号16bit
     unsigned short m_usLength;        // 数据包长度16bit
     unsigned short m_usCheckSum;      // 校验和16bit
    }__attribute__((packed))UDP_HEADER, *PUDP_HEADER;

    a.抓DNS请求数据 

    tcpdump -i eth1 udp dst port 53 
    

    2、系统测试

     -c参数对于运维人员来说也比较常用,因为流量比较大的服务器,靠人工CTRL+C还是抓的太多,甚至导致服务器宕机,于是可以用-c参数指定抓多少个包。

    time tcpdump -nn -i eth0 'tcp[tcpflags] = tcp-syn' -c 10000 > /dev/null
    

    上面的命令计算抓10000个SYN包花费多少时间,可以判断访问量大概是多少。  

    3、tcpdump 与 wireshark

    Wireshark(以前是ethereal)是Windows下非常简单易用的抓包工具。但在Linux下很难找到一个好用的图形化抓包工具。
    还好有Tcpdump。我们可以用Tcpdump + Wireshark 的完美组合实现:在 Linux 里抓包,然后在Windows 里分析包。

    tcpdump tcp -i eth1 -t -s 0 -c 100 and dst port ! 22 and src net 192.168.1.0/24 -w ./target.cap
    
    tcp: ip icmp arp rarp 和 tcp、udp、icmp这些选项等都要放到第一个参数的位置,用来过滤数据报的类型
    -i eth1 : 只抓经过接口eth1的包
    -t : 不显示时间戳
    -s 0 : 抓取数据包时默认抓取长度为68字节。加上-S 0 后可以抓到完整的数据包
    -c 100 : 只抓取100个数据包
    dst port ! 22 : 不抓取目标端口是22的数据包
    src net 192.168.1.0/24 : 数据包的源网络地址为192.168.1.0/24
    -w ./target.cap : 保存成cap文件,方便用ethereal(即wireshark)分析
    

    4、使用tcpdump 抓取HTTP包

    tcpdump  -XvvennSs 0 -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x4854
    

    0x4745 为"GET"前两个字母"GE",0x4854 为"HTTP"前两个字母"HT"。
    tcpdump 对截获的数据并没有进行彻底解码,数据包内的大部分内容是使用十六进制的形式直接打印输出的。显然这不利于分析网络故障,通常的解决办法是先使用带-w参数的tcpdump 截获数据并保存到文件中,然后再使用其他程序(如Wireshark)进行解码分析。当然也应该定义过滤规则,以避免捕获的数据包填满整个硬盘。
    基本上tcpdump总的的输出格式为:系统时间 来源主机.端口 > 目标主机.端口 数据包参数

      

    ---------------------------------------------书山有路勤为径,学海无涯苦作舟-------------------------------------------------------- 

    参考资料

    https://www.cnblogs.com/chenpingzhao/p/9108570.html

      

  • 相关阅读:
    Flutter 布局(九)- Flow、Table、Wrap详解
    Flutter 布局(三)- FittedBox、AspectRatio、ConstrainedBox详解
    广州小程序开发攻略
    Angular2 富文本编辑器 ng2-ckeditor 的使用
    onlyoffice5.4.2离线包下载—解决中文字体问题
    beego+vue父子组件通信(父子页面传值、父子组件传值、父子路由传值)
    beego+vue.js分离开发,结合发布,简单部署
    flow-vue.js移动端效果
    engineecms——工程师知识管理系统,带文档协作和状态和流程
    engineercms支持文档协作和文档流程,基于flow
  • 原文地址:https://www.cnblogs.com/easonscx/p/10616059.html
Copyright © 2020-2023  润新知