mirror of
https://github.com/zeek/zeek.git
synced 2025-10-15 13:08:20 +00:00
Rename analyzer/protocols -> analyzer/protocol
This commit is contained in:
parent
f7a10d915b
commit
4bc2ba60c9
279 changed files with 114 additions and 116 deletions
10
src/analyzer/protocol/modbus/CMakeLists.txt
Normal file
10
src/analyzer/protocol/modbus/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
include(BroPlugin)
|
||||
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
bro_plugin_begin(Modbus)
|
||||
bro_plugin_cc(Modbus.cc Plugin.cc)
|
||||
bro_plugin_bif(events.bif)
|
||||
bro_plugin_pac(modbus.pac modbus-analyzer.pac modbus-protocol.pac)
|
||||
bro_plugin_end()
|
45
src/analyzer/protocol/modbus/Modbus.cc
Normal file
45
src/analyzer/protocol/modbus/Modbus.cc
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
#include "Modbus.h"
|
||||
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
|
||||
|
||||
#include "events.bif.h"
|
||||
|
||||
using namespace analyzer::modbus;
|
||||
|
||||
ModbusTCP_Analyzer::ModbusTCP_Analyzer(Connection* c)
|
||||
: TCP_ApplicationAnalyzer("MODBUS", c)
|
||||
{
|
||||
interp = new binpac::ModbusTCP::ModbusTCP_Conn(this);
|
||||
}
|
||||
|
||||
ModbusTCP_Analyzer::~ModbusTCP_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::Done()
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::Undelivered(int seq, int len, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
void ModbusTCP_Analyzer::EndpointEOF(bool is_orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
||||
interp->FlowEOF(is_orig);
|
||||
}
|
||||
|
29
src/analyzer/protocol/modbus/Modbus.h
Normal file
29
src/analyzer/protocol/modbus/Modbus.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef ANALYZER_PROTOCOL_MODBUS_MODBUS_H
|
||||
#define ANALYZER_PROTOCOL_MODBUS_MODBUS_H
|
||||
|
||||
#include "analyzer/protocol/tcp/TCP.h"
|
||||
#include "modbus_pac.h"
|
||||
|
||||
namespace analyzer { namespace modbus {
|
||||
|
||||
class ModbusTCP_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
ModbusTCP_Analyzer(Connection* conn);
|
||||
virtual ~ModbusTCP_Analyzer();
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
|
||||
virtual void Undelivered(int seq, int len, bool orig);
|
||||
virtual void EndpointEOF(bool is_orig);
|
||||
|
||||
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new ModbusTCP_Analyzer(conn); }
|
||||
|
||||
protected:
|
||||
binpac::ModbusTCP::ModbusTCP_Conn* interp;
|
||||
};
|
||||
|
||||
} } // namespace analyzer::*
|
||||
|
||||
#endif
|
10
src/analyzer/protocol/modbus/Plugin.cc
Normal file
10
src/analyzer/protocol/modbus/Plugin.cc
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
#include "plugin/Plugin.h"
|
||||
|
||||
#include "Modbus.h"
|
||||
|
||||
BRO_PLUGIN_BEGIN(Modbus)
|
||||
BRO_PLUGIN_DESCRIPTION("Modbus Analyzer");
|
||||
BRO_PLUGIN_ANALYZER("MODBUS", modbus::ModbusTCP_Analyzer);
|
||||
BRO_PLUGIN_BIF_FILE(events);
|
||||
BRO_PLUGIN_END
|
295
src/analyzer/protocol/modbus/events.bif
Normal file
295
src/analyzer/protocol/modbus/events.bif
Normal file
|
@ -0,0 +1,295 @@
|
|||
## Generated for any modbus message regardless if the particular function
|
||||
## is further supported or not.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## is_orig: True if the event is raised for the originator side.
|
||||
event modbus_message%(c: connection, headers: ModbusHeaders, is_orig: bool%);
|
||||
|
||||
## Generated for any modbus exception message.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## code: The exception code.
|
||||
event modbus_exception%(c: connection, headers: ModbusHeaders, code: count%);
|
||||
|
||||
## Generated for a Modbus read coils request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address where of the first coil to be read.
|
||||
##
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read coils response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_coils_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus read discrete inputs request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil to be read.
|
||||
##
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_discrete_inputs_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read discrete inputs response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_discrete_inputs_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus read holding registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_holding_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read holding registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_holding_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read input registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_input_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read input registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_input_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus write single coil request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil to be written.
|
||||
##
|
||||
## value: The value to be written to the coil.
|
||||
event modbus_write_single_coil_request%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Generated for a Modbus write single coil response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil that was written.
|
||||
##
|
||||
## value: The value that was written to the coil.
|
||||
event modbus_write_single_coil_response%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Generated for a Modbus write single register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register to be written.
|
||||
##
|
||||
## value: The value to be written to the register.
|
||||
event modbus_write_single_register_request%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Generated for a Modbus write single register response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register that was written.
|
||||
##
|
||||
## value: The value that was written to the register.
|
||||
event modbus_write_single_register_response%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Generated for a Modbus write multiple coils request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil to be written.
|
||||
##
|
||||
## value: The values to be written to the coils.
|
||||
event modbus_write_multiple_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, coils: ModbusCoils%);
|
||||
|
||||
## Generated for a Modbus write multiple coils response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil that was written.
|
||||
##
|
||||
## quantity: The quantity of coils that were written.
|
||||
event modbus_write_multiple_coils_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## registers: The values to be written to the registers.
|
||||
event modbus_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register that was written.
|
||||
##
|
||||
## quantity: The quantity of registers that were written.
|
||||
event modbus_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus read file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks should be applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask to apply to the register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask to apply to the register.
|
||||
event modbus_mask_write_register_request%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks were applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask applied register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask applied to the register.
|
||||
event modbus_mask_write_register_response%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## read_start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## read_quantity: The number of registers to read.
|
||||
##
|
||||
## write_start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## write_registers: The values to be written to the registers.
|
||||
event modbus_read_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, read_start_address: count, read_quantity: count, write_start_address: count, write_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## written_registers: The register values read from the registers specified in the request.
|
||||
event modbus_read_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, written_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The address of the FIFO queue to read.
|
||||
event modbus_read_fifo_queue_request%(c: connection, headers: ModbusHeaders, start_address: count%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## fifos: The register values read from the FIFO queue on the device.
|
||||
event modbus_read_fifo_queue_response%(c: connection, headers: ModbusHeaders, fifos: ModbusRegisters%);
|
||||
|
610
src/analyzer/protocol/modbus/modbus-analyzer.pac
Normal file
610
src/analyzer/protocol/modbus/modbus-analyzer.pac
Normal file
|
@ -0,0 +1,610 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
#
|
||||
|
||||
%header{
|
||||
VectorVal* bytestring_to_coils(bytestring coils, uint quantity);
|
||||
RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header);
|
||||
%}
|
||||
|
||||
%code{
|
||||
VectorVal* bytestring_to_coils(bytestring coils, uint quantity)
|
||||
{
|
||||
VectorVal* modbus_coils = new VectorVal(BifType::Vector::ModbusCoils);
|
||||
|
||||
return modbus_coils;
|
||||
}
|
||||
|
||||
RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header)
|
||||
{
|
||||
RecordVal* modbus_header = new RecordVal(BifType::Record::ModbusHeaders);
|
||||
modbus_header->Assign(0, new Val(header->tid(), TYPE_COUNT));
|
||||
modbus_header->Assign(1, new Val(header->pid(), TYPE_COUNT));
|
||||
modbus_header->Assign(2, new Val(header->uid(), TYPE_COUNT));
|
||||
modbus_header->Assign(3, new Val(header->fc(), TYPE_COUNT));
|
||||
return modbus_header;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
refine flow ModbusTCP_Flow += {
|
||||
|
||||
function deliver_message(header: ModbusTCP_TransportHeader): bool
|
||||
%{
|
||||
if ( ::modbus_message )
|
||||
{
|
||||
BifEvent::generate_modbus_message(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
is_orig());
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# EXCEPTION
|
||||
function deliver_Exception(header: ModbusTCP_TransportHeader, message: Exception): bool
|
||||
%{
|
||||
if ( ::modbus_exception )
|
||||
{
|
||||
BifEvent::generate_modbus_exception(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.code});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=1
|
||||
function deliver_ReadCoilsRequest(header: ModbusTCP_TransportHeader, message: ReadCoilsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_coils_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_coils_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=1
|
||||
function deliver_ReadCoilsResponse(header: ModbusTCP_TransportHeader, message: ReadCoilsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_coils_response )
|
||||
{
|
||||
BifEvent::generate_modbus_read_coils_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
bytestring_to_coils(${message.bits}, ${message.bits}.length()*8));
|
||||
}
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=2
|
||||
function deliver_ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_discrete_inputs_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_discrete_inputs_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=2
|
||||
function deliver_ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_discrete_inputs_response )
|
||||
{
|
||||
BifEvent::generate_modbus_read_discrete_inputs_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
bytestring_to_coils(${message.bits}, ${message.bits}.length()*8));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=3
|
||||
function deliver_ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_holding_registers_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_holding_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=3
|
||||
function deliver_ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read holding register response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_holding_registers_response )
|
||||
{
|
||||
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i=0; i < ${message.registers}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_holding_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=4
|
||||
function deliver_ReadInputRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadInputRegistersRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_input_registers_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_input_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=4
|
||||
function deliver_ReadInputRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadInputRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read input register response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_input_registers_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i=0; i < (${message.registers})->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_input_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=5
|
||||
function deliver_WriteSingleCoilRequest(header: ModbusTCP_TransportHeader, message: WriteSingleCoilRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_coil_request )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil request %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=5
|
||||
function deliver_WriteSingleCoilResponse(header: ModbusTCP_TransportHeader, message: WriteSingleCoilResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_coil_response )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil response %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=6
|
||||
function deliver_WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_register_request )
|
||||
{
|
||||
BifEvent::generate_modbus_write_single_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=6
|
||||
function deliver_WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_single_register_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_single_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=15
|
||||
function deliver_WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_coils_request )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_coils_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
bytestring_to_coils(${message.coils}, ${message.quantity}));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=15
|
||||
function deliver_WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_coils_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_coils_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=16
|
||||
function deliver_WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersRequest): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus write multiple registers request byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_write_multiple_registers_request )
|
||||
{
|
||||
VectorVal * t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < (${message.registers}->size()); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_multiple_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=16
|
||||
function deliver_WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_multiple_registers_response )
|
||||
{
|
||||
BifEvent::generate_modbus_write_multiple_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.quantity});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=20
|
||||
function deliver_ReadFileRecordRequest(header: ModbusTCP_TransportHeader, message: ReadFileRecordRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_file_record_request )
|
||||
{
|
||||
//TODO: this need to be a vector of some Reference Request record type
|
||||
//VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${message.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r);
|
||||
//
|
||||
// Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, k);
|
||||
//
|
||||
// Val* l = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, l);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_read_file_record_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=20
|
||||
function deliver_ReadFileRecordResponse(header: ModbusTCP_TransportHeader, message: ReadFileRecordResponse): bool
|
||||
%{
|
||||
if ( ::modbus_read_file_record_response )
|
||||
{
|
||||
//VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < ${message.references}->size(); ++i )
|
||||
// {
|
||||
// //TODO: work the reference type in here somewhere
|
||||
// Val* r = new Val(${message.references[i].record_data}), TYPE_COUNT);
|
||||
// t->Assign(i, r);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_read_file_record_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=21
|
||||
function deliver_WriteFileRecordRequest(header: ModbusTCP_TransportHeader, message: WriteFileRecordRequest): bool
|
||||
%{
|
||||
if ( ::modbus_write_file_record_request )
|
||||
{
|
||||
//VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${message.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r);
|
||||
//
|
||||
// Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, k);
|
||||
//
|
||||
// Val* n = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, n);
|
||||
//
|
||||
// for ( unsigned int j = 0; j < (${message.references[i].register_value}->size()); ++j )
|
||||
// {
|
||||
// k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT);
|
||||
// t->Assign(i, k);
|
||||
// }
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_write_file_record_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# RESPONSE FC=21
|
||||
function deliver_WriteFileRecordResponse(header: ModbusTCP_TransportHeader, message: WriteFileRecordResponse): bool
|
||||
%{
|
||||
if ( ::modbus_write_file_record_response )
|
||||
{
|
||||
//VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
//for ( unsigned int i = 0; i < (${messages.references}->size()); ++i )
|
||||
// {
|
||||
// Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT);
|
||||
// t->Assign(i, r);
|
||||
//
|
||||
// Val* f = new Val((${message.references[i].file_num}), TYPE_COUNT);
|
||||
// t->Assign(i, f);
|
||||
//
|
||||
// Val* rn = new Val((${message.references[i].record_num}), TYPE_COUNT);
|
||||
// t->Assign(i, rn);
|
||||
//
|
||||
// for ( unsigned int j = 0; j<(${message.references[i].register_value}->size()); ++j )
|
||||
// {
|
||||
// Val* k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT);
|
||||
// t->Assign(i, k);
|
||||
// }
|
||||
|
||||
BifEvent::generate_modbus_write_file_record_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=22
|
||||
function deliver_MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterRequest): bool
|
||||
%{
|
||||
if ( ::modbus_mask_write_register_request )
|
||||
{
|
||||
BifEvent::generate_modbus_mask_write_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=22
|
||||
function deliver_MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterResponse): bool
|
||||
%{
|
||||
if ( ::modbus_mask_write_register_response )
|
||||
{
|
||||
BifEvent::generate_modbus_mask_write_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=23
|
||||
function deliver_ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersRequest): bool
|
||||
%{
|
||||
if ( ${message.write_byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read write multiple registers request write byte count %d", ${message.write_byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_write_multiple_registers_request )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < ${message.write_register_values}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.write_register_values[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_write_multiple_registers_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.read_start_address},
|
||||
${message.read_quantity},
|
||||
${message.write_start_address},
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=23
|
||||
function deliver_ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read write multiple registers response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_write_multiple_registers_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters);
|
||||
for ( unsigned int i = 0; i < ${message.registers}->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.registers[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_write_multiple_registers_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# REQUEST FC=24
|
||||
function deliver_ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueRequest): bool
|
||||
%{
|
||||
if ( ::modbus_read_fifo_queue_request )
|
||||
{
|
||||
BifEvent::generate_modbus_read_fifo_queue_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address});
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# RESPONSE FC=24
|
||||
function deliver_ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueResponse): bool
|
||||
%{
|
||||
if ( ${message.byte_count} % 2 != 0 )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
fmt("invalid value for modbus read FIFO queue response byte count %d", ${message.byte_count}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ::modbus_read_fifo_queue_response )
|
||||
{
|
||||
VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||
for ( unsigned int i = 0; i < (${message.register_data})->size(); ++i )
|
||||
{
|
||||
Val* r = new Val(${message.register_data[i]}, TYPE_COUNT);
|
||||
t->Assign(i, r);
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_read_fifo_queue_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
t);
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
};
|
||||
|
||||
|
||||
|
394
src/analyzer/protocol/modbus/modbus-protocol.pac
Normal file
394
src/analyzer/protocol/modbus/modbus-protocol.pac
Normal file
|
@ -0,0 +1,394 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
|
||||
enum function_codes {
|
||||
# Standard functions
|
||||
READ_COILS = 0x01,
|
||||
READ_DISCRETE_INPUTS = 0x02,
|
||||
READ_HOLDING_REGISTERS = 0x03,
|
||||
READ_INPUT_REGISTERS = 0x04,
|
||||
WRITE_SINGLE_COIL = 0x05,
|
||||
WRITE_SINGLE_REGISTER = 0x06,
|
||||
# READ_EXCEPTION_STATUS = 0x07,
|
||||
# DIAGNOSTICS = 0x08,
|
||||
# GET_COMM_EVENT_COUNTER = 0x0B,
|
||||
# GET_COMM_EVENT_LOG = 0x0C,
|
||||
WRITE_MULTIPLE_COILS = 0x0F,
|
||||
WRITE_MULTIPLE_REGISTERS = 0x10,
|
||||
# REPORT_SLAVE_ID = 0x11,
|
||||
READ_FILE_RECORD = 0x14,
|
||||
WRITE_FILE_RECORD = 0x15,
|
||||
MASK_WRITE_REGISTER = 0x16,
|
||||
READ_WRITE_MULTIPLE_REGISTERS = 0x17,
|
||||
READ_FIFO_QUEUE = 0x18,
|
||||
ENCAP_INTERFACE_TRANSPORT = 0x2B,
|
||||
|
||||
# Machine/vendor/network specific functions
|
||||
PROGRAM_484 = 0x09,
|
||||
POLL_484 = 0x0A,
|
||||
PROGRAM_584_984 = 0x0D,
|
||||
POLL_584_984 = 0x0E,
|
||||
PROGRAM_884_U84 = 0x12,
|
||||
RESET_COMM_LINK_884_U84 = 0x13,
|
||||
PROGRAM_CONCEPT = 0x28,
|
||||
FIRMWARE_REPLACEMENT = 0x7D,
|
||||
PROGRAM_584_984_2 = 0x7E,
|
||||
REPORT_LOCAL_ADDRESS = 0x7F,
|
||||
|
||||
# Exceptions (not really function codes but they are used similarly)
|
||||
READ_COILS_EXCEPTION = 0x81,
|
||||
READ_DISCRETE_INPUTS_EXCEPTION = 0x82,
|
||||
READ_HOLDING_REGISTERS_EXCEPTION = 0x83,
|
||||
READ_INPUT_REGISTERS_EXCEPTION = 0x84,
|
||||
WRITE_SINGLE_COIL_EXCEPTION = 0x85,
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION = 0x86,
|
||||
READ_EXCEPTION_STATUS_EXCEPTION = 0x87,
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION = 0x8F,
|
||||
WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x90,
|
||||
READ_FILE_RECORD_EXCEPTION = 0x94,
|
||||
WRITE_FILE_RECORD_EXCEPTION = 0x95,
|
||||
MASK_WRITE_REGISTER_EXCEPTION = 0x96,
|
||||
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x97,
|
||||
READ_FIFO_QUEUE_EXCEPTION = 0x98,
|
||||
};
|
||||
|
||||
# Main Modbus/TCP PDU
|
||||
type ModbusTCP_PDU(is_orig: bool) = record {
|
||||
header: ModbusTCP_TransportHeader;
|
||||
body: case is_orig of {
|
||||
true -> request: ModbusTCP_Request(header);
|
||||
false -> response: ModbusTCP_Response(header);
|
||||
};
|
||||
} &length=header.len+6, &byteorder=bigendian;
|
||||
|
||||
type ModbusTCP_TransportHeader = record {
|
||||
tid: uint16; # Transaction identifier
|
||||
pid: uint16; # Protocol identifier
|
||||
len: uint16; # Length of everyting after this field
|
||||
uid: uint8; # Unit identifier (previously 'slave address')
|
||||
fc: uint8; # MODBUS function code (see function_codes enum)
|
||||
} &byteorder=bigendian, &let {
|
||||
deliver: bool = $context.flow.deliver_message(this);
|
||||
};
|
||||
|
||||
type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
|
||||
READ_COILS -> readCoils: ReadCoilsRequest(header);
|
||||
READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsRequest(header);
|
||||
READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersRequest(header);
|
||||
READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersRequest(header);
|
||||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilRequest(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterRequest(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusRequest(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsRequest(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterRequest(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogRequest(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsRequest(header);
|
||||
WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersRequest(header);
|
||||
#REPORT_SLAVE_ID
|
||||
READ_FILE_RECORD -> readFileRecord: ReadFileRecordRequest(header);
|
||||
WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordRequest(header);
|
||||
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterRequest(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersRequest(header);
|
||||
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueRequest(header);
|
||||
#ENCAP_INTERFACE_TRANSPORT
|
||||
|
||||
# All the rest
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
# Responses
|
||||
#
|
||||
type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
|
||||
READ_COILS -> readCoils: ReadCoilsResponse(header);
|
||||
READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsResponse(header);
|
||||
READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersResponse(header);
|
||||
READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersResponse(header);
|
||||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilResponse(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterResponse(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusResponse(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsResponse(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterResponse(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogResponse(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsResponse(header);
|
||||
WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersResponse(header);
|
||||
#REPORT_SLAVE_ID
|
||||
READ_FILE_RECORD -> readFileRecord: ReadFileRecordResponse(header);
|
||||
WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordResponse(header);
|
||||
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterResponse(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersResponse(header);
|
||||
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueResponse(header);
|
||||
|
||||
# Exceptions
|
||||
READ_HOLDING_REGISTERS_EXCEPTION -> readHoldingRegistersException: Exception(header);
|
||||
WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultRegistersException: Exception(header);
|
||||
READ_COILS_EXCEPTION -> readCoilsException: Exception(header);
|
||||
READ_DISCRETE_INPUTS_EXCEPTION -> readDiscreteInputsException: Exception(header);
|
||||
READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header);
|
||||
WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header);
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header);
|
||||
READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header);
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header);
|
||||
READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header);
|
||||
WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header);
|
||||
MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header);
|
||||
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header);
|
||||
READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header);
|
||||
|
||||
# All the rest
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
type Exception(header: ModbusTCP_TransportHeader) = record {
|
||||
code: uint8;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_Exception(header, this);
|
||||
};
|
||||
|
||||
# REQUEST FC=1
|
||||
type ReadCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=1
|
||||
type ReadCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=2
|
||||
type ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=2
|
||||
type ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=3
|
||||
type ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=3
|
||||
type ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=4
|
||||
type ReadInputRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=4
|
||||
type ReadInputRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=5
|
||||
type WriteSingleCoilRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=5
|
||||
type WriteSingleCoilResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=6
|
||||
type WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=6
|
||||
type WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=15
|
||||
type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 0x07B0);
|
||||
byte_count: uint8 &check(byte_count == (quantity + 7)/8);
|
||||
coils: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=15
|
||||
type WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16 &check(quantity <= 0x07B0);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=16
|
||||
type WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16;
|
||||
byte_count: uint8;
|
||||
# We specify registers buffer with quantity and byte_count so that the analyzer
|
||||
# will choke if something doesn't match right (correct devices should make it right).
|
||||
registers: uint16[quantity] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=16
|
||||
type WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
quantity: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for following message type.
|
||||
type FileRecordRequest = record {
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
file_num: uint16 &check(file_num > 0);
|
||||
record_num: uint16 &check(record_num <= 0x270F);
|
||||
record_len: uint16;
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=20
|
||||
type ReadFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordRequest[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the following message type.
|
||||
type FileRecordResponse = record {
|
||||
file_len: uint8 &check(file_len >= 0x07 && file_len <= 0xF5);
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
record_data: uint16[file_len/2] &length=file_len;
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=20
|
||||
type ReadFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordResponse[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the two following message types.
|
||||
type ReferenceWithData = record {
|
||||
ref_type: uint8;
|
||||
file_num: uint16;
|
||||
record_num: uint16;
|
||||
word_count: uint16;
|
||||
register_value: uint16[word_count];
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=21
|
||||
type WriteFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=21
|
||||
type WriteFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=22
|
||||
type MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=22
|
||||
type MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=23
|
||||
type ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
read_start_address: uint16;
|
||||
read_quantity: uint16 &check(read_quantity <= 125);
|
||||
write_start_address: uint16;
|
||||
write_quantity: uint16 &check(write_quantity <= 100);
|
||||
write_byte_count: uint8;
|
||||
write_register_values: uint16[write_quantity] &length=write_byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=23
|
||||
type ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
registers: uint16[byte_count/2] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=24
|
||||
type ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
} &let{
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=24
|
||||
type ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint16 &check(byte_count <= 62);
|
||||
fifo_count: uint16 &check(fifo_count <= 31);
|
||||
register_data: uint16[fifo_count] &length=fifo_count*2;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueResponse(header, this);
|
||||
} &byteorder=bigendian;
|
32
src/analyzer/protocol/modbus/modbus.pac
Normal file
32
src/analyzer/protocol/modbus/modbus.pac
Normal file
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# The development of Bro's Modbus analyzer has been made possible thanks to
|
||||
# the support of the Ministry of Security and Justice of the Kingdom of the
|
||||
# Netherlands within the projects of Hermes, Castor and Midas.
|
||||
#
|
||||
# Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||
# http://www.simplymodbus.ca/faq.htm
|
||||
|
||||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
%extern{
|
||||
#include "events.bif.h"
|
||||
%}
|
||||
|
||||
analyzer ModbusTCP withcontext {
|
||||
connection: ModbusTCP_Conn;
|
||||
flow: ModbusTCP_Flow;
|
||||
};
|
||||
|
||||
connection ModbusTCP_Conn(bro_analyzer: BroAnalyzer) {
|
||||
upflow = ModbusTCP_Flow(true);
|
||||
downflow = ModbusTCP_Flow(false);
|
||||
};
|
||||
|
||||
%include modbus-protocol.pac
|
||||
|
||||
flow ModbusTCP_Flow(is_orig: bool) {
|
||||
flowunit = ModbusTCP_PDU(is_orig) withcontext (connection, this);
|
||||
}
|
||||
|
||||
%include modbus-analyzer.pac
|
Loading…
Add table
Add a link
Reference in a new issue