如何在python

时间:2016-01-05 15:08:12

标签: python networking byte icmp

为了学习,我目前正在尝试创建一个简单的python porgram来向某个设备发送ICMP ping数据包。为了开始,我查看了python模块Pyping的源代码:https://github.com/Akhavi/pyping/blob/master/pyping/core.py

我试图了解发送和构建数据包时发生的所有情况,但是我已经设法卡在代码的一部分上,并且似乎无法确切地知道它的功能和用途是什么。我一直在研究ICMP数据包,我知道它们包含类型代码校验和和数据现在困扰我的代码是:

    self.own_id = os.getpid() & 0xFFFF

    header = struct.pack(
        "!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
    )

    padBytes = []
    startVal = 0x42
    for i in range(startVal, startVal + (self.packet_size)):
        padBytes += [(i & 0xff)]  # Keep chars in the 0-255 range
    data = bytes(padBytes)

我的问题是:

  1. 将self.own_id和self.seq_number添加到标题中有什么用?
  2. 在for循环中计算的是什么,为什么它具有特定的起始值0x42?
  3. 我是网络新手,非常感谢任何帮助。

3 个答案:

答案 0 :(得分:7)

ICMP Echo Request数据包的描述

ICMP Echo Request PDU看起来像这样:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Type(8)   |     Code(0)   |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Identifier          |        Sequence Number        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             Payload                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这里是上面wiki链接中各个字段的描述:

  

客户端可以使用标识符和序列号将回复与导致回复的请求进行匹配。

     

实际上,大多数Linux系统都为每个ping进程使用唯一标识符,并且序列号在该进程中是一个递增的数字。 Windows使用固定标识符,该标识符在Windows版本之间变化,并且序列号仅在引导时重置。

pyping代码

的说明

标头生成

查看send_one_ping的完整函数体,这是您的代码所在的位置。我将用一些信息对其进行注释:

def send_one_ping(self, current_socket):
    """
    Send one ICMP ECHO_REQUEST
    """
    # Header is type (8), code (8), checksum (16), id (16), sequence (16)

    # Annotation: the Type is 8 bits, the code is 8 bits, the
    # header checksum is 16 bits

    # Additional Header Information is 32-bits (identifier and sequence number)

    # After that is Payload, which is of arbitrary length.

所以这一行

    header = struct.pack(
        "!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
    )

此行使用struct和布局!BBHHH创建数据包标头,表示:

  • B - 无符号字符(8位)
  • B - 无符号字符(8位)
  • H - 无符号短(16位)
  • H - 无符号短(16位)
  • H - 无符号短(16位)

所以标题看起来像这样:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     ICMP_ECHO  |     0        |          checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           self.own_id         |        self.seq_number        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

请注意:

  • self.own_id设置发送此数据的应用程序的标识符。对于此代码,它只使用程序的程序标识号。
  • self.seq_number设置序列号。这有助于您识别连续发送多个ICMP请求数据包。它可以帮助您完成计算ICMP数据包丢失等事情。

客户端可以使用组合的标识符和序列号字段来匹配回应请求的回应答复。

有效负载生成

现在让我们转到Payload部分。有效负载具有任意长度,但Ping类此代码从默认值获取为总数据包有效负载大小55 bytes

所以下面的部分只是创建了一堆任意字节来填充有效载荷部分。

padBytes = []
startVal = 0x42

# Annotation: 0x42 = 66 decimal
# This loop would go from [66, 66 + packet_size],
# which in default pyping means [66, 121)
for i in range(startVal, startVal + (self.packet_size)):
    padBytes += [(i & 0xff)]  # Keep chars in the 0-255 range
data = bytes(padBytes)

最后,byte(padBytes)实际上是这样的:

>> bytes(padBytes)
b'BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwx'

为什么选择0x42

据我所知,0x42作为有效载荷标识符没有实际意义,因此这似乎相当随意。这里的有效载荷实际上毫无意义。正如您在“有效载荷生成”部分中看到的那样,它只会生成一个并不真正意味着什么的连续序列。他们本可以决定用0x42字节填充整个数据包有效负载。如果需要的话。

答案 1 :(得分:0)

使用scapy http://www.secdev.org/projects/scapy/

Scapy是用python编写的数据包操作框架。 你可以伪造很多种类的数据包(http,tcp,ip,udp,icmp等......)

答案 2 :(得分:0)

ICMP echo请求和echo reply更好地称为ping。

ID和序列号允许将回复与请求进行匹配。它们是必要的,因为ICMP协议没有源和目标端口,如TCP和UDP,只有源和目标IP地址。多个进程可以ping同一主机,并且必须将回复传递给正确的进程。因此,每个回复都包含从相应请求中逐字复制的数据。 ID标识发送请求的进程。序列号有助于将回复与流程中的请求进行匹配。这对于计算RTT(往返时间)和检测未答复的ping是必要的。

循环中计算的数据是有效载荷,也是从请求复制到回复的字面上。有效负载是可选的,ping实现可以将其用于任何需要的地方。

为什么是0x42?我猜这位作者可能是道格拉斯·亚当斯的粉丝。