#!/usr/bin/env python # -*- coding: utf-8 -*- #https://scapy.readthedocs.io/en/latest/api/scapy.packet.html#scapy.packet.Packet #https://scapy.readthedocs.io/en/latest/api/scapy.contrib.modbus.html import getpass import scapy.all as scapy import scapy.contrib.modbus as mb from triPacket import triPacket def BytesToBits(value): status = [] for i in range(len(value)): #Transform the i'th byte into 1 binary string val = str(bin(value[i])) #Delete the '0x' header val = val[2:] #Fill with '0' to have 8 values val = val.rjust(8,'0') #Revert the list to get into the growing order val = val[::-1] #Add the 8 values on `status` status.extend(j for j in [*val]) return status def decode(pkt): '''Insert the packet's `pkt` the [type_call,address,status] informations into the `connec` database ''' if "ModbusADU" in pkt: global miniL modpkt = pkt["ModbusADU"] type_call = "" #Multiple Coils/Registers request-> else: Single request if "Multiple" in modpkt.payload.name: type_call += "m" else: type_call += "s" #Coils or Registers request if "Coil" in modpkt.payload.name: type_call += "C" else: type_call += "R" miniL[0] = type_call #Read request (read request are all "Multiple request") if "Read" in modpkt.payload.name: type_call += "r" #Response for a read request packet's if "Response" in modpkt.payload.name: #Number of byte that have been read #byte_count = modpkt.payload.getfieldval("byteCount") status = [] #Response for a read coils request packet's if "C" in type_call: #Get the list of byte's values that have been read value = modpkt.payload.getfieldval("coilStatus") #Transform the byte's values into a list of bits values for each byte status = BytesToBits(value) #Response for a read registers request packet's else: #Get the list of byte's values that have been read status = modpkt.payload.getfieldval("registerVal") #Fill the list of the values/address that have been read LPackets = [] for j in range(len(status)): LPackets.append([str.lower(miniL[0][1]),miniL[2]+j,status[j]]) #Add the read's status into the `connect` database triPacket(LPackets,connec) #Reset the globalization's list ####miniL = [0,0,0] #First request for a read packet's : Get the starting address and globalize it before getting the response else: addr = modpkt.payload.getfieldval("startAddr") miniL[2] = addr #Write request else: type_call += "w" #Write request needed information aren't on the Response packet's if "Response" in modpkt.payload.name: #Reset of the globalization miniL = [0,0,0] else: #Multiple write request if "m" in type_call: #Get the starting address addr = modpkt.payload.getfieldval("startAddr") #Get the list of values that have been read value = modpkt.payload.getfieldval("outputsValue") status = [] #Multiple write coils request (register's values are already on the good format) LPackets = [] #Get the number of bytes to be write in order to not reset to 0, address on the same bytes of the written one's if "C" in type_call: #Transform the byte's values into a list of bits values for each byte status = BytesToBits(value) byte_count = modpkt.payload.getfieldval("quantityOutput") #Fill the list of the values/address that while be write else: byte_count = modpkt.payload.getfieldval("quantityRegisters") status=value for j in range(byte_count): if miniL[0][1]=='C' and status[j]==65280: status[j]=1 LPackets.append([str.lower(miniL[0][1]),addr+j,status[j]]) #Add and check the write's status into the `connect` database triPacket(LPackets,connec) #Single write request else: #Single write coil request if "C" in type_call: addr = modpkt.payload.getfieldval("outputAddr") value = modpkt.payload.getfieldval("outputValue") if value==65280: value=1 #Single write register request else: addr = modpkt.payload.getfieldval("registerAddr") value = modpkt.payload.getfieldval("registerValue") #Add the write's status into the `connect` database triPacket([str.lower(miniL[0][1]),addr,value],connec) #Connection to the database print("In order for data sniffed to be stored inside the database, please register the following :") DB_HOST = input('host of the database server : ') if not DB_HOST: DB_HOST = '192.168.128.141' DB_NAME = input('name of the database : ') if not DB_NAME: DB_NAME = 'dblodufour1' DB_USER = input('login of the user : ') if not DB_USER: DB_USER = 'lodufour1' DB_PASSWORD = getpass.getpass('user password : ') connec=[DB_HOST,DB_NAME,DB_USER,DB_PASSWORD] #Initialization in order globalize the First Packet with his response miniL = [0,0,0] #Sniff the packets received on the localhost's network interface and call the decode() function for each of them scapy.sniff(iface="lo", prn=decode)