2025-01-07 19:32:26 +00:00
|
|
|
|
这段代码实现了一个基本的网络通信工具,允许用户通过图形用户界面(GUI)构造并发送不同类型的网络报文(如ICMP、UDP、TCP、DNS、IP)。让我们逐部分深入分析代码的实现原理和工作方式。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 1. **导入模块**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
|
|
|
|
import tkinter as tk
|
2025-01-06 06:49:56 +00:00
|
|
|
|
from tkinter import ttk, messagebox
|
2024-12-08 15:15:32 +00:00
|
|
|
|
import socket
|
|
|
|
|
import struct
|
|
|
|
|
import time
|
2025-01-06 06:49:56 +00:00
|
|
|
|
import uuid
|
|
|
|
|
import re
|
2025-01-07 19:32:26 +00:00
|
|
|
|
import random
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- **tkinter**: 用于构建GUI,提供创建窗口、标签、按钮等界面元素的功能。
|
|
|
|
|
- **socket**: 用于低层次的网络操作,如创建套接字、发送和接收数据等。
|
|
|
|
|
- **struct**: 用于处理二进制数据的打包和解包,例如网络数据报文的处理。
|
|
|
|
|
- **time**: 用于获取时间戳、计算延迟等。
|
|
|
|
|
- **uuid**: 用于获取唯一的标识符,特别是获取本机的MAC地址。
|
|
|
|
|
- **re**: 用于正则表达式,主要用来验证输入的IP地址或域名的格式。
|
|
|
|
|
- **random**: 用于生成随机数,这里主要用在构造DNS查询报文时随机生成事务ID。
|
|
|
|
|
|
|
|
|
|
### 2. **获取本机信息**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
|
|
|
|
def get_local_info():
|
|
|
|
|
hostname = socket.gethostname()
|
|
|
|
|
local_ips = socket.gethostbyname_ex(hostname)[-1]
|
|
|
|
|
mac = ':'.join(re.findall('..', '%012x' % uuid.getnode()))
|
|
|
|
|
return hostname, mac, local_ips
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- **`socket.gethostname()`**: 获取当前计算机的主机名。
|
|
|
|
|
- **`socket.gethostbyname_ex(hostname)[-1]`**: 根据主机名获取本机所有的IP地址(IPv4),返回的列表中的最后一个元素包含了所有本机的IP。
|
|
|
|
|
- **`uuid.getnode()`**: 获取计算机的硬件地址(MAC地址)。返回的是一个64位的数字,我们通过格式化成16进制,并使用正则表达式将其格式化为常见的MAC地址格式(如:`00:1A:2B:3C:4D:5E`)。
|
2025-01-06 06:49:56 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 3. **验证IP地址或域名**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
|
|
|
|
def validate_ip_or_domain(value):
|
|
|
|
|
ip_pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$')
|
|
|
|
|
domain_pattern = re.compile(r'^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$')
|
|
|
|
|
return ip_pattern.match(value) or domain_pattern.match(value)
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 该函数通过正则表达式检查用户输入的值是否符合IP地址或域名的格式。
|
|
|
|
|
- **IP地址格式**: `\d{1,3}(\.\d{1,3}){3}` 匹配四个以点分隔的1到3位数字。
|
|
|
|
|
- **域名格式**: `([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}` 匹配由字母、数字或中划线组成的域名部分和一个或多个子域名部分。
|
2025-01-06 06:49:56 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 4. **验证端口号**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
|
|
|
|
def validate_port(port):
|
|
|
|
|
return port.isdigit() and 1 <= int(port) <= 65535
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 该函数检查端口号是否在1到65535的范围内,确保用户输入的是有效的端口号。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 5. **构造IP头部**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
|
|
|
|
def construct_ip_header(src_ip, dest_ip, payload_length, protocol):
|
|
|
|
|
version = 4
|
|
|
|
|
ihl = 5
|
|
|
|
|
tos = 0
|
2025-01-07 19:32:26 +00:00
|
|
|
|
total_length = 20 + payload_length # 20字节的IP头部 + 数据部分长度
|
2025-01-06 06:49:56 +00:00
|
|
|
|
identification = 54321
|
|
|
|
|
flags_offset = 0
|
|
|
|
|
ttl = 64
|
|
|
|
|
header_checksum = 0
|
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
src_ip_bytes = socket.inet_aton(src_ip) # 转换IP地址为二进制格式
|
|
|
|
|
dest_ip_bytes = socket.inet_aton(dest_ip) # 转换目标IP为二进制格式
|
2025-01-06 06:49:56 +00:00
|
|
|
|
ip_header = struct.pack('!BBHHHBBH4s4s',
|
|
|
|
|
(version << 4) + ihl, tos, total_length,
|
|
|
|
|
identification, flags_offset, ttl, protocol,
|
|
|
|
|
header_checksum, src_ip_bytes, dest_ip_bytes)
|
|
|
|
|
|
|
|
|
|
return ip_header
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 该函数用于构造IP头部,按网络字节序(大端字节序)格式将各种字段打包。
|
|
|
|
|
- **`struct.pack`**: 用于将数据按指定格式打包成二进制数据。格式`'!BBHHHBBH4s4s'`表示:
|
|
|
|
|
- `!`:大端字节序。
|
|
|
|
|
- `B`:一个字节(8位),表示一个无符号整数。
|
|
|
|
|
- `H`:两个字节(16位),表示一个无符号整数。
|
|
|
|
|
- `4s`:4个字节的字符串(即4个字符的IP地址)。
|
|
|
|
|
- **字段说明**:
|
|
|
|
|
- **版本(version)**: 使用IPv4协议,版本号为4。
|
|
|
|
|
- **头部长度(ihl)**: 5表示头部长度为5个32位字(即20字节)。
|
|
|
|
|
- **总长度(total_length)**: 包括IP头部和数据部分的总长度。
|
|
|
|
|
- **TTL(ttl)**: 存活时间(Time To Live),默认为64。
|
|
|
|
|
- **协议(protocol)**: 用于指示数据部分使用的协议类型,如TCP、UDP、ICMP等。
|
|
|
|
|
|
|
|
|
|
### 6. **构造ICMP报文**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
|
|
|
|
def construct_icmp():
|
|
|
|
|
icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1)
|
2025-01-06 06:49:56 +00:00
|
|
|
|
data = b'Ping'
|
|
|
|
|
checksum = calculate_checksum(icmp_header + data)
|
2024-12-08 15:15:32 +00:00
|
|
|
|
icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1)
|
2025-01-06 06:49:56 +00:00
|
|
|
|
return icmp_header + data
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- **ICMP报文**用于网络诊断(例如Ping)。
|
|
|
|
|
- **`struct.pack('!BBHHH', 8, 0, 0, 1, 1)`**构造一个ICMP头部。`8`表示回显请求类型,`0`表示无错误,`checksum`和`seq`等字段在后续计算和填充。
|
|
|
|
|
- **`calculate_checksum`**: 计算ICMP报文的校验和,确保数据在传输过程中没有被篡改。
|
|
|
|
|
- **数据部分**: 这里简单地将“Ping”作为数据发送,实际应用中可以是更复杂的内容。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 7. **构造UDP报文**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
|
|
|
|
def construct_udp(src_port, dest_port):
|
|
|
|
|
udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0)
|
|
|
|
|
return udp_header
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 构造UDP头部,包含源端口、目标端口、长度和校验和。这里暂时将校验和置为0。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 8. **构造TCP报文(SYN)**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
2025-01-06 06:49:56 +00:00
|
|
|
|
def construct_tcp(src_port, dest_port):
|
|
|
|
|
seq = 0
|
|
|
|
|
ack_seq = 0
|
|
|
|
|
offset = 5
|
|
|
|
|
reserved = 0
|
2025-01-07 19:32:26 +00:00
|
|
|
|
flags = 0b000010 # SYN标志位为1,表示建立连接
|
|
|
|
|
window = socket.htons(5840) # 窗口大小
|
2025-01-06 06:49:56 +00:00
|
|
|
|
checksum = 0
|
|
|
|
|
urgent_ptr = 0
|
|
|
|
|
|
|
|
|
|
tcp_header = struct.pack('!HHLLBBHHH',
|
|
|
|
|
src_port, dest_port, seq, ack_seq,
|
|
|
|
|
(offset << 4) + reserved, flags, window,
|
|
|
|
|
checksum, urgent_ptr)
|
|
|
|
|
return tcp_header
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- **SYN报文**用于TCP的三次握手,建立连接时发送。
|
|
|
|
|
- **`flags = 0b000010`**表示SYN标志为1。
|
|
|
|
|
- **`window`**: 接收窗口大小。
|
|
|
|
|
- **`seq`**和**`ack_seq`**: TCP的序列号和确认号,初始化为0。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 9. **计算校验和**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
|
|
|
|
def calculate_checksum(data):
|
|
|
|
|
checksum = 0
|
|
|
|
|
n = len(data)
|
2025-01-07 19:32:26 +00:00
|
|
|
|
|
2025-01-06 06:49:56 +00:00
|
|
|
|
for i in range(0, n - 1, 2):
|
|
|
|
|
chunk = (data[i] << 8) + data[i + 1]
|
|
|
|
|
checksum += chunk
|
2025-01-07 19:32:26 +00:00
|
|
|
|
|
2025-01-06 06:49:56 +00:00
|
|
|
|
if n % 2 == 1:
|
|
|
|
|
checksum += data[-1] << 8
|
2025-01-07 19:32:26 +00:00
|
|
|
|
|
2025-01-06 06:49:56 +00:00
|
|
|
|
checksum = (checksum >> 16) + (checksum & 0xFFFF)
|
|
|
|
|
checksum += (checksum >> 16)
|
2025-01-07 19:32:26 +00:00
|
|
|
|
|
2025-01-06 06:49:56 +00:00
|
|
|
|
return ~checksum & 0xFFFF
|
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 该函数计算数据的校验和,主要用于检测数据在传输过程中是否发生了错误。
|
|
|
|
|
- 将数据拆分为16位块并求和。
|
|
|
|
|
- 如果数据长度为奇数,则补零。
|
|
|
|
|
- 最后对结果取反,并返回16位校验和。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 10. **构造DNS查询报文**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
2025-01-07 19:32:26 +00:00
|
|
|
|
def build_dns_query(domain):
|
|
|
|
|
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
# 随机生成事务ID
|
|
|
|
|
txid = random.randint(0, 65535)
|
|
|
|
|
flags = 0x0100 # 查询标志
|
|
|
|
|
qdcount = 1 # 查询数量
|
|
|
|
|
ancount = 0 # 回答数量
|
|
|
|
|
nscount = 0 # 权威服务器数量
|
|
|
|
|
arcount = 0 # 附加记录数量
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
query = struct.pack('>HHHHHH', txid, flags, qdcount, ancount, nscount, arcount)
|
|
|
|
|
query += encode_domain(domain)
|
|
|
|
|
query += struct.pack('>HH', 1, 1) # 类型为A记录(IPv4地址)
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
return query
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- **`build_dns_query`**构造DNS查询报文,查询给定域名的A记录(即返回IPv4地址)。
|
|
|
|
|
- **`encode_domain(domain)`**: 将域名按照DNS协议格式编码,域名分段,每段前面加上该段的长度。
|
|
|
|
|
- 事务ID(txid)随机生成,查询的标志位设置为标准查询。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 11. **发送报文**
|
2024-12-08 15:15:32 +00:00
|
|
|
|
```python
|
2025-01-07 19:32:26 +00:00
|
|
|
|
def send_packet():
|
|
|
|
|
src_ip = ip_combobox.get()
|
|
|
|
|
dest_host = dest_entry.get()
|
|
|
|
|
dest_port = port_entry.get()
|
|
|
|
|
packet_type = var.get()
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 该函数会根据用户在GUI中输入的源IP地址、目标地址、目标端口和报文类型(ICMP、UDP、TCP、DNS、IP)来发送报文。具体的报文构造和发送过程,会根据不同类型的报文选择相应的构造函数。
|
2024-12-08 15:15:32 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
### 12. **GUI界面设计与事件循环**
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```python
|
2025-01-07 19:32:26 +00:00
|
|
|
|
window = tk.Tk()
|
|
|
|
|
window.title("网络通信软件")
|
2025-01-06 06:49:56 +00:00
|
|
|
|
```
|
2025-01-07 19:32:26 +00:00
|
|
|
|
- 使用**tkinter**创建GUI窗口,设置窗口标题和布局。
|
|
|
|
|
- 通过**`Label`**、**`Entry`**、**`Button`**等控件让用户输入本机IP、目标地址、端口和报文类型。
|
|
|
|
|
- 通过**`Button`**控件触发发送报文的功能,调用 `send_packet()`。
|
2025-01-06 06:49:56 +00:00
|
|
|
|
|
2025-01-07 19:32:26 +00:00
|
|
|
|
**总结:**
|
|
|
|
|
这段代码的核心功能是通过GUI界面实现用户自定义网络报文的构造、发送和反馈。主要操作包括获取本机信息、验证输入数据、根据不同报文类型(如ICMP、TCP、UDP、DNS)构造报文,最后通过`socket`库将其发送到指定目标。
|