from scapy.all import *
from scapy.contrib.modbus import ModbusADURequest, ModbusADUResponse
from scapy.layers.inet import TCP
from collections import defaultdict
class State:
IDLE = "Idle"
AWAITING_RESPONSE = "Awaiting_Response"
COMPLETED = "Completed"
ERROR = "Error"
class ModbusSession:
def __init__(self, trans_id, start_time):
self.trans_id = trans_id
self.start_time = start_time
self.state = State.AWAITING_RESPONSE
self.timeout = 2.0
class ModbusFSM:
def __init__(self):
self.sessions = defaultdict(lambda: None)
self.current_state = State.IDLE
def process_packet(self, pkt):
if TCP in pkt and (pkt[TCP].dport == 502 or pkt[TCP].sport == 502):
if ModbusADURequest in pkt:
self._handle_request(pkt)
elif ModbusADUResponse in pkt:
self._handle_response(pkt)
self._cleanup_timeout_sessions(pkt.time)
def _handle_request(self, pkt):
trans_id = pkt.transId
print(f"[Request] 时间: {pkt.time:.6f}s | TransID: {trans_id} | 功能码: 0x{pkt.funcCode:02x}")
self.sessions[trans_id] = ModbusSession(trans_id, pkt.time)
self.current_state = State.AWAITING_RESPONSE
def _handle_response(self, pkt):
trans_id = pkt.transId
session = self.sessions.get(trans_id)
if not session:
print(f"[Warning] 未知事务ID: {trans_id}")
return
if session.state == State.AWAITING_RESPONSE:
if pkt.funcCode & 0x80:
print(f"[Error] 事务ID: {trans_id} | 异常功能码: 0x{pkt.funcCode:02x}")
session.state = State.ERROR
else:
print(f"[Response] 事务ID: {trans_id} | 正常响应")
session.state = State.COMPLETED
del self.sessions[trans_id]
def _cleanup_timeout_sessions(self, current_time):
timeout_trans = []
for trans_id, session in self.sessions.items():
if session and (current_time - session.start_time > session.timeout):
print(f"[Timeout] 事务ID: {trans_id} | 等待超时")
timeout_trans.append(trans_id)
for trans_id in timeout_trans:
del self.sessions[trans_id]
if __name__ == "__main__":
packets = rdpcap("F:/研究生/数据集/Modbus_dataset-master/Modbus_dataset-master/CnC_uploading_exe_modbus_6RTU_with_operate.pcap")
fsm = ModbusFSM()
sorted_packets = sorted(packets, key=lambda x: x.time)
for pkt in sorted_packets:
fsm.process_packet(pkt)