上面是Wireshark用户手册的链接,整体情况请点击。
一,准备工作
Wireshark提供Mac版本,可以从官网下载安装,到这篇博客为止最新版本应该是2.2.1。安装好之后打开的第一个界面如下:
Wireshark在第一个界面就把当前系统所包含的网卡列出来了,直接点击任何一项就可以开始监听通过该网卡的所有网络流量。
当我们把iPhone通过usb连接macbook时,Wireshark并不能直接监听通过iPhone的网络流量,需要通过一个系统程序在我们的Mac系统上,建立一个映射到iPhone的虚拟网卡,在terminal中输入如下命令即可:格式为:rvictl -s [UUID]
然后选择下图中蓝色的网卡,既可抓当前的iPhone包;
Wireshare即进入如下界面开始监听iPhone设备上的所有流量。
此时,启动iPhone上的任意App,只要有网络流量产生,对应的网络包都会在Wireshark上述的列表中展示出来。
Wireshark的流量监控界面主要分为四块,由上至下;
第一部分(标号为1)是工具栏,通过工具栏我们可以控制监控的行为,比如开始抓包,停止抓包,重新开始抓包,以及在包之间跳转等等。工具栏的底部有个输入框,可以让我们手动输入包的过滤条件,这部分对于熟练使用Wireshark抓包非常重要,后面会详细的讲解。
第二部分(标号为2)是历史流量包列表展示界面,这里展示的是从抓包开始,所有通过我们iPhone设备的流量。列表界面不同的包有不同的颜色,Wireshark通过颜色来区分包的类型,对于特定场景快速识别目标流量非常有用,后面也会专门讲解。
第三部分(标号为3)是单个包的详细信息展示面板,我们在第二部分选中的网络包在这一部分会将其结构以可阅读的文本形式展示出来,要正确阅读这一部分的信息需要对tcp/ip协议有一定的掌握。
第四部分(标号为4)是单个包的二进制流信息展示面板,这一部分展示的信息是包的原始数据,也是一个网络包所包含内容的真实展现,我们在第三部分多选中的协议头,都会在这一部分以同步高亮的形式标记出来。这一部分的展示是为了让我们对包的真实内容做直观的判断,能具体到单个byte。
二,简单的Filter过滤包
使用Wireshark和使用Charles最大的区别在于,Charles只捕获HTTP流量,而Wireshark捕捉的是经过目标网卡所有的流量,流量包可以在几秒内膨胀到难以阅读的数量,所以此时我们需要使用Filter来做包的过滤,Filter规则定的越细,剔除掉的干扰信息就越多,分析起来就越快。
Wireshark的Filter分为两种,一种为Capture Filter,另一种是Display Filter。
Capture Filter出现在初始界面,在网卡列表的上方有个输入框,允许我们输入capture filter,一旦输入了特定的capture规则,Wireshark就只捕获符合该规则的流量包了。
Display Filter出现在流量监控界面,在工具栏的下方有个输入框,允许我们输入display filter,display filter只是从界面上过滤掉不符合规则的包,Wireshark实际上还是监听了这些包,一旦去掉display filter,所有的包又会出现在同一界面。
Capture Filter的规则和我们平常使用tcpdump的filter语法是一致的,比如为了只监控http的流量,我们可以先在初始化界面选中rvi0网卡,再在capture filter输入框里输入:
//只捕获HTTP流量
port 80 or port 443
回车之后Wireshark就开始监控我们iPhone上所有的http和https流量了 ,非常简单,我们还可以使用其他的capture filter来捕获特定的流量,比如想分析DNS解析过程,可以使用:
//只捕获DNS流量
port 53
比如只想捕获和特定服务器相关的流量:
//只捕获和特定主机的流量
host 171.10.191.10
Display Filter的语法是由Wireshark自定义的,和Capture filter的语法不能混用。比如我们只想看某个主机的流量,可以使用如下Display Filter:
ip.addr==171.10.191.10
如果只看http或者https的流量,可以用:
tcp.port == 80 || tcp.port == 443
Wireshark实际上提供了便捷的UI操作帮助我们来书写Display Filter,在Display Filter输入框的最右边有个Expression按钮,点击之后可以弹出如下界面:
Display Filter的语法本质上是个等是关系描述,我们可以在search当中输入我们感兴趣的协议比如http,再在展开的协议头里选择我们的条件比如http.host,最后设置Relation和Value就可以生成一个Display Filter条件了。
2.1 包颜色规则
Wireshark在大多数时候捕获的包数量都远超我们感兴趣的数量,而且各个连接的包都混杂在一起,为了方便我们识别不同的连接会话,Wireshark默认使用一种着色规则帮助我们来进行包类型区分。
具体的规则可以通过菜单View->Coloring Rules...查看,默认规则如下:
这里有个小技巧,如上图所示,我只将我感兴趣的协议包上了色,集中在http,tcp,udp包,这样分析起来更加直观。比如根据上图的规则,tcp三次握手中的Sync包是使用灰色标记的,这样我就可以在下图的包中迅速定位一次tcp连接的开始包位置:
当然,包的颜色也可以按照自己的视觉习惯进行定制,我个人习惯把Sync包和FIN包设置一个高亮的颜色,方便判断一次HTTP会话的起始和结束。
2.2 流量跟踪
Wireshark默认情况下将不同网络连接的流量都混在一起展示,即使给不同协议的包上色之后,要单独查看某个特定连接的流量依然不怎么方便,我们可以通过Wireshark提供的两种方式来实现这个目标。
方式一:Follow Stream
当我们选中某个包之后,右键弹出的菜单里,有个选项允许我们将当前包所属于的完整流量单独列出来,如下图:
Wireshark支持我们常见的四种Stream,TCP,UDP,HTTP,SSL。比如我们选中Follow TCP Stream之后可以得到如下的详细分析输出(样本为监控iPhone手机的流量):
上图中将iPhone和Server之间某次的连接流量完整的呈现出来,包括iPhone发送了多少个包,Server回了多少个包,以及iPhone上行和下行的流量,还提供流量编解码选择,文本搜索功能等。
三,Wireshark与http&https
HTTP的工作方式并不复杂,先由客户端向服务端发起一个请求,再由服务器回复一个请求。但是因为服务器与客户端并不是物理上直接相连,而是通过DNS,NAT等等一系列网络设备进行计算,不可避免的需要先确定对方的存在与否。这个确定过程就是HTTP的三次握手;
我再APP中使用WKWebView加载http://www.rfc-editor.org/info/rfc2616时候抓包如下,探讨HTTP的工作原理
3.1 三次握手
3.1.1,第一条protocol为DNS而且只有一个包,这是因为我的demo多次运行,DNS已经4.31.198.49与http://www.rfc-editor.org/info/rfc2616的映射,所以客户端下一步可以与4.31.198.49进行通讯。
3.1.2,图中第二条数据的protocol为TCP,由于HTTP协议基于TCP,所以开始三次握手,从上图可以看出Src Port(客户端):55906,Dst Port(服务器):8。TCP数据包中,seq表示这个包的序号,注意,这个序号不是按1递增的,而是按tcp包内数据字节长度加上,如包内数据是21字节,而当前IP1发到IP2的包的seq是10的话,那下个IP1发到IP2的包的seq就是10+21=31。
3.1.3,每个tcp包都带有win、ack,这些是告诉对方,我还可以接收数据的滑动窗口是多少,如果A发到B的包的win为0,就是A告诉B说我现在滑动窗口为0了,饱了,你不要再发给我了,就说明A端环境有压力(如带宽满了等)
3.1.4,ack可以理解为应答。A发给B的ack是告诉B,我已收到你发的数据包,收到ack号这里了,你下次要发seq为ack号的给我
3.1.5,在网络不堵即滑动窗口一点都不堵的情况下,第一个包的ack号就是第二个包的seq号,如果堵了,由于是滑动窗口缓存处理队列,所以这个值会错开 (即拥塞避免,快重传,慢启动等一系列避免网络拥塞的策略)
3.1.6,注意我们分析tcp包时,要以一个会话做为一个完整对象,即通讯只发生在两个IP之间,两个固定的端口之间,如果端口变化了,那链接就不是同一条了,不同的链接之间的seq是没有关联的。因为我之前已经设置了Capture Filter为host 10.0.78.73(本机iPhone的ip),所以在这里可以设置display filter的规则为ip.dst == 4.31.198.49 || ip.dst == 10.0.78.73,即我只显示ip为4.31.198.49与10.0.78.73的数据包。
3.2 HTTP包
3.2.1,上图中灰色的数据包是客户端向服务端发送的“GET /info/rfc2616 HTTP/1.1”请求,即通过1.1版的HTTP协议,获取/info目录里的rfc2616文件。说白了就是想下载页面的内容。
3.2.2,图中蓝色行及其下方为一个HTTP请求的完整请求内容,即:请求行、请求头(headerField)、请求体(body);同理,响应也有状态行、响应头、实体内容。接下来我们逐个展开。
1,请求行
包含请求方法(Method)、请求统一资源标识符(URI)、HTTP版本号,如图:
请求方法就是我们所熟悉的POST、GET、HEAD、PUT等
URI就是URL中排除掉Host剩下的部分,也就是资源在服务器本地上的路径
HTTP版本号,目前主流的版本是1.1(1999年开始采用),最新的版本是2.0(2015年5月发布)。不同版本之间差异下面会再展开
2、请求头
请求头主要存放对客户端想给服务端的附加信息,下图框框的部分就是请求头(上方图的部分):
HTTP请求在iOS中用NSURLRequest与NSMutableRequest表示;HTTP响应用NSHTTPURLResponse表示。
Host: 目标服务器的网络地址
Accept: 让服务端知道客户端所能接收的数据类型,如text/html */*
Content-Type: body中的数据类型,如application/json; charset=UTF-8
Accept-Language: 客户端的语言环境,如zh-cn
Accept-Encoding: 客户端支持的数据压缩格式,如gzip
User-Agent:
客户端的软件环境,我们可以更改该字段为自己客户端的名字,比如QQ music v1.11,比如浏览器Mozilla/5.0
(Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/600.8.9 (KHTML, like Gecko) Maxthon/4.5.2
Connection: keep-alive,该字段是从HTTP 1.1才开始有的,用来告诉服务端这是一个持久连接,“请服务端不要在发出响应后立即断开TCP连接”。关于该字段的更多解释将在后面的HTTP版本简介中展开。
Content-Length: body的长度,如果body为空则该字段值为0。该字段一般在POST请求中才会有。POST请求的body请求体也有可能是空的,因此POST中Content-Length也有可能为0
Cookie: 记录者用户信息的保存在本地的用户数据,如果有会被自动附上
值得一提的是,在iOS中当你发送一个任意请求时,不管你愿不愿意,NSURLRequest都会自动帮你记录你所访问的URL上设置的cookie。在iOS中用NSHTTPCookieStorage表示,是一个单例。通过
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for(NSHTTPCookie *cookiein[cookieJar cookies]) {
NSLog(@"%@", cookie);
}
可以获取目前被自动保存的所有cookie。
3、请求体
真正需要发给服务端的数据,在使用POST-multipart上传请求中请求体就是上传文件的二进制NSData类型数据;在GET请求中请求体为空;在普通的POST请求中请求体就是一些表单数据。在iOS中一般用NSURLRequest与NSMutableURLRequest的HTTPBody属性表示,添加body用-[NSMutableURLRequest setHTTPBody:]。
4、响应状态行
上图是是服务端返回给客户端的响应状态行,包含HTTP版本号、状态码、状态码对应的英文名称。
以下就是典型的正确状态行:
HTTP/1.1 200 OK\r\n
这个部分需要讲的是错误码。事实上HTTP请求错误码可以根据错误码从左往右第一个数字大致分为以下几类:
1XX:信息提示。不代表成功或者失败,表示临时响应,比如100表示继续,101表示切换协议
2XX: 成功
3XX: 重定向
4XX:客户端错误,很有可能是客户端发生问题,如亲切可爱的404表示未找到文件,说明你的URI是有问题的,服务器机子上该目录是没有该文件的;414URI太长
5XX: 服务器错误,比如504网关超时
错误码是不用去记的,出错了再查对应的错误码含义就行。但是知道上面的分类有助于第一时间做出大体的判断,起码你能清楚是服务端还是客户端的原因。
5、响应头与响应实体
这部分与请求部分差异不大,响应头的字field会有稍许不同。
3.3 超时重传
这是我再抓包过程中发现的丢包现象,如上面所说4.31.198.49为服务器的ip,在上图中服务器发送给客户端的包丢了,丢了???服务器响应体丢了怎么办呢?是否会开始下一轮的三次握手呢?
1,HTTP丢包后,客户端会告诉服务器丢包了,当前Seq = 363,下次请发送Ack = 1的序号包;服务器收到请求后,忽略之前的seq = 363的包,重新发送seq = 1的包。
2,服务器确定后说,“好的,亲。”序号包为Seq = 1的包确定,下次请请求Ack为363的包,我当前的窗口为(win = 131744),空闲的很,大胆请求吧皮卡丘;
上图为客户端确定收到的Seq = 363的包,目前可以确定收到Seq = 1449 的包,所以客户端重新请求Ack = 1449的包。
3.4 HTTPS怎么办???
HTTPS是个研究好几天后,我还是确定没有服务器的同学配合,客户端用Wireshark无法抓到实际意义的包。
上图是HTTPS的响应包,下方是抓包后显示的数据,很明显除了这条可以知道是HTTP数据外
,其他的完全看不懂,因为所有信息都被加密了,这也反映苹果规定所有请求必须是https请求的明智。
3.4 HTTPS解密
1,找服务器的基友表达善意,将服务器的秘钥导出,例如key01.key文件
2,单击Wireshark的Preferences(Mac版本为例),
在弹出的Wireshark首选项设置中选择Protocol的SSL(加密套接字协议),选择RSA keys list的Edit按钮,如下格式填好,单击OK。接下来就可以见证奇迹的时刻。
四,TCP,HTTP,Socket
这三个概念经常被谈到,也是比较容易被混掉的概念。在回顾之前我们先看一下这三者在TCP/IP协议族中的位置关系:
HTTP是应用层的协议,更靠近用户端;TCP是传输层的协议;而socket是从传输层上抽象出来的一个抽象层,本质是接口。所以本质上三种还是很好区分的。尽管如此,有时候你可能会懵逼,HTTP连接、TCP连接、socket连接有什么区别?好吧,如果上面的图解释的还是不够清楚的话,我们继续往下看。
4.1,TCP连接与HTTP连接的区别
上文提过,HTTP是基于TCP的,客户端往服务端发送一个HTTP请求时第一步就是要建立与服务端的TCP连接,也就是先三次握手,“你好,你好,你好”。从HTTP 1.1开始支持持久连接,也就是一次TCP连接可以发送多次的HTTP请求。
小总结:HTTP基于TCP
4.2,TCP连接与Socket连接的区别
在图4.1中我们提到,socket层只是在TCP/UDP传输层上做的一个抽象接口层,因此一个socket连接可以基于连接,也有可能基于UDP。基于TCP协议的socket连接同样需要通过三次握手建立连接,是可靠的;基于UDP协议的socket连接不需要建立连接的过程,不过对方能不能收到都会发送过去,是不可靠的,大多数的即时通讯IM都是后者。
小总结:Socket也基于TCP
4.3,HTTP连接与Socket连接的区别
区分这两个概念是比较有意义的,毕竟TCP看不见摸不着,HTTP与Socket是实实在在能用到的。
HTTP是短连接,Socket(基于TCP协议的)是长连接。尽管HTTP1.1开始支持持久连接,但仍无法保证始终连接。而Socket连接一旦建立TCP三次握手,除非一方主动断开,否则连接状态一直保持。
HTTP连接服务端无法主动发消息,Socket连接双方请求的发送先后限制。这点就比较重要了,因为它将决定二者分别适合应用在什么场景下。HTTP采用“请求-响应”机制,在客户端还没发送消息给服务端前,服务端无法推送消息给客户端。必须满足客户端发送消息在前,服务端回复在后。Socket连接双方类似peer2peer的关系,一方随时可以向另一方喊话。
4.4,问题来了:什么时候该用HTTP,什么时候该用socket
这个问题的提出是很自然而然的。当你接到一个与另一方的网络通讯需求,自然会考虑用HTTP还是用Socket。
用HTTP的情况:双方不需要时刻保持连接在线,比如客户端资源的获取、文件上传等。
用Socket的情况:大部分即时通讯应用(QQ、微信)、聊天室、苹果APNs等