Analysis-video-traffic-with-wireshark

本文最后更新于:2023年6月19日 晚上

准备

在本次分析中,我选择了腾讯视频流量进行抓包分析。
首先,腾讯视频的流量是走的 tcp 协议,而且全站 https 加密,因此报文内容是乱码,需要解码。
以 windows 系统+Chrome 浏览器为例,首先要导出浏览器存储的密钥,通过计算机属性——高级系统设置——环境变量,新建一个变量名“SSLKEYLOGFILE”的变量,变量值是导出的密钥具体文件地址。
image.png
经测试,chrome 和 firefox 均无法导出密钥,最终找到解决办法如下。
运行命令

1
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --ssl-key-log-file = "E:\sslkey.log"

即可。
然后再在 wireshark 中绑定
image.png
但是还是不行,不知道什么原因,可能是没有重启电脑?协商的 sslkey 不能随时更新到 log 文件中,我也不知道为什么,就先不管它的传输层 tcp 报文原始信息了。
确实重启电脑就好了。

业务流程

网络视频业务可以从 1.主页访问,2.外链链入。
不同网站视频页面的 url 格式不同。

| 业务提供商 | 视频页面的 url 正则表达式 |
| :————: | :——————————————-: | ——————————————— |
| 优酷 | v\.youku\.com/vshow/id[A-Za-z0-9=]{17}.* |
| 优酷移动端 | .*api\.(mobile | 3g)\.youku\.com/videos/[A-Za-z0-9=]{17}/.* |
| 搜狐视频 | tv\.sohu\.com/[0-9]{8}/n[0-9]{9}.* |
| 腾讯视频 | v\.qq\.com/x/cover/[a-z0-9]{15}.* |
| 爱奇艺 | www\.iqiyi\.com/[a-z]_[a-z0-9]{10}.* |
| CNTV | tv\.cntv\.cn/video/[A-Za-z0-9=]{16}/.* |
| PPTV | v\.pptv\.com/show/[A-Za-z0-9=]{16}/.* |

用户在观看视频时,可能会有一系列交互动作,可以分成两类:播放操作与社交行为。
前者包括:

  • 暂停
  • 重新播放
  • 跳转
  • 调节清晰度
  • 静音
  • 调节播放速度等

后者包括:

  • 评论
  • 收藏
  • 点赞
  • 分享等

通信报文分析

【1】https://segmentfault.com/a/1190000018746027
【2】https://www.jianshu.com/p/a3a25c6627ee
【3】https://www.dell.com/community/
【4】https://imququ.com/post/http2-traffic-in-wireshark.html
对于腾讯视频 5 分钟的观看抓包。
得到以下结论:

  • 首先进行 TCP 三次握手

image.png

  • 然后进行 ssl 层的 tls 连接。

image.png

  1. Client Hello
    1. Client Hello 报文:客户端对加密算法的支持度不同,因此需要向服务端发送客户端支持的加密套件(Cipher Suite) ,同时还要生成一个 随机数 同时保存在客户端和发送给服务
  2. Server Hello
    1. ServerCertificate 报文:服务端收到 Client Hello 之后,向客户端发送 CA 认证的数字证书,用来鉴别服务端身份信息,同时还要生成一个 随机数 同时保存在服务端和发送给客户端
    2. Server Hello Done 报文:表示服务端宣告第一阶段的客户端服务端握手协商结束
    3. 可选:Certificate Request 报文:必要情况下,要求客户端发送证书验证身份
    4. 可选:Server Key Exchange 报文:如果 CA 认证的数字证书提供的信息不够,服务端还可发送提供补充信息
  3. Client Finish
    1. Client Key Exchange 报文:客户端收到 CA 数字证书并通过验证,然后通过 CA 公钥解密获取到 服务端公钥。Client Key Exchange 报文包括有一个随机数,这个随机数被称为 Pre-master key/secret;一个表示随后的信息使用双方协商好的加密方法和密钥发送的 通知 ;还有一个通过协商好的 HASH 算法对前面所有信息内容的 HASH 计算值,用来提供服务端校验。这些信息都通过服务端公钥加密传送给服务端
    2. ClientCipherSpec 报文:该报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥进行加密通信(也就是使用两个随机数以及第三个 Pre-master key/secret 随机数一起算出一个对称密钥 session key/secret
    3. Finished 报文:该报文包括连接至此的所有报文的校验值,使用服务端公钥进行加密
    4. 可选:ClientCertificate 报文:如果服务端请求,客户端需要发送 CA 数字证书
    5. 可选:CertificateVerify 报文:服务端如果要求 CA 数字证书,那么需要通过 HASH 算法计算一个服务端发送来的信息摘要
  4. Server Finish
    1. 服务端最后对客户端发送过来的 Finished 报文使用服务端私钥进行解密校验
    2. ClientCipherSpec 报文:报文通知服务端,此后的通信都将使用协商好的加密算法计算对称密钥 session key/secret 进行加密通信
    3. Finished 报文:标志 TLS 连接建立成功
  5. TLS 握手成功此后通过对称密钥 session key/secret 加密通信
  • 然后开始 http 请求对话,这里发出一个 GET 请求

image.png
image.png
可以看到目标 host 格式为 v.smtcdns.com,可见是一个 cdn 节点。
然后就是 TCP 流
image.png
这里无法解析 TCP 的报文具体信息,应该就是相应的视频流。
在所有 tcp 分片发完之后,然后服务器发送一个 http 200,返回的内容是 video,采用 MP2T 编码。image.png
image.png
然后再次重复上面的过程。
整个观看视频过程中产生的 http 报文过滤后如下
image.png

使用 python 进行自动化分析与特征提取

【1】https://github.com/SewellDinG/scapy-ssl_tls-demo
【2】https://github.com/hackers-terabit/scapy-ssl_tls-python3
【3】http://blog.whiterabbitxyj.com/2017/11/07/python-scapy/
【4】https://wizardforcel.gitbooks.io/scapy-docs/content/3.html
【5】https://www.osgeo.cn/scapy/layers/http.html
【6】https://www.osgeo.cn/scapy/usage.html
【7】https://scapy.readthedocs.io/en/latest/api/scapy.layers.tls.html
首先,scapy 在 2.4.0 已经开始支持 http 和 tls 的解析,但是在默认配置中没有添加,可以通过

1
2
3
from scapy.all import *
load_layer('tls')
load_layer('http')

进行手动导入。

这里真的坑了我好久,我一直在安装那个 tls 的补充包,但安不上,读了很多文档才琢磨出来

不过也仅仅是能解析到 tls 层,而 http 层由于加密仍然无法解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
###[ Ethernet ]###
dst = 54:a7:03:d3:a3:91
src = 42:23:09:71:f5:85
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 1033
id = 21817
flags = DF <--对应分片
frag = 0
ttl = 128
proto = 6
chksum = 0x9daf
src = 192.168.1.101
dst = 60.233.5.16
\options \
###[ TCP ]###
sport = 50503
dport = 443
seq = 1900018089
ack = 3368278776
dataofs = 8
reserved = 0
flags = PA <--对应SYN, FIN, ACK, PSH, RST, URG
window = 4116
chksum = 0xe752
urgptr = 0
options = [('NOP', None), ('NOP', None), ('Timestamp', (2543536, 2295788294))]
###[ TLS ]###
type = application_data
version = TLS 1.2
len = 976 [deciphered_len= 976]
iv = b''
\msg \
|###[ TLS Application Data ]###
| data = '\x00\x00\x00\x00\x00\x00\x00\x02\xf4\xae\x1e\x90P\x058\x0bd\xae\xae\x14\x86\xfd\xb4\x19\xb3E\xc3\x86\x1fg\xf5\x8a)\xdf@\xe4\xf0|Q%Ay\xb3\xb0^\xf1\xe2\r\xb3\xf7\xb0\xab\xb5eFe@\xe3\x9c\xf5o\xaa\xb3\x13\x8d\xc9^G\x10\xc7\x90\xf5\xe7FM\x80\xfcAH\xf88\xf5\xea\x86K%EB\xbft\xc23\xbavv\xae\xda\xbf\x07\x90\xc6\x12\x1f\x81v\xcdl\xd3\xb0\xe7p\xcf\xb1\xac\xceK\x1f\xdf4\xbb\xbc\x93\x81E\xdd\xda\x96\xd0\xae+\x80\xb3\x8e\x84\xdf^"ig]w\xc0\xe8P\x92\x1a!3\xca\xb4\x0b\xc3\x83<B\x10\x80\xa5\xeb9F\xde\xbc\xf1\xf6\x91\xaac\x15\x85\xc7rs\xfeP0\x15\x02H~Ba\x104\x89~MP<x\xbc\xfe$|\xd6\x9de\xff\x9b\xfe\x11\xf2]\x87\xa9\xc1\x84yT\xe9R*\x9cVP-Q\x15\xeb\x99\xde@\xc4,\xads\xb1\xcd\x8f\xcb\x9e\xe8@PE\xdd\xab\x1b*\xd1I-\x96Oa\x8e\xc5\xbest\xd0\xbe\x03{r\xe3f\x17\xebx\xbd\x8b\xde~B&\xfe"\xa9d\xd4\xdaZH|\xce\xdb$\xe2P7F\x83\x14\xa0i\x8f\x9a\xe5VL\xf9\x87U\x94\xf8\xe3]\xfd[\x84(\rOs\xc9CG\x9d\xe8\x8eo,\xed\x94\xe0\xfaT\x87\x9d\xe1e\x8d\xea\x19+\xed\x0b\xf9\xea\x8d\xdc\xca\x0c\xa8\x85_\x9aF\xaanF\xa6b\xd0\xc0\xe4B\xdb\xec\x13[\x14)\xcc\x7f\xb0\x83\xe7\xae\xb5b\x04;y\x83ZE\x19C\x18\x01#e\xc8f\xb7@\x86\xae\xd8u\x944f\xe1\x82\xec\xe8]\x0c\x8f&\x8c\x9d\xe5<\x88J2\xc4"\rev"\xe1@\x05\xbc\xc2\x7f\x98\x07\xae\x9e\xab\xa9\x892`%\xe7\xecxX\xb94\xb8:\xf3\xa3\x07\x18\xe4\xe9\x99N\xf0\xc3\x85\xca\xbe\x93\xc0~\xe6\xa5\xbaf\xc1\xdc\'\xb6\xf1W\xcf\xb7\x06\xf4`\x1d\xc4\xe6|\xd1\xd1\x05\x7f\x8f\x9d\x8c\xb5&\x8btr\xc5=\xe0\x9f\xb4S\x93<\x96/\xb8\xd1CE\x10\x82\x8a\xf7\xef\x9b\x81h\xa5y(\xedw\xe4[o\xaa\x1c\xe6\xed\xf0\xaa\x82t\xc5;\x8a\nk\x9a\xe1<\xc6\x03L\xd8\xc1S\xd2\x08\x08\xaf\xa54%\xed\xed\xc8*\x95\xd4\xfd\xf8\xf9D\xdf6\x16\xf8\x11\t?\xbc\xe7\x1e\xcc<\xff\xda\xe9\xa6\xd8 l\x83\xe8\xd93?C#}7KI\xdeVt$\xa5\xdc\xba\x0fL\x9b\x19(\t\xb5U\xd5\x82\xc0*\xb7\xf2\xda2dW!\x7fRF\xcf\xa6Qk\xcfD\xf8G\xac\xee\xe8F\xe6\x8e(Q\x91\xd9\xeb^C\xb3I\xea\n\xc1\xd84\xd4\x9d\xcc=Yt\xf0"|xo\xb4p\xaf\xc85\xad\r\xd4&\xd9\xbd\x85\x8b>Tv\\\x06\x19@G\x0f\xfdF\x7f\x174M+\x17/7\xb7\xe8!T\xf5h\xf3\xffQ\xdd\x1e\xfc\xe5\xc4L`\xf5\x12T\x14\xb1\\\xd3F\xb1\x95\xab)\x1at\xc0$\xca\x00\xf46\xc8\xc0\x94\xab(g\xb7.\xcf\xa0|%\xfe\xa6\xb2UJ\xbd\x1eZ2w2H(\xedz>\x02\x11\xb2;\xbe\xe1\x1c\xc6\xe2\xfa\xb6\x19\xd5\xc0\xe2Z\x16\x87O8\xdcs\'\xc1:\xc8\xd4\x9e&o\xc5\x1b;\x86\xb2\x00\xe50\xba<\xbe\xa8\x14\xd2\xc9\xe7\xc1#\'\x8e\xaa\x82\xc9\xf1\xd0\xdak\xe5DS\x95\xc3k\x8f\xf1\xdaa\xbdC\xb2\xadk\x04\x024\\)\xa9\x86X\x0e\x99h\x93\xf8\x88\xba\xaf\x9f\xb6X(\xcep\xd3\x87\xba=\tr1k`"\x94$4A\xf9(\x1e\xa9\x9f\x83\x8f\xe8YVOK\xa8_}\xc0)-\x14\xf1\x15V[j,\xd3`\xa0\xeaD\xa2\x08N!\xd1\x92\xb8\xe0D\x86K.\xf8\xcfGb\x943mGB\x9b,~\xe4\xcdR\xd4s\x19\xd5\xfeb\x94\xd7u\xcd\xc5\xa4\x1f\xfbBS\x0c\x8e\x06\xf2\x95\xd05\x80\x08\xbdh\xbb\xd6\xe4\x04&\xc4{\x12}\'-\xc7O}]\xbd\xd0\xf8\xaep\x9c\xb6\rH\x0e6,\xab\xb0\xf3k\x00\xb4\x16\x04\x1b\x13:,=\x8d\xbf\x99\x03'
mac = b''
pad = b''
padlen = None

None

暂时没有找到如何像 wireshark 一样可以实时绑定密钥进行会话解密的 python 命令行方式,所以提取特征只限制在 tls 层及以下。也就是说,诸如 http 的状态码,请求类型,实体内容类型都无法解析

提取特征

首先,确定提取的特征名称。
首先是五元组特征:(源 IP,目的 IP,源端口,目的端口,协议类型)

1
2
3
4
5
6
7
8
9
feature_name=['fiat_mean','fiat_min','fiat_max','fiat_std','biat_mean','biat_min','biat_max','biat_std',
'diat_mean','diat_min','diat_max','diat_std','duration','fwin_total','fwin_mean','fwin_min',
'fwin_max','fwin_std','bwin_total','bwin_mean','bwin_min','bwin_max','bwin_std','dwin_total',
'dwin_mean','dwin_min','dwin_max','dwin_std','fpnum','bpnum','dpnum','bfpnum_rate','fpnum_s',
'bpnum_s','dpnum_s','fpl_total','fpl_mean','fpl_min','fpl_max','fpl_std','bpl_total','bpl_mean',
'bpl_min','bpl_max','bpl_std','dpl_total','dpl_mean','dpl_min','dpl_max','dpl_std','bfpl_rate',
'fpl_s','bpl_s','dpl_s','fin_cnt','syn_cnt','rst_cnt','pst_cnt','ack_cnt','urg_cnt','cwe_cnt','ece_cnt',
'fwd_pst_cnt','fwd_urg_cnt','bwd_pst_cnt','bwd_urg_cnt','fp_hdr_len','bp_hdr_len','dp_hdr_len',''
'f_ht_len','b_ht_len','d_ht_len']

其中

  • 13 个包的到达特征
1
2
3
4
5
# feature about packet arrival interval 13
fiat_mean,fiat_min,fiat_max,fiat_std = packet_iat(fwd_flow)#正向发送的两个数据包之间的平均时间,正向发送的两个数据包之间的最短时间,正向发送的两个数据包标准偏差
biat_mean,biat_min,biat_max,biat_std = packet_iat(bwd_flow)#反向发送的两个数据包之间的平均时间,...
diat_mean,diat_min,diat_max,diat_std = packet_iat(pkts)#两次流动之间的平均时间,...
duration = round(pkts[-1].time - pkts[0].time+ decimal.Decimal(0.0001), 6) #流动持续时间
  • 15 个拥塞窗口特征
1
2
3
4
# 拥塞窗口大小特征 15
fwin_total,fwin_mean,fwin_min,fwin_max,fwin_std = packet_win(fwd_flow)
bwin_total,bwin_mean,bwin_min,bwin_max,bwin_std = packet_win(bwd_flow)
dwin_total,dwin_mean,dwin_min,dwin_max,dwin_std = packet_win(pkts)
  • 7 个包的数量特征
1
2
3
4
5
6
7
8
# feature about packet num  7
fpnum=len(fwd_flow) #前向总包数
bpnum=len(bwd_flow) #反向总包数
dpnum=fpnum+bpnum
bfpnum_rate = round(bpnum / (fpnum + 0.001), 6)
fpnum_s = round(fpnum / duration, 6)
bpnum_s = round(bpnum / duration, 6)
dpnum_s = round(dpnum / duration, 6) #流数据包速率,即每秒传输的数据包数
  • 19 个包的总长度特征
1
2
3
4
5
6
7
8
# 包的总长度 19
fpl_total,fpl_mean,fpl_min,fpl_max,fpl_std = packet_len(fwd_flow)#正向报文总大小,正向报文的平均大小,...
bpl_total,bpl_mean,bpl_min,bpl_max,bpl_std = packet_len(bwd_flow)
dpl_total,dpl_mean,dpl_min,dpl_max,dpl_std = packet_len(pkts)
bfpl_rate = round(bpl_total / (fpl_total + 0.001), 6)
fpl_s = round(fpl_total / duration, 6)
bpl_s = round(bpl_total / duration, 6)
dpl_s = round(dpl_total / duration, 6) #流字节率,即每秒传输的数据包数
  • 12 个包的标志特征
1
2
3
4
# 包的标志特征 12
fin_cnt,syn_cnt,rst_cnt,pst_cnt,ack_cnt,urg_cnt,cwe_cnt,ece_cnt=packet_flags(pkts,0)#FIN的报文数,SYN报文数,...
fwd_pst_cnt,fwd_urg_cnt=packet_flags(fwd_flow,1)#在正向传输的数据包中设置PSH标志的次数,URG标志的次数
bwd_pst_cnt,bwd_urg_cnt=packet_flags(bwd_flow,1)#在反向传输的数据包中设置PSH标志的次数,URG标志的次数
  • 6 个包的头部特征
1
2
3
4
5
6
7
# 包头部长度 6
fp_hdr_len=packet_hdr_len(fwd_flow)
bp_hdr_len=packet_hdr_len(bwd_flow)
dp_hdr_len=packet_hdr_len(pkts)
f_ht_len=round(fp_hdr_len /(fpl_total+1), 6)
b_ht_len=round(bp_hdr_len /(bpl_total+1), 6)
d_ht_len=round(dp_hdr_len /dpl_total, 6)

这里介绍一种快速过滤的方法。

1
2
3
4
5
from scapy.all import *
packets = rdpcap('/content/drive/MyDrive/network_trafic_analysis/qqlarge.pcap')
tcp_p = packets.filter(lambda x: x.haslayer('TCP'))
--------------------------------------------------
<filtered qqlarge.pcap: TCP:9201 UDP:0 ICMP:0 Other:0>

然后对于已知的流量包,去 github 上找了现成的轮子https://github.com/jiangph1001/flow-feature.git。提取效果如下。

image.pngimage.png
可以看到,在观看视频的时间里,与服务器 ip 之间的数据流是最多的。

过滤 idea

  1. 可以分析视频流 tcp 报文的频谱,进行傅里叶变换或者小波变换,得到正常情况下的能量谱特征。
  2. 对于 DDos 等其他攻击情况下的频谱,也可以进行相关分析。
  3. 对于正常的 TCP 流,可以使用梳状滤波器进行滤波,让 RTT 对应频带的频率通过,保证大部分正常 TCP 流量通过,然后将其余的归为灰流量。

    见参考文献:基于频谱分析的 LDos 攻击流量过滤方法

参考

在查解析 pcap 包的时候发现了这样几个仓库,感觉很棒。
Joy https://github.com/cisco/joy
相关使用可参考https://flashgene.com/archives/70939.html
https://www.freebuf.com/sectool/161431.html
官方文档https://github.com/cisco/joy/blob/master/doc/using-joy-05.pdf
更详细的使用可以见https://github.com/cisco/joy/wiki,里面包括了搭建,捕获,特征提取的全过程

https://asmcn.icopy.site/awesome/awesome-pcaptools/
这个是收录的所有和 pcap 相关的工具,包括抓取,解析,提取特征。而这个站点,真的是一个宝藏站点,里面啥都有…真的太强了。
https://asmcn.icopy.site/awesome/awesome-network-analysis/#software 比如这个就是网络分析相关的软件大全(图论相关)