实现功能
在计算机里捕获IP协议报文、UDP协议报文、TCP协议报文,并分析报文里的关键信息。
流程
搭建服务基本框架
基于操作系统的线程池实现。
链接:操作系统学习实战篇——实现支持异步任务的线程池
网卡的工作模式
本文将网卡设置成混杂模式
server.py
# -*- encoding=utf-8 -*-
import socket
import json
from 计算机操作系统.pool import ThreadPool as tp
from 计算机操作系统.task import AsyncTask
class ProcessTask(AsyncTask):
def __init__(self,packet,*args,**kwargs):
self.packet = packet
AsyncTask(func=self.process,*args,**kwargs)
pass
def process(self):
pass
class Server:
def __init__(self):
# 工作协议类型、套接字类型、工作具体的协议
self.sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
# 套接字绑定到具体端口
# 自己的主机IP,在终端通过 ipconfig 命令查看
self.ip = '192.168.1.105'
self.port = 8888
self.sock.bind((self.ip, self.port))
# 混杂模式
self.sock.ioctl(socket.SIO_RCVALL,socket.RCVALL_ON)
# 定义线程池,线程数量10个
self.pool = tp(10)
self.pool.start()
def loop_server(self):
while True:
# 1. 接收
packet, addr = self.sock.recv(65535)
# 2. 生成Task
task = ProcessTask(packet)
# 3. 提交
self.pool.put(task)
# 4. 获取结果
result = task.get_result()
result = json.dumps(
result,
indent=4
)
print(result)
if __name__=='__main__':
server = Server()
server.loop_server()
Python操作字节序列
字节序:
大端字节序:高位字节在前,低位字节在后
小端字节序:低位字节在前,高位字节在后
由于计算机电路先处理低位字节效率比较高,主机的CPU处理时会按照小端字节序处理,但是呈现出来是大端字节序的形式。
而人类习惯读写大端字节序,所以网络中使用的是大端字节序。
struct_demo.py
# -*- encoding=utf-8 -*-
import struct
# 八个字节
bin_str = b'ABCD1234'
print(bin_str)
# B 一个字节转换为一个整数
# >表示大端字节序
result = struct.unpack('>BBBBBBBB', bin_str)
print(result)
# H 两个字节转换为一个整数
result = struct.unpack('>HHHH', bin_str)
print(result)
# L 四个字节转换为一个整数
result = struct.unpack('>LL', bin_str)
print(result)
# 八个ASCII码的字符
result = struct.unpack('>8s', bin_str)
print(result)
result = struct.unpack('>BBHL', bin_str)
print(result)
实现IP报文解析器
parser.py (net包下)
# -*- encoding=utf-8 -*-
import struct
import socket
class IPParser:
IP_HEADER_LENGTH = 20
@classmethod
def parser_ip_header(cls, ip_header):
'''
IP报文格式
1. 4位IP-version 4位IP头长度 8位服务类型 16位总长度
2.16位标识符 3位标记符 13位片偏移
3. 8位TTL 8位协议 16位IP头部校验和
4.32位源IP地址
5.32位目的IP地址
'''
# 第一行
line1 = struct.unpack('>BBH', ip_header[:4])
ip_version = line1[0] >> 4
iph_length = line1[0] & 15 * 4
pkt_length = line1[2]
# 第三行
line3 = struct.unpack('>BBH', ip_header[8:12])
TTL = line3[0]
# 1表示ICMP协议,6表示TCP协议,17表示UDP协议
protocol = line3[1]
iph_checksum = line3[2]
# 第四行
line4 = struct.unpack('>4s', ip_header[12:16])
src_ip = socket.inet_ntoa(line4[0])
# 第五行
line5 = struct.unpack('>4s', ip_header[16:20])
dst_ip = socket.inet_ntoa(line5[0])
return {
'ip_version': ip_version,
'iph_length': iph_length,
'packet_length': pkt_length,
'TTL': TTL,
'protocol': protocol,
'iph_checksum': iph_checksum,
'src_ip': src_ip,
'dst_ip': dst_ip
}
@classmethod
def parser(cls, packet):
ip_header = packet[:20]
return cls.parser_ip_header(ip_header)
server.py里ProcessTask类修改:
class ProcessTask(AsyncTask):
def __init__(self,packet,*args,**kwargs):
self.packet = packet
# AsyncTask(func=self.process,*args,**kwargs)
super(ProcessTask,self).__init__(func=self.process,*args,**kwargs)
def process(self):
ip_header = IPParser.parser(self.packet)
return ip_header
实现UDP报文解析器
parser.py(trans包下)
# -*- encoding=utf-8 -*-
import struct
class TransParser:
# 定义IP头部偏移
IP_HEADER_OFFSET = 20
UDP_HEADER_LENGTH = 8
class UDPParser(TransParser):
'''
1. 16位源端口 16位目的端口
2. 16位UDP长度 16位校验和
'''
@classmethod
def parser_udp_header(cls, udp_header):
udp_header = struct.unpack('>HHHH', udp_header)
return{
'src_port': udp_header[0],
'dst_port': udp_header[1],
'udp_length': udp_header[2],
'udp_checksum': udp_header[3]
}
pass
@classmethod
def parser(cls, packet):
udp_header = packet[cls.IP_HEADER_OFFSET:cls.IP_HEADER_OFFSET+cls.UDP_HEADER_LENGTH]
return cls.parser_udp_header(udp_header)
修改server.py的ProcessTask类
class ProcessTask(AsyncTask):
def __init__(self,packet,*args,**kwargs):
self.packet = packet
# AsyncTask(func=self.process,*args,**kwargs)
super(ProcessTask,self).__init__(func=self.process,*args,**kwargs)
def process(self):
headers = {
'network_header': None,
'transport_header': None
}
ip_header = IPParser.parser(self.packet)
headers['network_header'] = ip_header
if ip_header['protocol'] == 17:
udp_header = UDPParser.parser(self.packet)
headers['transport_header'] = udp_header
return headers
实现TCP报文解析器
修改parser.py(trans包下)
# -*- encoding=utf-8 -*-
import struct
class TransParser:
# 定义IP头部偏移
IP_HEADER_OFFSET = 20
UDP_HEADER_LENGTH = 8
TCP_HEADER_LENGTH = 20
def data2str(data):
l = len(data)
data = struct.unpack(l*'B',data)
string = ''
for ch in data:
if ch >= 127 or ch < 32:
string += '.'
else:
string += chr(ch)
return string
class UDPParser(TransParser):
'''
1. 16位源端口 16位目的端口
2. 16位UDP长度 16位校验和
'''
@classmethod
def parser_udp_header(cls, udp_header):
udp_header = struct.unpack('>HHHH', udp_header)
return{
'src_port': udp_header[0],
'dst_port': udp_header[1],
'udp_length': udp_header[2],
'udp_checksum': udp_header[3]
}
pass
@classmethod
def parser(cls, packet):
udp_header = packet[cls.IP_HEADER_OFFSET:cls.IP_HEADER_OFFSET+cls.UDP_HEADER_LENGTH]
data = packet[cls.IP_HEADER_OFFSET+cls.UDP_HEADER_LENGTH:]
result = cls.parser_udp_header(udp_header)
data = data2str(data)
result['data'] = data
return result
class TCPParser(TransParser):
'''
1. 16位源端口 16位目的端口
2. 序列号
3. 确认号
4. 4位数据偏移 6位保留字段 6位标志位 16位窗口大小
5. 16位校验和 16位紧急指针
'''
@classmethod
def parser_tcp_header(cls, tcp_header):
line1 = struct.unpack('>HH', tcp_header[:4])
src_port = line1[0]
dst_port = line1[1]
line2 = struct.unpack('>L', tcp_header[4:8])
seq_num = line2[0]
line3 = struct.unpack('>L', tcp_header[8:12])
ack_num = line3[0]
line4 = struct.unpack('>BBH', tcp_header[12:16])
data_offset = line4[0] >> 4
flags = line4[1] & int('00111111',2)
FIN = flags & 1
SYN = (flags >> 1) & 1
RST = (flags >> 2) & 1
PSH = (flags >> 3) & 1
ACK = (flags >> 4) & 1
URG = (flags >> 5) & 1
win_size = line4[2]
line5 = struct.unpack('>HH', tcp_header[16:20])
checksum = line5[0]
urg_point = line5[1]
return{
'src_port': src_port,
'dst_port': dst_port,
'seq_num': seq_num,
'ack_num': ack_num,
'data_offset': data_offset,
'flag': {
'FIN': FIN,
'SYN': SYN,
'RST': RST,
'PSH': PSH,
'ACK': ACK,
'URG': URG
},
'win_size': win_size,
'checksum': checksum,
'urg_point': urg_point
}
@classmethod
def parser(cls, packet):
tcp_header = packet[cls.IP_HEADER_OFFSET:cls.IP_HEADER_OFFSET+cls.TCP_HEADER_LENGTH]
data = packet[cls.IP_HEADER_OFFSET+cls.TCP_HEADER_LENGTH:]
data = data2str(data)
result = cls.parser_tcp_header(tcp_header)
result['data'] = data
return result
修改server.py的ProcessTask类
class ProcessTask(AsyncTask):
def __init__(self,packet,*args,**kwargs):
self.packet = packet
# AsyncTask(func=self.process,*args,**kwargs)
super(ProcessTask,self).__init__(func=self.process,*args,**kwargs)
def process(self):
headers = {
'network_header': None,
'transport_header': None
}
ip_header = IPParser.parser(self.packet)
headers['network_header'] = ip_header
if ip_header['protocol'] == 17:
udp_header = UDPParser.parser(self.packet)
headers['transport_header'] = udp_header
elif ip_header['protocol'] == 6:
tcp_header = TCPParser.parser(self.packet)
headers['transport_header'] = tcp_header
return headers