为了学习,我目前正在尝试创建一个简单的 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)
我的问题是:
- 在header中加上self.own_id和self.seq_number有什么用?
- for 循环中正在计算什么,为什么它有一个特定的起始值 0x42?
我是网络新手,非常感谢任何帮助。
最佳答案
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 链接的各个字段的描述:
The Identifier and Sequence Number can be used by the client to match the reply with the request that caused the reply.
In practice, most Linux systems use a unique identifier for every ping process, and sequence number is an increasing number within that process. Windows uses a fixed identifier, which varies between Windows versions, and a sequence number that is only reset at boot time.
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
创建数据包 header ,这意味着:
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 数据包丢失等操作。
客户端可以使用标识符和序列号字段组合来将回显回复与回显请求相匹配。
有效载荷生成
现在让我们转到有效负载部分。有效载荷是任意长度的,但是 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
作为 Payload 标识符没有实际意义,因此这看起来相当武断。这里的有效载荷实际上是毫无意义的。从 Payload Generation 部分可以看出,它只是生成一个连续的序列,实际上没有任何意义。如果他们愿意,他们本可以决定用 0x42
字节填充整个数据包负载。
关于python - 如何在 python 中构建 ICMP 数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34614893/