# 25 | 抓包分析的回顾、拾遗,和提高 你好,我是胜辉。 在完成了三个实战模块之后,我们已经把三层以上,也就是网络层、传输层、应用层等的排查技术都比较完整地学习了一遍。在二十多节课里,我们学习了对TCP的各种行为,比如握手、挥手、拥塞、重传等行为进行排查的技巧,也对tcpdump和Wireshark进行了深度使用。 除了这些技巧以外,我们更是在TLS加解密和应用层HTTP协议等理论知识方面,做了不少深入的研究。我想,很多工具的使用技巧、案例的排查思路已经在我们的脑海中回荡,那么如果我们来一次系统性的总结和提高,对我们的学习效果一定是一种更大的促进。 所以在这节课里,我们就来对过去的二十多讲进行一次回顾、总结和提高吧。 ## tcpdump和tshark等命令行工具的技巧 tcpdump是我们用得最多的抓包工具,其中的基本技巧可以汇总为以下几个方面。 ### tcpdump 我们做抓包,一般都需要指定过滤条件,这样才能保证对系统的CPU、内存、硬盘等资源不产生过大的影响。这里说的过滤条件又分基础参数和真正的过滤条件,我们分别来看一下。 #### 基础参数 这类基础参数可能不算是用来过滤报文本身的参数,它们一般用于指定抓包的网口、报文长度等等。我们来逐个看一下。 要限定网络接口,可以用-i参数。比如: ```bash tcpdump -i eth0 ``` > 补充:这里还有个小的注意点。当我们用 `-i any` 的时候,抓到的报文就不是标准以太网帧头格式了,这一点你需要知道一下。 如果我们要查看抓包过程中的详细信息,下面这三种参数任你选择: ```bash tcpdump -v #提供TTL等信息 tcpdump -vv #提供更多信息 tcpdump -vvv #提供最详细的信息 ``` > 补充:如果你还是用 `-w file.pcap` 存入了文件,加上 `-v` 参数可以每10秒打印一次抓取的报文数量。 默认情况下,tcpdump会对IP进行反向域名查询等操作,这些DNS查询等行为可能影响抓包的效率。这时候就可以加上 `-n` 参数: ```bash tcpdump -n ``` 为了节约抓包成本,我们经常会指定要抓取的每个报文的长度。比如,假如TCP没有启用扩展头部,那么指定54字节即可抓取到从二层帧头部到TCP头部的这些信息了。 ```bash tcpdump -s 54 ``` #### tcpdump过滤条件 抓包的过滤条件当然是重中之重的技巧了,这里我们再复习一下。 要限定IP,可以用下面的过滤器: ```bash tcpdump host 10.10.10.10 tcpdump dst host 10.10.10.10 tcpdump src host 10.10.10.11 ``` 要限定端口,可以用这个: ```bash tcpdump port 22 ``` 我们有时候也需要从已有的抓包文件中过滤出报文,然后存入一个新的文件。比如下面的例子: ```bash tcpdump -r file.pcap 'tcp[tcpflags] & (tcp-rst) != 0' -w rst.pcap ``` ### tshark tshark是一个跟随Wireshark一起安装进系统的工具,类似情况的工具还有editcap、mergecap、capinfos等等,算是Wireshark大礼包了。这些工具都有很强大的功能,这个大礼包真是物超所值。 我们先看tshark。在课程中,我们主要用tshark实现了三个不同的需求场景。 第一个场景是**统计文件中的HTTP状态码**。我们可以到[Gitee](https://gitee.com/steelvictor/network-analysis/tree/master/17)里找到相关的抓包示例文件,然后运行下面的命令: ```bash tshark -r lesson17-in-shorten.pcap -T fields -e http.response.code | grep -v ^$ | sort | uniq -c | sort -r ``` 输出如下: ```bash 2931 200  704 502  227 304  141 400   45 301   41 302   16 408   14 403    6 503    6 404    2 206 ``` 可见,HTTP状态码都非常整体地统计展现出来了。所以我们做抓包分析,不仅要熟练掌握Wireshark,同时也应该多了解这些命令行工具,它们经常可以提供Wireshark以外的一种良好的补充。 第二个场景是**解析文件中的HTTP事务的耗时**。比如我们可以用下面的命令: ```bash tshark -r 文件名 -T fields -e http.time | grep -v ^$ ``` 第三个场景,是在找到HTTP耗时最高的事务,然后找这个**事务所在的整个TCP流的所有报文**,我们可以用下面的命令: ```bash tshark -r 文件名 -T fields -e frame.number -e http.time -e tcp.stream | sort -k2 -r | head -1 | awk '{print $3}' | xargs -n 1 -I {} tshark -r captured.pcap -Y "tcp.stream eq {}" ``` 这个命令比较长,用到了多次管道符,特别是最后一个管道符后面,使用了 `tshark -Y "tcp.stream eq xx"`,而这个过滤器就是把某个特定的TCP流给完整地展示出来,这样的话我们不仅可以找到HTTP耗时最高的事务,而且这个事务的完整的TCP流里的报文都展示了出来,方便我们做进一步的分析。 tshark的功能有很多,比如它还可以查看TCP初始往返时间: ```bash tshark -r file.cap -T fields -e tcp.analysis.initial_rtt | grep -v ^$ ``` 也可以统计TCP重传包的数量: ```bash tshark -n -q -r file.pcap -z "io,stat,0,tcp.analysis.retransmission" ``` 最后,tshark其实也经常用来直接抓包,这时候它跟tcpdump是差不多的。比如下面这样: ```bash tshark -i "Wireless Network Connection" -w file.pcap host 123.45.66.77 ``` ### capinfos 这个工具主要是用来获取抓包文件的整体信息,比如我们使用下面这个命令,就获取到了抓包文件的报文数、文件大小、抓包时长等丰富的信息。 ```bash $ capinfos lesson17-in-shorten.pcap File name:           lesson17-in-shorten.pcap File type:           Wireshark/tcpdump/... - pcap File encapsulation:  Ethernet File timestamp precision:  microseconds (6) Packet size limit:   file hdr: 65535 bytes Packet size limit:   inferred: 150 bytes Number of packets:   88 k File size:           9769 kB Data size:           115 MB Capture duration:    297.564908 seconds ...... ``` 它的功能跟Wireshark里Statistics下拉菜单里的Capture File Properties是类似的: ![图片](https://static001.geekbang.org/resource/image/1f/65/1ffa6ed19402ed396364c828b38bf065.png?wh=298x177) ### editcap 有时候我们需要传递抓包文件,同时又不想透露应用层数据等敏感信息,那么一个简便的做法,就是直接修改抓包文件中每个报文的长度。前面说过,`tcpdump -s` 是可以指定抓包大小的,而即使文件已经生成了,我们还是可以**通过editcap名把它改小**。是不是挺方便的? 比如要把原始文件 `old.pcap` 的每个报文截短为54字节,可以执行下面的命令: ```bash editcap -s 54 old.pcap new.pcap ``` 另外,editcap也可以用来把TLS key文件跟抓包文件合并在一起,这样你在分享文件的时候,就不需要传送两个文件了,而是一个合并过的文件就可以。比如下面的命令: ```bash editcap --inject-secrets tls,key.log in.pcap out.pcap ``` > 注意:命令中的 `tls` 跟后面的 `key.log` 文件名之间有一个逗号,虽然略有点奇怪,但这就是它的格式。 ## Wireshark的技巧 Wireshark可以算是我们课程中最闪亮的明珠了。不过很多同学在学习这门课之前,可能对Wireshark的了解很少,一打开文件就晕了,心里只有一个念头“不明觉厉”。当然,通过课程的学习,相信你对这方面已经积累了相当不错的经验。如果你现在看到Wireshark窗口甚至有“亲切”的感觉,那就对了,说明你有了长足的进步,已经真正跟Wireshark成为密友了。 这里我们再来简单回顾一下相关的技巧。 ### Wireshark的解读技巧 在解读抓包文件时,第一步一般可以查看Expert Information。这个信息的解读方法是这样的: * Warning条目的底色是黄色,意味着可能有问题,应重点关注。 * Note条目的底色是浅蓝色,是在允许范围内的小问题,也要关注。 * Chat条目的底色是正常蓝色,属于TCP/UDP的正常行为,比如这次通信里TCP握手和挥手分别有多少次等,可以作为参考。 * 一般来说,乱序可以重点关注。 在解读完Expert Information之后,我们会找到可疑的报文,展开进一步的分析。此时我们用得最多的技巧,可能就是**Follow TCP Stream**了。我们可以选中一个报文后右单击,然后选择Follow,在二级菜单中选择TCP Stream,这样就来到了这个报文所在的TCP流。基本上每次做抓包分析都会用到这一步的操作。 ![图片](https://static001.geekbang.org/resource/image/7f/yy/7f82e0a848203641b591cbb7e1de76yy.png?wh=747x519) 另外,Wireshark默认展示的列经常不能满足我们的需求,所以我们也要学会**添加自定义列**的方法。简单来说,就是选择一个报文后,进入它的详情部分(界面下方),然后选中某一个属性,右单击,选择Apply as Column即可。比如下面这样: ![图片](https://static001.geekbang.org/resource/image/5d/4a/5d3f3de2c419fd0ae2be8fc4085b754a.jpg?wh=819x416) 上图中,我们通过添加TTL自定义列,就可以让每个报文的TTL值都在主视图中展现,极大地方便了对这些TTL的比较。所以我们除了掌握协议知识以外,也要挖掘各类工具的使用技巧。所谓“工欲善其事,必先利其器”也。 另外,在排查中,**时间**这个信息也是十分重要的。根据场景的不同,我们需要不同的Time列的表示方式。比如: * 当我们关注**前后报文间的间隔时间**时,我们会选择Delta time或者Delta time displayed。 * 当我们关注**跨度比较大的报文间隔**时,最好选择Absolute time或者另外两个Absolute date的形式。 ![图片](https://static001.geekbang.org/resource/image/bc/d9/bce1f5aa452d84c742dc6d9a119699d9.jpg?wh=602x195) ### 对TCP提示的解读 Wireshark很方便也很强大,但如果我们脱离了对网络协议的理解,那其实还是无从下手。比如我们做得最多的TCP排查,就需要结合自己对TCP的掌握才能做好解读。 举个例子。同样是Wireshark关于TCP窗口方面的提示,TCP Window Full跟TCP Zero Window都是什么含义,有什么差别呢?像这样的例子很多,如果你真正地从Wireshark给你的众多信息中获取了有效的线索,那你的排查能力必定有一个明显的提升了。 #### TCP Window相关的信息 就TCP Window Full而言,它的意思是**一端的在途字节数等于另一端的接收窗口**。Wireshark会根据发送出去的数据量和已经确认的数量,计算出在途字节数,然后跟另一端的接收窗口进行比较。如果两者相等,说明另一端的接收窗口已经被用满了。这个对于排查TCP传输速度相关的问题是很有帮助的。 然后是TCP Zero Window,这个比较简单,就是**接收窗口为零**。这是主动通告对端“自己的接收窗口已经为零,不要再发送数据了”。所以你能看出来它跟TCP Window Full的区别了吗? 它们的**区别**是:TCP Zero Window是一个体现在TCP报文里的信息,算是主动“投降”。而TCP Window Full不是在报文中的信息,它是Wireshark自己分析出来的结论。知道了两者的区别,相信你也知道在相应的场景下,该如何结合这种解读来帮助排查了。 #### TCP传输状况信息 关于TCP传输速度也是一个热门话题,我们在第9~11讲都做了比较深入的探讨。在Wireshark里,我们可以利用下面几个图表来帮助排查。 * I/O Graph:这个可以看到IP报文级别的传输速度。 * TCP Stream Graphs:这里又分了几种子图表,分别是: * Time Sequence (Stevens):查看TCP序列号随着时间的变化趋势。 * Throughput:查看传输数据量的变化趋势。 * Round Trip Time:查看传输中RTT的变化。 * Window Scaling:查看接收窗口在传输中的变化,还可以直观地找到TCP Window Full事件。 你可以在Statistics下拉菜单中找到上面的这些工具。 ![图片](https://static001.geekbang.org/resource/image/e9/b3/e94dd4yydeb31d9f1f19511fdc1a3ab3.jpg?wh=457x101) ### Wireshark过滤器 过滤器这一块也是很丰富且很有价值的内容。有时候,能否写出一个高效准确的过滤器,几乎可以当做是区分我们抓包分析水准的量尺,所以对过滤器再重视也不为过。下面我们来回顾几个典型的过滤器。 在**匹配字符串**的时候,我们可以有以下这样的好几种选择,分别在不同的分层上实现了文本过滤。 ```bash tcp contains "abc" ip contains "abc" http contains "abc" ``` 那如果要**匹配二进制数据**该如何做到呢?我们知道,每个字节是8位,Wireshark用两个十六进制数字表示一个字节,我们在每个字节之间加上破折号就可以了。比如下面这样也可以搜到带“abc”的报文: ```bash tcp.payload contains 61-62-63 ``` 当我们要根据TCP报文的标志位进行过滤的时候,就可以用下面这种过滤器: ```bash tcp.flags.reset eq 1 #找到RST报文 tcp.flags.syn eq 1 #找到握手的SYN报文 tcp.flags.fin eq 1 #找到挥手的FIN报文 ``` 我们还可以根据时间匹配来过滤报文,比如: ```bash frame.time >="Mar 28, 2022 00:00:00" ``` #### tcp.analysis类过滤器 此外,Wireshark的过滤器中还有一个非常重要的部分,是TCP分析器tcp.analysis,当你在Wiresahrk过滤器栏输入tcp.analysis后,就自动有很多的过滤器提示出来。这些过滤器经常能起到很大的帮助,因为它们大多不是直接的报文内容,而是从报文中推导出来的信息,已经包含了一定的分析价值。 比如下面这个过滤器,可以找到超过200ms才发回的确认报文: ```bash tcp.analysis.ack_rtt >0.2 and tcp.len == 0 ``` 当我们要找到所有的TCP Window Full的报文的时候,就可以输入: ```bash tcp.analysis.window_full ``` 更多的tcp.analysis过滤器你可以自己去Wireshark里摸索一下,相信你会有很大的收获。 ## 排查技巧 其实,网络排查很多人都在做,也都会抓包,那为什么不是每个人都可以从抓包文件中分析出有效的信息来呢?可能关键就在于我课程里提到过的**两大鸿沟**。 * 应用现象跟网络现象之间的鸿沟:你可能看得懂应用层的日志,但是不知道网络上具体发生了什么。 * 工具提示跟协议理解之间的鸿沟:你看得懂Wireshark、tcpdump这类工具的输出信息的含义,但就是无法真正地把它们跟你对协议的理解对应起来。 为了突破第一个鸿沟,我们需要熟悉抓包技术,知道什么时候抓包、抓哪些包,然后也有能力把抓包文件中的信息,跟应用层日志中的信息对应起来。 为了突破第二个鸿沟,我们需要深刻理解网络协议,知道TCP、IP、HTTP这些协议的行为和脾性,然后结合Wireshark的各种技巧,把网络协议跟实际现象结合起来,从而推进排查工作。 当然,填平这两个鸿沟只是我们的目标,而手段就是这里要讨论的排查技巧了。这里的内容也比较多,我选了几个典型场景和思路,希望对你有所启发。 ## 逐段测试法 我们经常会遇到一个请求从开始到结束会经过很多环节的情况,比如: > 客户端-> 第四层LB -> 第七层LB -> 服务端 在这种时候,我们可以做逐段测试,比如从客户端直接访问服务端,这样可以很快地确定中间这两个环节是否跟问题有直接关系了。比如像下面这样: ![](https://static001.geekbang.org/resource/image/5d/0e/5d65cc33e0dcac560610779f92c2070e.jpg?wh=2000x435) ### 抓包分析思路 在进入抓包分析阶段后,从经验性的排查思路这个角度来说,一般的抓包分析可以用我们在[第5讲“定位防火墙(一)”](https://time.geekbang.org/column/article/481042)中提到过的步骤,也就是: * 查看 Expert Information; * 重点关注可疑报文(比如Warining级别),追踪其整个TCP流; * 深入分析这个可疑TCP流的第二到四层的报文,再结合应用层表象,综合分析其根因; * 结合两侧的抓包文件,找到属于同一个TCP流的数据包,对比分析,得出结论。 下图就概括了这个过程,你可以来参考一下: ![](https://static001.geekbang.org/resource/image/dd/ac/dd1958be82b3ea019ffef41ffbc120ac.jpg?wh=2000x360) ### 整数值 在有整数值出现的时候,我们需要提起精神,务必重点调查这个整数值背后的原因。因为一般来说,这往往意味着**有人为的设置**在里面,比如某种客户端超时设置。这些设置经常会跟问题有直接或间接的关系。 比如在[第15讲](https://time.geekbang.org/column/article/488979),我们就发现客户端有5秒超时的机制,并在Wireshark里体现了这一点。 ![图片](https://static001.geekbang.org/resource/image/ea/cb/eab1ef276008a85d53f6a222034b89cb.jpg?wh=827x221) ### 偶发性问题 对于偶发性问题,我们可以采用这样的策略: > 预估->开始抓包和观察->问题重现->停止抓包->分析 下图概括了这个过程,供你参考: ![](https://static001.geekbang.org/resource/image/2a/dd/2aa3b64b995d21037cb9323ccc57d9dd.jpg?wh=2000x1125) ### 对比分析 还有一个很关键的技术是“对比分析”。我们做抓包分析,经常要在多处抓包后做对比分析。比如以下两种典型场景: * 同一个TCP流在客户端和服务端的抓包文件; * LB前后不同TCP连接的抓包文件。 对于第一种场景,我们可以**用报文本身的字段**作为匹配条件,因为属于同一个TCP流,它们的这些属性字段值一定不会变化。比如用裸序列号(原始序列号),像下面的过滤器: ```bash tcp.seq_raw eq 123456 ``` 找到同一个TCP流之后,就可以把两个Wireshark窗口都打开,放在一起进行比较。你可以回顾下第5讲的案例,参考我们是如何做对比分析的。 ![图片](https://static001.geekbang.org/resource/image/e8/94/e8c1561009a05c1c9523dbef2fd8bb94.png?wh=1303x303) 而对于第二种场景,TCP连接就是完全不同的了,所以无法像上面第一种场景那样,把TCP报文里的字段值作为关联映射的条件。我们一般可以**寻找应用层的特征**,比如某个uuid,然后运用Wireshark过滤器,在两侧不同TCP连接的抓包文件中,找到同样包含这个uuid的报文。比如这些过滤器: ```bash http contains "uuidxxxxx" tcp contains "uuidxxxxx" ip contains "uuidxxxxx" frame contains "uuidxxxxx" ``` ## 小结 这节课,我们是对整个课程中,不少抓包分析的技术细节进行了回顾。我在[春节特别放送(三)](https://time.geekbang.org/column/article/484358)里其实提到过,思维导图是一个很好的学习工具,所以我就汇总整理了专栏中的核心内容。因为内容也比较多,我分为了4个部分来分别展示,你可以通过它们快速找到你需要的知识点。 * **排查技术** ![](https://static001.geekbang.org/resource/image/f0/c2/f034075fyybd5eccf3c61494dcf731c2.jpg?wh=4000x3615) * **协议** ![](https://static001.geekbang.org/resource/image/22/fe/22b139be4e6a642ee0792152938c22fe.jpg?wh=4000x2482) * **系统相关** ![](https://static001.geekbang.org/resource/image/c8/a3/c8d8628bdc9a7b5051b0bffddbb189a3.jpg?wh=4000x2493) * **抓包分析** ![](https://static001.geekbang.org/resource/image/18/fb/18b39b2624bb23afd24f7fe52caf23fb.jpg?wh=4000x2022) ## 思考题 给你留两道思考题: * 假设通信的一端发送了TCP Zero Window报文,并且后续没有更多新的报文过来,那么另一端是不是一直不能发送新的数据了呢? * 请写一个tshark命令,用来过滤出抓包文件中的DupAck。 欢迎你把答案分享到留言区,我们一同进步、成长。