当前位置:   article > 正文

分析网络抓包用 python 更高效

pyshark
Abstract分析网络抓包用 python 更高效
AuthorsWalter Fan
Categorylearning note
Statusv1.0
Updated2023-01-10
LicenseCC-BY-NC-ND 4.0

网络抓包分析用的最多的两大工具是 tcpdump 和 wireshark.

一般我们通过 tcpdump 或者 wireshark 来捕获网络包为 *.pcap 或者 *.pcapng 文件

tcpdump  -G 60 -W 1 -w /tmp/test.pcap

而分析 pcap 文件可以用 wireshark, wireshark 之强大, 界面之复杂不用赘述, 相关书籍和文章汗牛充栋.

我更喜欢用其命令行工具 tshark, 例如

tshark -P -V -x -2 -T text -j rtp -c 3 -r /tmp/test.pcap

例如将 pcapng 文件中的前 100 包按以下条件过滤出来,并导出为 json 文件

  1. tshark -r 2022-03-30-cc.pcapng -2 -Y "ip.addr == 10.140.202.120 and rtp.p_type == 123" -V -c 100 -T json >
  2. packet_sample.json"

输出结果如下

  1. //省略 1 ~ 4 层的信息: 1)frame, 2)eth, 3)ip, 4)udp
  2. "rtp": {
  3. "rtp.setup": "",
  4. "rtp.setup_tree": {
  5. "rtp.setup-frame": "1",
  6. "rtp.setup-method": "HEUR RTP"
  7. },
  8. "rtp.version": "2",
  9. "rtp.padding": "0",
  10. "rtp.ext": "1",
  11. "rtp.cc": "0",
  12. "rtp.marker": "0",
  13. "rtp.p_type": "123",
  14. "rtp.seq": "8637",
  15. "rtp.extseq": "74173",
  16. "rtp.timestamp": "2709737133",
  17. "rtp.ssrc": "0xe19bcceb",
  18. "rtp.ext.profile": "0x0000bede",
  19. "rtp.ext.len": "2",
  20. "rtp.hdr_exts": {
  21. "RFC 5285 Header Extension (One-Byte Header)": {
  22. "rtp.ext.rfc5285.id": "2",
  23. "rtp.ext.rfc5285.len": "3",
  24. "rtp.ext.rfc5285.data": "e0:9c:ac"
  25. },
  26. "RFC 5285 Header Extension (One-Byte Header)": {
  27. "rtp.ext.rfc5285.id": "3",
  28. "rtp.ext.rfc5285.len": "2",
  29. "rtp.ext.rfc5285.data": "c4:70"
  30. }
  31. },
  32. "rtp.payload": "92:00:60:90:80:c6:67:..."
  33. }

更多具体用法参见 tshark –help

如果我们需要进一步地进行自动化分析,pyshark 是一个不错的库, 用 python 就可以读取分析网络包

pyshark 基本用法

1) 从抓包文件中读取网络包

  1. >>> import pyshark
  2. >>> cap = pyshark.FileCapture('/tmp/mycapture.cap')
  3. >>> cap
  4. <FileCapture /tmp/mycapture.cap (589 packets)>
  5. >>> print cap[0]
  6. Packet (Length: 698)
  7. Layer ETH:
  8. Destination: BLANKED
  9. Source: BLANKED
  10. Type: IP (0x0800)
  11. Layer IP:
  12. Version: 4
  13. Header Length: 20 bytes
  14. Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
  15. Total Length: 684
  16. Identification: 0x254f (9551)
  17. Flags: 0x00
  18. Fragment offset: 0
  19. Time to live: 1
  20. Protocol: UDP (17)
  21. Header checksum: 0xe148 [correct]
  22. Source: BLANKED
  23. Destination: BLANKED
  24. ...

2) 从一个网络接口中读取网络包

  1. >>> capture = pyshark.LiveCapture(interface='eth0')
  2. >>> capture.sniff(timeout=50)
  3. >>> capture
  4. <LiveCapture (5 packets)>
  5. >>> capture[3]
  6. <UDP/HTTP Packet>
  7. for packet in capture.sniff_continuously(packet_count=5):
  8. print 'Just arrived:', packet

3) 使用环形缓冲区从一个网络接口中读取网络包

  1. >>> capture = pyshark.LiveRingCapture(interface='eth0')
  2. >>> capture.sniff(timeout=50)
  3. >>> capture
  4. <LiveCapture (5 packets)>
  5. >>> capture[3]
  6. <UDP/HTTP Packet>
  7. for packet in capture.sniff_continuously(packet_count=5):
  8. print 'Just arrived:', packet

4) 从一个远程网络接口读取网络包

  1. >>> capture = pyshark.RemoteCapture('192.168.1.101', 'eth0')
  2. >>> capture.sniff(timeout=50)
  3. >>> capture

更多用法参见 https://github.com/KimiNewt/pyshark

综合实例

我在分析 WebRTC 中的网络梯度延迟 OWDV(One Way Delay Variation), 也用它写了一小段脚本, 性价比极高

  1. #!/usr/bin/env python3
  2. import pyshark
  3. import pandas as pd
  4. import matplotlib
  5. import matplotlib.pyplot as plt
  6. import argparse
  7. from datetime import datetime
  8. """
  9. abs_send_time_24 = (ntp_timestamp_64 >> 14) & 0x00ffffff ;
  10. NTP timestamp is the number of seconds since the epoch, in 32.32 bit fixed point format.
  11. It is 24 bit 6.18 fixed point, yielding 64s wraparound and 3.8us resolution
  12. int kAbsSendTimeFraction = 18;
  13. int kAbsSendTimeInterArrivalUpshift = 8;
  14. int kInterArrivalShift = RTPHeaderExtension::kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
  15. constexpr double kTimestampToMs = 1000.0 / static_cast<double>(1 << kInterArrivalShift);
  16. uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
  17. Timestamp send_time = Timestamp::Millis(static_cast<int64_t>(timestamp) * kTimestampToMs);
  18. """
  19. # fraction part has 18 bits
  20. kAbsSendTimeFraction = 18
  21. kAbsSendTimeInterArrivalUpshift = 8
  22. # after upshfit 8 bits, there are 26 bits for fraction
  23. kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift
  24. kTimestampToMs = 1000.0 / (1 << kInterArrivalShift)
  25. def send_time_to_ms(send_time_24bits):
  26. timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift
  27. send_time = timestamp * kTimestampToMs
  28. return send_time
  29. class RtpAnalyzer:
  30. def __init__(self, input_file, output_file):
  31. self._pcap_file = input_file
  32. self._csv_file = output_file
  33. def read_pcap(self, display_filter, count):
  34. dataList = []
  35. packets = pyshark.FileCapture(self._pcap_file, display_filter=display_filter)
  36. i = 0
  37. for packet in packets:
  38. dataItem = {}
  39. dataItem["arrival_time"] = datetime.fromtimestamp(float(packet.frame_info.time_epoch))
  40. dataItem["arrival_time_ms"] = float(packet.frame_info.time_epoch) * 1000
  41. dataItem["rtp_timestamp"] = int(packet.rtp.timestamp)
  42. dataItem["extseq"] = int(packet.rtp.extseq)
  43. dataItem["packet_size"] = int(packet.udp.length)
  44. if int(packet.rtp.ext_rfc5285_id) == 2:
  45. send_time_24bits = packet.rtp.ext_rfc5285_data.main_field.hex_value
  46. dataItem["abs_send_time"] = send_time_to_ms(send_time_24bits)
  47. dataList.append(dataItem)
  48. i += 1
  49. if i >= count:
  50. break
  51. dataFrame = pd.DataFrame(dataList)
  52. #print(dataFrame)
  53. return dataFrame
  54. def calculate_delta(self, df, row_interval=1):
  55. df["arrival_time_ms_diff"] = df["arrival_time_ms"].diff(periods=row_interval)
  56. df["send_time_diff"] = df["abs_send_time"].diff(periods=row_interval)
  57. df["OWDV"] = df["arrival_time_ms_diff"] - df["send_time_diff"]
  58. df["OWDV"] = df["OWDV"].abs()
  59. df = df[df['OWDV'] < 60]
  60. print(df)
  61. df.to_csv(self._csv_file)
  62. print(df["OWDV"].describe())
  63. print("* note: filter out OWDV if it > 60s because abs_send_time wrap around by 64s")
  64. return df
  65. def draw_chart(self, chart_file, df, x, y):
  66. plt.style.use('seaborn-v0_8-whitegrid')
  67. fig = plt.figure(figsize=(36, 18))
  68. font = {'size': 16}
  69. plt.plot(x, y, data=df)
  70. #plt.show()
  71. fig.savefig(chart_file)
  72. plt.close()
  73. if __name__ == '__main__':
  74. parser = argparse.ArgumentParser()
  75. parser.add_argument('-i', action='store', dest='input_file', help='specify input file')
  76. parser.add_argument('-o', action='store', dest='output_file', help='specify output file')
  77. parser.add_argument('-f', action='store', dest='filter', default="rtp", help='specify filter expression')
  78. parser.add_argument('-c', action='store', dest='count', default=10, help='specify packet count')
  79. args = parser.parse_args()
  80. if not args.input_file or not args.output_file or not args.output_file.endswith(".csv"):
  81. print("usage: ./rtp_analyze.py -i <pcap_file> -f <filter_expression>")
  82. print('such as: ./rtp_analyze.py -i /tmp/test_owdv.pcap -o "test_owdv.csv" -f "rtp.ssrc==0x8ab92fad" -c 100000')
  83. exit(0)
  84. rtpAnalyzer = RtpAnalyzer(args.input_file, args.output_file)
  85. df = rtpAnalyzer.read_pcap(args.filter, int(args.count))
  86. if not df.empty:
  87. df = rtpAnalyzer.calculate_delta(df)
  88. rtpAnalyzer.draw_chart("{}.png".format(args.output_file[:-4]), df, "arrival_time", "OWDV")

上述小程序生成的图片如下

参考资料



本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号