从Wireshark抓包来看IP分片

文件来自于前几天CyBRICS 2021中的lx100题目,因为做题时候被IP分片坑到了,发现自己对于网络这一块的知识掌握的并不好,所以写一篇文章来理一下。为了省事就直接用比赛的pcap文件做样例了:点击

从Wireshark抓包来看IP分片

UDP/lPv4分片

一般来说我们都知道MTU是1500字节,因此超过1500字节的数据就需要进行ip分片。包含源和目的端口号的UDP头部只出现在第一个分片里,分片由IPv4头部中的标识(Identification)、分片偏移(Fragment offiet)和更多分片(More Fragments, MF)字段控制。

  • 这些分片的标识(Identification)都是同样的而且分片偏移(Fragment offiet)是以8字节为单位的偏移。同样的标识和不同的偏移让接收方可以对分片进行重组。
  • 更多分片(More Fragments, MF)标志指明该数据报后面是否还有更多的分组, 只有最后一个分片才应置成0。当MF = 0的分片被接收到时, 重组程序才能确定原始数据报的长度, 它等于分片偏移字段的值(乘以8)加上当前分片IPv4总长度字段的值。

下图是一个长度为3000字节的udp包(包括了udp包头的8个字节)。原始数据报要加上ipv4包头为3020字节,它超过了MTU的限制,因此发送的时候会进行分片。

  • 第一个分片中实际传送了1472字节的udp数据和8字节udp包头。此时偏移为0,因为接下来还有数据包要发因此MF置为1
  • 第二个分片传送1480字节的udp数据,因为第一个分片已经传送了1480,因此这里的偏移为1480/8 = 185,因为接下来还有数据包要发因此MF置为1
  • 最后一个分片传送剩下的40字节数据,并将MF置为0,此时接收方可以通过370*8+60来算出原始数据报的长度,因此也知道了udp包的大小为3000字节(减掉ip包头的20字节)。

在这里插入图片描述

wireshark中的视角

比赛用的这个pcap文件实际上抓的是照相机用udp传输图片的过程,我们以一个udp传输为例:
在这里插入图片描述
我们发现它和我们上一节讲的不太一样,UDP包按理应该是第一个出现,之后跟着一串分片后的IPv4包。而打开上面截图里所有的IPv4包我们发现它们的MF标志全都是1,且最后的udp包的length明显大于了MTU。这是因为wireshark的首选项中自动开启了重组分片数据的选项。这个默认开启的选项能直接帮我们重组原udp数据包并用它替换了MF=0的那个IPv4数据包,因此在开启该选项时我们是找不到MF=0的数据包的。
在这里插入图片描述
为了更好的理解这个过程我们关闭这个选项。可以看到下面的样子:
在这里插入图片描述
我们打开第一个UDP包,它的数据如下:
在这里插入图片描述
我们可以知道虽然它包头中的长度给出的Length为8537(包含8个字节包头,实际数据为8529字节),但是真实的Data里只有1472字节,1472+8(UDP包头)+20(IP包头) = MTU。我们再看这一组最后一个IPv4包:
在这里插入图片描述
我们可以看到它的MF=0,因此计算925*8+1157 = 8557,这是原始的数据报文,减掉20字节的IP包头,恰好为8537字节。与UDP包中所给的Length值一样。

回到这是一道ctf题目

上文也说了这是一道照相机传图片的题,因此一个udp包就是一张图,我们只需从pcap文件里提取图片就行。
用pyshark库对pcap文件进行处理,注意首选项中的改动是会影响到pyshark这个库的设置的。是否打开重组IPv4数据包选项会影响到UDP流的追踪结果(这很显然啦,毕竟两个选项下udp的DATA大小都不一样了)。
直接贴一手队友的代码(开启重组数据包的选项):

import pyshark

def get_jpeg(data, cnt):
    start = data.find('ffd8ff') #jpg文件头
    data = data[start:]
    l = len(data)//2
    bs = int(data, base=16).to_bytes(l, 'big')
    with open(f'jpgs/{cnt}.jpeg', 'wb') as f:
        f.write(bs)


pcap = pyshark.FileCapture('lx100.pcap', display_filter='udp', include_raw=True, use_json=True)
cnt = 0
for p in pcap:
    if p.highest_layer == 'DATA_RAW':
        data = str(p['UDP'].get('payload_raw')[0])
        if data.endswith('ffd9'): # jpg文件尾
            cnt += 1
            get_jpeg(data, cnt)

在这里插入图片描述

  • 8
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值