├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── scripts ├── __load__.zeek └── main.zeek ├── tests ├── .gitignore ├── analyzer │ └── basic.zeek ├── baseline │ └── analyzer.basic │ │ ├── conn.log │ │ ├── modbus.log │ │ ├── modbus_detailed.log │ │ ├── modbus_mask_write_register.log │ │ └── modbus_read_write_multiple_registers.log ├── btest.cfg ├── files │ └── random.seed ├── scripts │ ├── diff-remove-timestamps │ └── get-zeek-env └── traces │ └── modbus_example.pcap └── zkg.meta /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Battelle Energy Alliance, LLC 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | This project contains code from Idaho National Laboratory's ICSNPP Project 2 | Github URL: https://github.com/cisagov/ICSNPP 3 | Licensed under BSD 3-Part License. 4 | 5 | 6 | © 2023 Battelle Energy Alliance, LLC 7 | ALL RIGHTS RESERVED 8 | 9 | Prepared by Battelle Energy Alliance, LLC 10 | Under Contract No. DE-AC07-05ID14517 11 | With the U. S. Department of Energy 12 | 13 | NOTICE: This computer software was prepared by Battelle Energy 14 | Alliance, LLC, hereinafter the Contractor, under Contract 15 | No. AC07-05ID14517 with the United States (U. S.) Department of 16 | Energy (DOE). The Government is granted for itself and others acting on 17 | its behalf a nonexclusive, paid-up, irrevocable worldwide license in this 18 | data to reproduce, prepare derivative works, and perform publicly and 19 | display publicly, by or on behalf of the Government. There is provision for 20 | the possible extension of the term of this license. Subsequent to that 21 | period or any extension granted, the Government is granted for itself and 22 | others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide 23 | license in this data to reproduce, prepare derivative works, distribute 24 | copies to the public, perform publicly and display publicly, and to permit 25 | others to do so. The specific term of the license can be identified by 26 | inquiry made to Contractor or DOE. NEITHER THE UNITED STATES NOR THE UNITED 27 | STATES DEPARTMENT OF ENERGY, NOR CONTRACTOR MAKES ANY WARRANTY, EXPRESS OR 28 | IMPLIED, OR ASSUMES ANY LIABILITY OR RESPONSIBILITY FOR THE USE, ACCURACY, 29 | COMPLETENESS, OR USEFULNESS OR ANY INFORMATION, APPARATUS, PRODUCT, OR 30 | PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY 31 | OWNED RIGHTS. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICSNPP-Modbus 2 | 3 | Industrial Control Systems Network Protocol Parsers (ICSNPP) - Modbus. 4 | 5 | ## Overview 6 | 7 | ICSNPP-Modbus is a Zeek package that extends the logging capabilities of Zeek's default Modbus protocol parser. 8 | 9 | Zeek's default Modbus parser logs Modbus traffic to modbus.log. This log file remains unchanged. This package extends Modbus logging capability by adding four new Modbus log files: 10 | * modbus_detailed.log 11 | * modbus_mask_write_register.log 12 | * modbus_read_write_multiple_registers.log 13 | * modbus_read_device_identification.log 14 | 15 | For additional information on these log files, see the *Logging Capabilities* section below. 16 | 17 | ## Installation 18 | 19 | ### Package Manager 20 | 21 | This script is available as a package for [Zeek Package Manger](https://docs.zeek.org/projects/package-manager/en/stable/index.html) 22 | 23 | ```bash 24 | zkg refresh 25 | zkg install icsnpp-modbus 26 | ``` 27 | 28 | If ZKG is configured to load packages (see @load packages in quickstart guide), this script will automatically be loaded and ready to go. 29 | [ZKG Quickstart Guide](https://docs.zeek.org/projects/package-manager/en/stable/quickstart.html) 30 | 31 | If users are not using site/local.zeek or another site installation of Zeek and want to run this script on a packet capture, they can add `icsnpp-modbus` to the command to run this script on the packet capture: 32 | 33 | ```bash 34 | git clone https://github.com/cisagov/icsnpp-modbus.git 35 | zeek -Cr icsnpp-modbus/tests/traces/modbus_example.pcap icsnpp-modbus 36 | ``` 37 | 38 | ### Manual Install 39 | 40 | To install this script manually, clone this repository and copy the contents of the scripts directory into `${ZEEK_INSTALLATION_DIR}/share/zeek/site/icsnpp-modbus`. 41 | 42 | ```bash 43 | git clone https://github.com/cisagov/icsnpp-modbus.git 44 | zeek_install_dir=$(dirname $(dirname `which zeek`)) 45 | cp -r icsnpp-modbus/scripts/ $zeek_install_dir/share/zeek/site/icsnpp-modbus 46 | ``` 47 | 48 | If using a site deployment, simply add echo `@load icsnpp-modbus` to the local.site file. 49 | 50 | If users are not using site/local.zeek or another site installation of Zeek and want to run this package on a packet capture, they can add `icsnpp-modbus` to the command to run this plugin's scripts on the packet capture: 51 | 52 | ```bash 53 | zeek -Cr icsnpp-modbus/tests/traces/modbus_example.pcap icsnpp-modbus 54 | ``` 55 | 56 | ## Logging Capabilities 57 | 58 | ### Detailed Modbus Field Log (modbus_detailed.log) 59 | 60 | #### Overview 61 | 62 | This log captures Modbus header and data fields and logs them to **modbus_detailed.log**. 63 | 64 | This log file contains the functions (read/write), count, addresses, and values of Modbus coils, discrete inputs, input registers, and holding registers. 65 | 66 | A "network_direction" meta-data field is also included in the log. The "network_direction" column specifies whether the message was a *request* or a *response* message. 67 | If an exception arises in the Modbus data, the exception code will be logged in the "values" field. 68 | 69 | #### Fields Captured 70 | 71 | | Field | Type | Description | 72 | | ----------------- |-----------|-------------------------------------------------------------------| 73 | | ts | time | Timestamp | 74 | | uid | string | Unique ID for this connection | 75 | | id | conn_id | Default Zeek connection info (IP addresses, ports) | 76 | | is_orig | bool | True if the packet is sent from the originator | 77 | | source_h | address | Source IP address (see *Source and Destination Fields*) | 78 | | source_p | port | Source port (see *Source and Destination Fields*) | 79 | | destination_h | address | Destination IP address (see *Source and Destination Fields*) | 80 | | destination_p | port | Destination port (see *Source and Destination Fields*) | 81 | | tid | count | Modbus transaction identifier | 82 | | uint | count | Modbus terminal unit identifier | 83 | | func | string | Modbus function code | 84 | | request_response | string | REQUEST or RESPONSE | 85 | | address | count | Starting address of value(s) field | 86 | | quantity | count | Number of addresses/values read or written to | 87 | | values | string | Value(s) of coils, discrete_inputs, or registers read/written to | 88 | 89 | 90 | ### Mask Write Register Log (modbus_mask_write_register.log) 91 | 92 | #### Overview 93 | 94 | This log captures the fields of the Modbus *mask_write_register* function (function code 0x16) and logs them to **modbus_mask_write_register.log**. 95 | 96 | #### Fields Captured 97 | 98 | | Field | Type | Description | 99 | | ----------------- |-----------|-------------------------------------------------------------------| 100 | | ts | time | Timestamp | 101 | | uid | string | Unique ID for this connection | 102 | | id | conn_id | Default Zeek connection info (IP addresses, ports) | 103 | | is_orig | bool | True if the packet is sent from the originator | 104 | | source_h | address | Source IP address (see *Source and Destination Fields*) | 105 | | source_p | port | Source port (see *Source and Destination Fields*) | 106 | | destination_h | address | Destination IP address (see *Source and Destination Fields*) | 107 | | destination_p | port | Destination port (see *Source and Destination Fields*) | 108 | | tid | count | Modbus transaction identifier | 109 | | uint | count | Modbus terminal unit identifier | 110 | | func | string | Modbus function code | 111 | | request_response | string | REQUEST or RESPONSE | 112 | | address | count | Address of the target register | 113 | | and_mask | count | Boolean 'and' mask to apply to the target register | 114 | | or_mask | count | Boolean 'or' mask to apply to the target register | 115 | 116 | ### Read Write Multiple Registers Log (modbus_read_write_multiple_registers.log) 117 | 118 | #### Overview 119 | 120 | This log captures the fields of the Modbus *read/write multiple registers* function (function code 0x17) and logs them to **modbus_read_write_multiple_registers.log**. 121 | 122 | #### Fields Captured 123 | 124 | | Field | Type | Description | 125 | | ----------------------|-----------|---------------------------------------------------------------| 126 | | ts | time | Timestamp | 127 | | uid | string | Unique ID for this connection | 128 | | id | conn_id | Default Zeek connection info (IP addresses, ports) | 129 | | is_orig | bool | True if the packet is sent from the originator | 130 | | source_h | address | Source IP address (see *Source and Destination Fields*) | 131 | | source_p | port | Source port (see *Source and Destination Fields*) | 132 | | destination_h | address | Destination IP address (see *Source and Destination Fields*) | 133 | | destination_p | port | Destination port (see *Source and Destination Fields*) | 134 | | tid | count | Modbus transaction identifier | 135 | | uint | count | Modbus terminal unit identifier | 136 | | func | string | Modbus function code | 137 | | request_response | string | REQUEST or RESPONSE | 138 | | write_start_address | count | Starting address of registers to be written | 139 | | write_registers | string | Register values written | 140 | | read_start_address | count | Starting address of the registers to read | 141 | | read_quantity | count | Number of registers to read in | 142 | | read_registers | string | Register values read | 143 | 144 | 145 | ### Read Device Identification Log (modbus_read_device_identification.log) 146 | 147 | #### Overview 148 | 149 | This log captures the fields of the Modbus *encapsulated interface transport* function (function code 0x2B) when the MEI type is set to 14 (0x0E) and logs them to **modbus_read_device_identification.log**. 150 | 151 | Note: this log is only produced in Zeek versions 6.1 and above 152 | 153 | #### Fields Captured 154 | 155 | | Field | Type | Description | 156 | | ----------------------|-----------|---------------------------------------------------------------| 157 | | ts | time | Timestamp | 158 | | uid | string | Unique ID for this connection | 159 | | id | conn_id | Default Zeek connection info (IP addresses, ports) | 160 | | is_orig | bool | True if the packet is sent from the originator | 161 | | source_h | address | Source IP address (see *Source and Destination Fields*) | 162 | | source_p | port | Source port (see *Source and Destination Fields*) | 163 | | destination_h | address | Destination IP address (see *Source and Destination Fields*) | 164 | | destination_p | port | Destination port (see *Source and Destination Fields*) | 165 | | tid | count | Modbus transaction identifier | 166 | | uint | count | Modbus terminal unit identifier | 167 | | func | string | Modbus function code | 168 | | request_response | string | REQUEST or RESPONSE | 169 | | mei_type | string | MEI Type - Always READ-DEVICE-IDENTIFICATION | 170 | | conformity_level_code | string | Conformity Level Code | 171 | | conformity_level | string | Conformity Level | 172 | | device_id_code | count | Device ID Code | 173 | | object_id_code | string | Object ID Code | 174 | | object_id | string | Object ID | 175 | | object_value | string | Object Value | 176 | 177 | ### Source and Destination Fields 178 | 179 | #### Overview 180 | 181 | Zeek's typical behavior is to focus on and log packets from the originator and not log packets from the responder. However, most ICS protocols contain useful information in the responses, so the ICSNPP parsers log both originator and responses packets. Zeek's default behavior, defined in its `id` struct, is to never switch these originator/responder roles which leads to inconsistencies and inaccuracies when looking at ICS traffic that logs responses. 182 | 183 | The default Zeek `id` struct contains the following logged fields: 184 | * id.orig_h (Original Originator/Source Host) 185 | * id.orig_p (Original Originator/Source Port) 186 | * id.resp_h (Original Responder/Destination Host) 187 | * id.resp_p (Original Responder/Destination Port) 188 | 189 | Additionally, the `is_orig` field is a boolean field that is set to T (True) when the id_orig fields are the true originators/source and F (False) when the id_resp fields are the true originators/source. 190 | 191 | To not break existing platforms that utilize the default `id` struct and `is_orig` field functionality, the ICSNPP team has added four new fields to each log file instead of changing Zeek's default behavior. These four new fields provide the accurate information regarding source and destination IP addresses and ports: 192 | * source_h (True Originator/Source Host) 193 | * source_p (True Originator/Source Port) 194 | * destination_h (True Responder/Destination Host) 195 | * destination_p (True Responder/Destination Port) 196 | 197 | The pseudocode below shows the relationship between the `id` struct, `is_orig` field, and the new `source` and `destination` fields. 198 | 199 | ``` 200 | if is_orig == True 201 | source_h == id.orig_h 202 | source_p == id.orig_p 203 | destination_h == id.resp_h 204 | destination_p == id.resp_p 205 | if is_orig == False 206 | source_h == id.resp_h 207 | source_p == id.resp_p 208 | destination_h == id.orig_h 209 | destination_p == id.orig_p 210 | ``` 211 | 212 | #### Example 213 | 214 | The table below shows an example of these fields in the log files. The first log in the table represents a Modbus request from 192.168.1.10 -> 192.168.1.200 and the second log represents a Modbus reply from 192.168.1.200 -> 192.168.1.10. As shown in the table below, the `id` structure lists both packets as having the same originator and responder, but the `source` and `destination` fields reflect the true source and destination of these packets. 215 | 216 | | id.orig_h | id.orig_p | id.resp_h | id.resp_p | is_orig | source_h | source_p | destination_h | destination_p | 217 | | ------------ | --------- |---------------|-----------|---------|---------------|----------|---------------|-------------- | 218 | | 192.168.1.10 | 47785 | 192.168.1.200 | 502 | T | 192.168.1.10 | 47785 | 192.168.1.200 | 502 | 219 | | 192.168.1.10 | 47785 | 192.168.1.200 | 502 | F | 192.168.1.200 | 502 | 192.168.1.10 | 47785 | 220 | 221 | ## Coverage 222 | 223 | See [Logging Capabilities](#logging-capabilities) for detailed information of the parser coverage. 224 | 225 | The Modbus protocol contains a few vendor and product specific functions. These vendor/product specific functions are not included in this parser. All coverage details in this section include information and statistics based on the basic/default Modbus protocol. 226 | 227 | ### General/Header Logging 228 | 229 | The general log file for Modbus (modbus.log) is produced by Zeek's default Modbus parser and is not modified by this parser extension. 230 | 231 | ### Detailed Logging 232 | 233 | Detailed logging for 11 Modbus functions are logged in the detailed log file (modbus_detailed.log), 1 Modbus function is logged in the mask write register log file (modbus_mask_write_register.log), 1 Modbus function is logged in the read write multiple registers log file (modbus_read_write_multiple_registers.log), and 1 Modbus function is logged in the read device identification log file (modbus_read_device_identification.log). The other, much less common, 5 Modbus functions do not contain detailed logging, therefore, ~74% (14/19) of the default Modbus functions contain detailed logging. 234 | 235 | ## ICSNPP Packages 236 | 237 | All ICSNPP Packages: 238 | * [ICSNPP](https://github.com/cisagov/icsnpp) 239 | 240 | Full ICS Protocol Parsers: 241 | * [BACnet](https://github.com/cisagov/icsnpp-bacnet) 242 | * Full Zeek protocol parser for BACnet (Building Control and Automation) 243 | * [BSAP](https://github.com/cisagov/icsnpp-bsap) 244 | * Full Zeek protocol parser for BSAP (Bristol Standard Asynchronous Protocol) over IP 245 | * Full Zeek protocol parser for BSAP Serial comm converted using serial tap device 246 | * [Ethercat](https://github.com/cisagov/icsnpp-ethercat) 247 | * Full Zeek protocol parser for Ethercat 248 | * [Ethernet/IP and CIP](https://github.com/cisagov/icsnpp-enip) 249 | * Full Zeek protocol parser for Ethernet/IP and CIP 250 | * [GE SRTP](https://github.com/cisagov/icsnpp-ge-srtp) 251 | * Full Zeek protocol parser for GE SRTP 252 | * [Genisys](https://github.com/cisagov/icsnpp-genisys) 253 | * Full Zeek protocol parser for Genisys 254 | * [OPCUA-Binary](https://github.com/cisagov/icsnpp-opcua-binary) 255 | * Full Zeek protocol parser for OPC UA (OPC Unified Architecture) - Binary 256 | * [S7Comm](https://github.com/cisagov/icsnpp-s7comm) 257 | * Full Zeek protocol parser for S7comm, S7comm-plus, and COTP 258 | * [Synchrophasor](https://github.com/cisagov/icsnpp-synchrophasor) 259 | * Full Zeek protocol parser for Synchrophasor Data Transfer for Power Systems (C37.118) 260 | * [Profinet IO CM](https://github.com/cisagov/icsnpp-profinet-io-cm) 261 | * Full Zeek protocol parser for Profinet I/O Context Manager 262 | 263 | Updates to Zeek ICS Protocol Parsers: 264 | * [DNP3](https://github.com/cisagov/icsnpp-dnp3) 265 | * DNP3 Zeek script extending logging capabilities of Zeek's default DNP3 protocol parser 266 | * [Modbus](https://github.com/cisagov/icsnpp-modbus) 267 | * Modbus Zeek script extending logging capabilities of Zeek's default Modbus protocol parser 268 | 269 | ### License 270 | 271 | Copyright 2023 Battelle Energy Alliance, LLC. Released under the terms of the 3-Clause BSD License (see [`LICENSE.txt`](./LICENSE.txt)). 272 | -------------------------------------------------------------------------------- /scripts/__load__.zeek: -------------------------------------------------------------------------------- 1 | @load ./main -------------------------------------------------------------------------------- /scripts/main.zeek: -------------------------------------------------------------------------------- 1 | ##! modbus_extended.zeek 2 | ##! 3 | ##! Binpac Modbus Protocol Analyzer - Contains the base script-layer functionality for processing events 4 | ##! emitted from the analyzer. (Utilizes Zeek's built-in Modbus parser) 5 | ##! 6 | ##! Authors: Brett Rasmussen & Stephen Kleinheider 7 | ##! Contact: brett.rasmussen@inl.gov & stephen.kleinheider@inl.gov 8 | ##! 9 | ##! Copyright (c) 2023 Battelle Energy Alliance, LLC. All rights reserved. 10 | 11 | @load base/protocols/modbus 12 | 13 | module Modbus_Extended; 14 | 15 | export { 16 | redef enum Log::ID += { LOG_DETAILED, 17 | LOG_MASK_WRITE_REGISTER, 18 | LOG_READ_WRITE_MULTIPLE_REGISTERS, 19 | LOG_READ_DEVICE_IDENTIFICATION }; 20 | 21 | ######################################################################################################################### 22 | ##################################### Modbus Detailed Log -> modbus_detailed.log ###################################### 23 | ######################################################################################################################### 24 | type Modbus_Detailed: record { 25 | ts : time &log; # Timestamp of event 26 | uid : string &log; # Zeek unique ID for connection 27 | id : conn_id &log; # Zeek connection struct (addresses and ports) 28 | is_orig : bool &log; # the message came from the originator/client or the responder/server 29 | source_h : addr &log; # Source IP Address 30 | source_p : port &log; # Source Port 31 | destination_h : addr &log; # Destination IP Address 32 | destination_p : port &log; # Destination Port 33 | tid : count &log &optional; # Modbus transaction id 34 | unit : count &log &optional; # Modbus terminal unit identifier 35 | func : string &log &optional; # Modbus Function 36 | request_response : string &log &optional; # REQUEST or RESPONSE 37 | address : count &log &optional; # Starting address for value(s) field 38 | quantity : count &log &optional; # Number of addresses/values read or written to 39 | values : string &log &optional; # Coils, discrete_inputs, or registers read/written to 40 | }; 41 | global log_modbus_detailed: event(rec: Modbus_Detailed); 42 | 43 | ######################################################################################################################### 44 | ############################# Mask Write Register Log -> modbus_mask_write_register.log ############################### 45 | ######################################################################################################################### 46 | type Mask_Write_Register: record { 47 | ts : time &log; # Timestamp of event 48 | uid : string &log; # Zeek unique ID for connection 49 | id : conn_id &log; # Zeek connection struct (addresses and ports) 50 | is_orig : bool &log; # the message came from the originator/client or the responder/server 51 | source_h : addr &log; # Source IP Address 52 | source_p : port &log; # Source Port 53 | destination_h : addr &log; # Destination IP Address 54 | destination_p : port &log; # Destination Port 55 | tid : count &log &optional; # Modbus transaction id 56 | unit : count &log &optional; # Modbus terminal unit identifier 57 | func : string &log &optional; # Modbus Function 58 | request_response : string &log &optional; # REQUEST or RESPONSE 59 | address : count &log &optional; # Address of the target register 60 | and_mask : count &log &optional; # Boolean 'and' mask to apply to the target register 61 | or_mask : count &log &optional; # Boolean 'or' mask to apply to the target register 62 | }; 63 | global log_mask_write_register: event(rec: Mask_Write_Register); 64 | 65 | ######################################################################################################################### 66 | ################### Read Write Multiple Registers Log -> modbus_read_write_multiple_registers.log ##################### 67 | ######################################################################################################################### 68 | type Read_Write_Multiple_Registers: record { 69 | ts : time &log; # Timestamp of event 70 | uid : string &log; # Zeek unique ID for connection 71 | id : conn_id &log; # Zeek connection struct (addresses and ports) 72 | is_orig : bool &log; # the message came from the originator/client or the responder/server 73 | source_h : addr &log; # Source IP Address 74 | source_p : port &log; # Source Port 75 | destination_h : addr &log; # Destination IP Address 76 | destination_p : port &log; # Destination Port 77 | tid : count &log &optional; # Modbus transaction id 78 | unit : count &log &optional; # Modbus terminal unit identifier 79 | func : string &log &optional; # Modbus Function 80 | request_response : string &log &optional; # REQUEST or RESPONSE 81 | write_start_address : count &log &optional; # Starting address of the registers to write to 82 | write_registers : ModbusRegisters &log &optional; # Register values written 83 | read_start_address : count &log &optional; # Starting address of the registers to read 84 | read_quantity : count &log &optional; # Number of registers to read 85 | read_registers : ModbusRegisters &log &optional; # Register values read 86 | }; 87 | global log_read_write_multiple_registers: event(rec: Read_Write_Multiple_Registers); 88 | 89 | ######################################################################################################################### 90 | ####################### Read Device Identification Log -> modbus_read_device_identification.log ####################### 91 | ######################################################################################################################### 92 | type Read_Device_Identification: record { 93 | ts : time &log; # Timestamp of event 94 | uid : string &log; # Zeek unique ID for connection 95 | id : conn_id &log; # Zeek connection struct (addresses and ports) 96 | is_orig : bool &log; # the message came from the originator/client or the responder/server 97 | source_h : addr &log; # Source IP Address 98 | source_p : port &log; # Source Port 99 | destination_h : addr &log; # Destination IP Address 100 | destination_p : port &log; # Destination Port 101 | tid : count &log &optional; # Modbus transaction id 102 | unit : count &log &optional; # Modbus terminal unit identifier 103 | func : string &log &optional; # Modbus Function - 104 | request_response : string &log &optional; # REQUEST or RESPONSE 105 | mei_type : string &log &optional; # MEI Type - Always READ-DEVICE-IDENTIFICATION 106 | conformity_level_code : string &log &optional; # Conformity Level Code 107 | conformity_level : string &log &optional; # Conformity Level 108 | device_id_code : count &log &optional; # Device ID Code 109 | object_id_code : string &log &optional; # Object ID Code 110 | object_id : string &log &optional; # Object ID 111 | object_value : string &log &optional; # Object Value 112 | }; 113 | global log_read_device_identification: event(rec: Read_Device_Identification); 114 | } 115 | 116 | redef DPD::ignore_violations += { Analyzer::ANALYZER_MODBUS }; 117 | 118 | @if (Version::at_least("6.1.0")) 119 | const device_identification_conformity_level = { 120 | [0x01] = "Basic Identification (Stream)", 121 | [0x02] = "Regular Identification (Stream)", 122 | [0x04] = "Extended Identification (Stream)", 123 | [0x81] = "Basic Identification (Stream, Individual)", 124 | [0x82] = "Regular Identification (Stream, Individual)", 125 | [0x83] = "Extended Identification (Stream, Individual)" 126 | } &default=function(i: count):string { return "Unknown"; } &redef; 127 | 128 | const device_identification_object_id = { 129 | [0x00] = "VendorName", 130 | [0x01] = "ProductCode", 131 | [0x02] = "MajorMinorVersion", 132 | [0x03] = "VendorURL", 133 | [0x04] = "ProductName", 134 | [0x05] = "ModelName", 135 | [0x06] = "UserApplicationName" 136 | } &default=function(i: count):string { return "Unknown"; } &redef; 137 | 138 | const device_identification_read_object_id = { 139 | [0x00] = "Basic Device Identification", 140 | [0x01] = "Regular Device Identification", 141 | [0x02] = "Extended Device Identification", 142 | [0x03] = "Specific Device Identification" 143 | } &default=function(i: count):string { return "Unknown"; } &redef; 144 | @endif 145 | 146 | ############################################################################################################################# 147 | ####################################### Converts Coil Vector to List of Boolean Values ##################################### 148 | ############################################################################################################################# 149 | function coils_to_bools_string (coils: ModbusCoils): string { 150 | 151 | local first_iter = T; 152 | local ret_str = ""; 153 | 154 | for ([i] in coils) { 155 | if (first_iter){ 156 | ret_str = fmt("%s", coils[i]); 157 | first_iter = F; 158 | } 159 | else 160 | ret_str = fmt("%s,%s", ret_str, coils[i]); 161 | } 162 | 163 | return ret_str; 164 | } 165 | 166 | ############################################################################################################################# 167 | ###################################### Converts Register Vector to List of Count Values #################################### 168 | ############################################################################################################################# 169 | function registers_to_counts_string (registers: ModbusRegisters): string { 170 | 171 | local first_iter = T; 172 | local ret_str = ""; 173 | 174 | for ([i] in registers) { 175 | if (first_iter) { 176 | ret_str = fmt("%d", registers[i]); 177 | first_iter = F; 178 | } 179 | else 180 | ret_str = fmt("%s,%d", ret_str, registers[i]); 181 | } 182 | 183 | return ret_str; 184 | } 185 | ############################################################################################################################# 186 | ############################################## Test for Handled Modbus Functions ########################################### 187 | ############################################################################################################################# 188 | function handled_modbus_funct_list (cur_func_str : string): bool { 189 | if ((cur_func_str == "READ_COILS") || 190 | (cur_func_str == "READ_COILS_EXCEPTION") || 191 | (cur_func_str == "READ_DISCRETE_INPUTS") || 192 | (cur_func_str == "READ_DISCRETE_INPUTS_EXCEPTION") || 193 | (cur_func_str == "READ_HOLDING_REGISTERS") || 194 | (cur_func_str == "READ_HOLDING_REGISTERS_EXCEPTION") || 195 | (cur_func_str == "READ_INPUT_REGISTERS") || 196 | (cur_func_str == "READ_INPUT_REGISTERS_EXCEPTION") || 197 | (cur_func_str == "READ_FILE_RECORD") || 198 | (cur_func_str == "READ_FILE_RECORD_EXCEPTION") || 199 | (cur_func_str == "WRITE_SINGLE_COIL") || 200 | (cur_func_str == "WRITE_SINGLE_COIL_EXCEPTION") || 201 | (cur_func_str == "WRITE_SINGLE_REGISTER") || 202 | (cur_func_str == "WRITE_SINGLE_REGISTER_EXCEPTION") || 203 | (cur_func_str == "WRITE_MULTIPLE_COILS") || 204 | (cur_func_str == "WRITE_MULTIPLE_COILS_EXCEPTION") || 205 | (cur_func_str == "WRITE_MULTIPLE_REGISTERS") || 206 | (cur_func_str == "WRITE_MULTIPLE_REGISTERS_EXCEPTION") || 207 | (cur_func_str == "READ_WRITE_MULTIPLE_REGISTERS") || 208 | (cur_func_str == "READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION") || 209 | (cur_func_str == "MASK_WRITE_REGISTER") || 210 | (cur_func_str == "MASK_WRITE_REGISTER_EXCEPTION") || 211 | (cur_func_str == "WRITE_FILE_RECORD") || 212 | (cur_func_str == "WRITE_FILE_RECORD_EXCEPTION") || 213 | (cur_func_str == "READ_FIFO_QUEUE") || 214 | (cur_func_str == "READ_FIFO_QUEUE_EXCEPTION")) { 215 | return T; 216 | } 217 | @if (Version::at_least("6.1.0")) 218 | if ((cur_func_str == "DIAGNOSTICS") || 219 | (cur_func_str == "DIAGNOSTICS_EXCEPTION") || 220 | (cur_func_str == "ENCAP_INTERFACE_TRANSPORT") || 221 | (cur_func_str == "ENCAP_INTERFACE_TRANSPORT_EXCEPTION")) { 222 | return T; 223 | } 224 | @endif 225 | 226 | # This function does not yet have a separate message handler 227 | return F; 228 | } 229 | 230 | ############################################################################################################################# 231 | # Defines Log Streams for modbus_detailed.log, modbus_mask_write_register.log, and modbus_read_write_multiple_registers.log # 232 | ############################################################################################################################# 233 | event zeek_init() &priority=5 { 234 | Log::create_stream(Modbus_Extended::LOG_DETAILED, [$columns=Modbus_Detailed, 235 | $ev=log_modbus_detailed, 236 | $path="modbus_detailed"]); 237 | 238 | Log::create_stream(Modbus_Extended::LOG_MASK_WRITE_REGISTER, [$columns=Mask_Write_Register, 239 | $ev=log_mask_write_register, 240 | $path="modbus_mask_write_register"]); 241 | 242 | Log::create_stream(Modbus_Extended::LOG_READ_WRITE_MULTIPLE_REGISTERS, [$columns=Read_Write_Multiple_Registers, 243 | $ev=log_read_write_multiple_registers, 244 | $path="modbus_read_write_multiple_registers"]); 245 | 246 | Log::create_stream(Modbus_Extended::LOG_READ_DEVICE_IDENTIFICATION, [$columns=Read_Device_Identification, 247 | $ev=log_read_device_identification, 248 | $path="modbus_read_device_identification"]); 249 | } 250 | 251 | ############################################################################################################################# 252 | ################### Defines logging of modbus_read_discrete_inputs_request event -> modbus_detailed.log ################### 253 | ############################################################################################################################# 254 | event modbus_read_discrete_inputs_request(c: connection, 255 | headers: ModbusHeaders, 256 | start_address: count, 257 | quantity: count){ 258 | 259 | local read_discrete_inputs_request: Modbus_Detailed; 260 | 261 | read_discrete_inputs_request$ts = network_time(); 262 | read_discrete_inputs_request$uid = c$uid; 263 | read_discrete_inputs_request$id = c$id; 264 | 265 | read_discrete_inputs_request$is_orig = T; 266 | read_discrete_inputs_request$source_h = c$id$orig_h; 267 | read_discrete_inputs_request$source_p = c$id$orig_p; 268 | read_discrete_inputs_request$destination_h = c$id$resp_h; 269 | read_discrete_inputs_request$destination_p = c$id$resp_p; 270 | 271 | read_discrete_inputs_request$tid = headers$tid; 272 | read_discrete_inputs_request$unit = headers$uid; 273 | read_discrete_inputs_request$func = Modbus::function_codes[headers$function_code]; 274 | read_discrete_inputs_request$request_response = "REQUEST"; 275 | read_discrete_inputs_request$address = start_address; 276 | read_discrete_inputs_request$quantity = quantity; 277 | 278 | Log::write(LOG_DETAILED, read_discrete_inputs_request); 279 | } 280 | 281 | ############################################################################################################################# 282 | ################## Defines logging of modbus_read_discrete_inputs_response event -> modbus_detailed.log ################### 283 | ############################################################################################################################# 284 | event modbus_read_discrete_inputs_response(c: connection, 285 | headers: ModbusHeaders, 286 | coils: ModbusCoils){ 287 | 288 | local read_discrete_inputs_response: Modbus_Detailed; 289 | 290 | read_discrete_inputs_response$ts = network_time(); 291 | read_discrete_inputs_response$uid = c$uid; 292 | read_discrete_inputs_response$id = c$id; 293 | 294 | read_discrete_inputs_response$is_orig = F; 295 | read_discrete_inputs_response$source_h = c$id$resp_h; 296 | read_discrete_inputs_response$source_p = c$id$resp_p; 297 | read_discrete_inputs_response$destination_h = c$id$orig_h; 298 | read_discrete_inputs_response$destination_p = c$id$orig_p; 299 | 300 | read_discrete_inputs_response$tid = headers$tid; 301 | read_discrete_inputs_response$unit = headers$uid; 302 | read_discrete_inputs_response$func = Modbus::function_codes[headers$function_code]; 303 | read_discrete_inputs_response$request_response = "RESPONSE"; 304 | read_discrete_inputs_response$quantity = |coils|; 305 | read_discrete_inputs_response$values = coils_to_bools_string (coils); 306 | 307 | Log::write(LOG_DETAILED, read_discrete_inputs_response); 308 | } 309 | 310 | ############################################################################################################################# 311 | ######################## Defines logging of modbus_read_coils_request event -> modbus_detailed.log ######################## 312 | ############################################################################################################################# 313 | event modbus_read_coils_request(c: connection, 314 | headers: ModbusHeaders, 315 | start_address: count, 316 | quantity: count){ 317 | 318 | local read_coils_request: Modbus_Detailed; 319 | 320 | read_coils_request$ts = network_time(); 321 | read_coils_request$uid = c$uid; 322 | read_coils_request$id = c$id; 323 | 324 | read_coils_request$is_orig = T; 325 | read_coils_request$source_h = c$id$orig_h; 326 | read_coils_request$source_p = c$id$orig_p; 327 | read_coils_request$destination_h = c$id$resp_h; 328 | read_coils_request$destination_p = c$id$resp_p; 329 | 330 | read_coils_request$tid = headers$tid; 331 | read_coils_request$unit = headers$uid; 332 | read_coils_request$func = Modbus::function_codes[headers$function_code]; 333 | read_coils_request$request_response = "REQUEST"; 334 | read_coils_request$address = start_address; 335 | read_coils_request$quantity = quantity; 336 | 337 | Log::write(LOG_DETAILED, read_coils_request); 338 | } 339 | 340 | ############################################################################################################################# 341 | ####################### Defines logging of modbus_read_coils_response event -> modbus_detailed.log ######################## 342 | ############################################################################################################################# 343 | event modbus_read_coils_response(c: connection, 344 | headers: ModbusHeaders, 345 | coils: ModbusCoils){ 346 | 347 | local read_coils_response: Modbus_Detailed; 348 | 349 | read_coils_response$ts = network_time(); 350 | read_coils_response$uid = c$uid; 351 | read_coils_response$id = c$id; 352 | 353 | read_coils_response$is_orig = F; 354 | read_coils_response$source_h = c$id$resp_h; 355 | read_coils_response$source_p = c$id$resp_p; 356 | read_coils_response$destination_h = c$id$orig_h; 357 | read_coils_response$destination_p = c$id$orig_p; 358 | 359 | read_coils_response$tid = headers$tid; 360 | read_coils_response$unit = headers$uid; 361 | read_coils_response$func = Modbus::function_codes[headers$function_code]; 362 | read_coils_response$request_response = "RESPONSE"; 363 | read_coils_response$quantity = |coils|; 364 | read_coils_response$values = coils_to_bools_string (coils); 365 | 366 | Log::write(LOG_DETAILED, read_coils_response); 367 | } 368 | 369 | ############################################################################################################################# 370 | ################## Defines logging of modbus_read_input_registers_request event -> modbus_detailed.log #################### 371 | ############################################################################################################################# 372 | event modbus_read_input_registers_request(c: connection, 373 | headers: ModbusHeaders, 374 | start_address: count, 375 | quantity: count) { 376 | 377 | local read_input_request: Modbus_Detailed; 378 | 379 | read_input_request$ts = network_time(); 380 | read_input_request$uid = c$uid; 381 | read_input_request$id = c$id; 382 | 383 | read_input_request$is_orig = T; 384 | read_input_request$source_h = c$id$orig_h; 385 | read_input_request$source_p = c$id$orig_p; 386 | read_input_request$destination_h = c$id$resp_h; 387 | read_input_request$destination_p = c$id$resp_p; 388 | 389 | read_input_request$tid = headers$tid; 390 | read_input_request$unit = headers$uid; 391 | read_input_request$func = Modbus::function_codes[headers$function_code]; 392 | read_input_request$request_response = "REQUEST"; 393 | read_input_request$address = start_address; 394 | read_input_request$quantity = quantity; 395 | 396 | Log::write(LOG_DETAILED, read_input_request); 397 | } 398 | 399 | ############################################################################################################################# 400 | ################## Defines logging of modbus_read_input_registers_response event -> modbus_detailed.log ################### 401 | ############################################################################################################################# 402 | event modbus_read_input_registers_response(c: connection, 403 | headers: ModbusHeaders, 404 | registers: ModbusRegisters) { 405 | 406 | local read_input_response: Modbus_Detailed; 407 | 408 | read_input_response$ts = network_time(); 409 | read_input_response$uid = c$uid; 410 | read_input_response$id = c$id; 411 | 412 | read_input_response$is_orig = F; 413 | read_input_response$source_h = c$id$resp_h; 414 | read_input_response$source_p = c$id$resp_p; 415 | read_input_response$destination_h = c$id$orig_h; 416 | read_input_response$destination_p = c$id$orig_p; 417 | 418 | read_input_response$tid = headers$tid; 419 | read_input_response$unit = headers$uid; 420 | read_input_response$func = Modbus::function_codes[headers$function_code]; 421 | read_input_response$request_response = "RESPONSE"; 422 | read_input_response$quantity = |registers|; 423 | read_input_response$values = registers_to_counts_string (registers); 424 | 425 | Log::write(LOG_DETAILED, read_input_response); 426 | } 427 | 428 | ############################################################################################################################# 429 | ################## Defines logging of modbus_read_holding_registers_request event -> modbus_detailed.log ################## 430 | ############################################################################################################################# 431 | event modbus_read_holding_registers_request(c: connection, 432 | headers: ModbusHeaders, 433 | start_address: count, 434 | quantity: count) { 435 | 436 | local read_holding_request: Modbus_Detailed; 437 | read_holding_request$ts = network_time(); 438 | read_holding_request$uid = c$uid; 439 | read_holding_request$id = c$id; 440 | 441 | read_holding_request$is_orig = T; 442 | read_holding_request$source_h = c$id$orig_h; 443 | read_holding_request$source_p = c$id$orig_p; 444 | read_holding_request$destination_h = c$id$resp_h; 445 | read_holding_request$destination_p = c$id$resp_p; 446 | 447 | read_holding_request$tid = headers$tid; 448 | read_holding_request$unit = headers$uid; 449 | read_holding_request$func = Modbus::function_codes[headers$function_code]; 450 | read_holding_request$request_response = "REQUEST"; 451 | read_holding_request$address = start_address; 452 | read_holding_request$quantity = quantity; 453 | 454 | Log::write(LOG_DETAILED, read_holding_request); 455 | } 456 | 457 | ############################################################################################################################# 458 | ################# Defines logging of modbus_read_holding_registers_response event -> modbus_detailed.log ################## 459 | ############################################################################################################################# 460 | event modbus_read_holding_registers_response(c: connection, 461 | headers: ModbusHeaders, 462 | registers: ModbusRegisters) { 463 | 464 | local read_holding_reg_response: Modbus_Detailed; 465 | 466 | read_holding_reg_response$ts = network_time(); 467 | read_holding_reg_response$uid = c$uid; 468 | read_holding_reg_response$id = c$id; 469 | 470 | read_holding_reg_response$is_orig = F; 471 | read_holding_reg_response$source_h = c$id$resp_h; 472 | read_holding_reg_response$source_p = c$id$resp_p; 473 | read_holding_reg_response$destination_h = c$id$orig_h; 474 | read_holding_reg_response$destination_p = c$id$orig_p; 475 | 476 | read_holding_reg_response$tid = headers$tid; 477 | read_holding_reg_response$unit = headers$uid; 478 | read_holding_reg_response$func = Modbus::function_codes[headers$function_code]; 479 | read_holding_reg_response$request_response = "RESPONSE"; 480 | read_holding_reg_response$quantity = |registers|; 481 | read_holding_reg_response$values = registers_to_counts_string (registers);; 482 | 483 | Log::write(LOG_DETAILED, read_holding_reg_response); 484 | } 485 | 486 | ############################################################################################################################# 487 | ##################### Defines logging of modbus_read_fifo_queue_request event -> modbus_detailed.log ###################### 488 | ############################################################################################################################# 489 | event modbus_read_fifo_queue_request(c: connection, 490 | headers: ModbusHeaders, 491 | start_address: count) { 492 | 493 | local read_fifo_queue_request: Modbus_Detailed; 494 | 495 | read_fifo_queue_request$ts = network_time(); 496 | read_fifo_queue_request$uid = c$uid; 497 | read_fifo_queue_request$id = c$id; 498 | 499 | read_fifo_queue_request$is_orig = T; 500 | read_fifo_queue_request$source_h = c$id$orig_h; 501 | read_fifo_queue_request$source_p = c$id$orig_p; 502 | read_fifo_queue_request$destination_h = c$id$resp_h; 503 | read_fifo_queue_request$destination_p = c$id$resp_p; 504 | 505 | read_fifo_queue_request$tid = headers$tid; 506 | read_fifo_queue_request$unit = headers$uid; 507 | read_fifo_queue_request$func = Modbus::function_codes[headers$function_code]; 508 | read_fifo_queue_request$request_response = "REQUEST"; 509 | read_fifo_queue_request$address = start_address; 510 | 511 | Log::write(LOG_DETAILED, read_fifo_queue_request); 512 | } 513 | 514 | ############################################################################################################################# 515 | ##################### Defines logging of modbus_read_fifo_queue_response event -> modbus_detailed.log ##################### 516 | ############################################################################################################################# 517 | event modbus_read_fifo_queue_response(c: connection, 518 | headers: ModbusHeaders, 519 | fifos: ModbusRegisters) { 520 | 521 | local read_fifo_queue_response: Modbus_Detailed; 522 | 523 | read_fifo_queue_response$ts = network_time(); 524 | read_fifo_queue_response$uid = c$uid; 525 | read_fifo_queue_response$id = c$id; 526 | 527 | read_fifo_queue_response$is_orig = F; 528 | read_fifo_queue_response$source_h = c$id$resp_h; 529 | read_fifo_queue_response$source_p = c$id$resp_p; 530 | read_fifo_queue_response$destination_h = c$id$orig_h; 531 | read_fifo_queue_response$destination_p = c$id$orig_p; 532 | 533 | read_fifo_queue_response$tid = headers$tid; 534 | read_fifo_queue_response$unit = headers$uid; 535 | read_fifo_queue_response$func = Modbus::function_codes[headers$function_code]; 536 | read_fifo_queue_response$request_response = "RESPONSE"; 537 | read_fifo_queue_response$quantity = |fifos|; 538 | read_fifo_queue_response$values = registers_to_counts_string (fifos); 539 | 540 | Log::write(LOG_DETAILED, read_fifo_queue_response); 541 | } 542 | 543 | ############################################################################################################################# 544 | ##################### Defines logging of modbus_write_single_coil_request event -> modbus_detailed.log #################### 545 | ############################################################################################################################# 546 | event modbus_write_single_coil_request(c: connection, 547 | headers: ModbusHeaders, 548 | address: count, 549 | value: bool) { 550 | 551 | local write_single_coil_request: Modbus_Detailed; 552 | 553 | write_single_coil_request$ts = network_time(); 554 | write_single_coil_request$uid = c$uid; 555 | write_single_coil_request$id = c$id; 556 | 557 | write_single_coil_request$is_orig = T; 558 | write_single_coil_request$source_h = c$id$orig_h; 559 | write_single_coil_request$source_p = c$id$orig_p; 560 | write_single_coil_request$destination_h = c$id$resp_h; 561 | write_single_coil_request$destination_p = c$id$resp_p; 562 | 563 | write_single_coil_request$tid = headers$tid; 564 | write_single_coil_request$unit = headers$uid; 565 | write_single_coil_request$func = Modbus::function_codes[headers$function_code]; 566 | write_single_coil_request$request_response = "REQUEST"; 567 | write_single_coil_request$address = address; 568 | write_single_coil_request$quantity = 1; 569 | write_single_coil_request$values = fmt("%s", value); 570 | 571 | Log::write(LOG_DETAILED, write_single_coil_request); 572 | } 573 | 574 | ############################################################################################################################# 575 | #################### Defines logging of modbus_write_single_coil_response event -> modbus_detailed.log #################### 576 | ############################################################################################################################# 577 | event modbus_write_single_coil_response(c: connection, 578 | headers: ModbusHeaders, 579 | address: count, 580 | value: bool) { 581 | 582 | local write_single_coil_response: Modbus_Detailed; 583 | 584 | write_single_coil_response$ts = network_time(); 585 | write_single_coil_response$uid = c$uid; 586 | write_single_coil_response$id = c$id; 587 | 588 | write_single_coil_response$is_orig = F; 589 | write_single_coil_response$source_h = c$id$resp_h; 590 | write_single_coil_response$source_p = c$id$resp_p; 591 | write_single_coil_response$destination_h = c$id$orig_h; 592 | write_single_coil_response$destination_p = c$id$orig_p; 593 | 594 | write_single_coil_response$tid = headers$tid; 595 | write_single_coil_response$unit = headers$uid; 596 | write_single_coil_response$func = Modbus::function_codes[headers$function_code]; 597 | write_single_coil_response$request_response = "RESPONSE"; 598 | write_single_coil_response$address = address; 599 | write_single_coil_response$quantity = 1; 600 | write_single_coil_response$values = fmt("%s", value); 601 | 602 | Log::write(LOG_DETAILED, write_single_coil_response); 603 | } 604 | 605 | ############################################################################################################################# 606 | ################### Defines logging of modbus_write_single_register_request event -> modbus_detailed.log ################## 607 | ############################################################################################################################# 608 | event modbus_write_single_register_request(c: connection, 609 | headers: ModbusHeaders, 610 | address: count, 611 | value: count) { 612 | 613 | local write_single_register_request: Modbus_Detailed; 614 | 615 | write_single_register_request$ts = network_time(); 616 | write_single_register_request$uid = c$uid; 617 | write_single_register_request$id = c$id; 618 | 619 | write_single_register_request$is_orig = T; 620 | write_single_register_request$source_h = c$id$orig_h; 621 | write_single_register_request$source_p = c$id$orig_p; 622 | write_single_register_request$destination_h = c$id$resp_h; 623 | write_single_register_request$destination_p = c$id$resp_p; 624 | 625 | write_single_register_request$tid = headers$tid; 626 | write_single_register_request$unit = headers$uid; 627 | write_single_register_request$func = Modbus::function_codes[headers$function_code]; 628 | write_single_register_request$request_response = "REQUEST"; 629 | write_single_register_request$address = address; 630 | write_single_register_request$quantity = 1; 631 | write_single_register_request$values = fmt("%d", value); 632 | 633 | Log::write(LOG_DETAILED, write_single_register_request); 634 | } 635 | 636 | ############################################################################################################################# 637 | ################## Defines logging of modbus_write_single_register_response event -> modbus_detailed.log ################## 638 | ############################################################################################################################# 639 | event modbus_write_single_register_response(c: connection, 640 | headers: ModbusHeaders, 641 | address: count, 642 | value: count) { 643 | 644 | local write_single_register_response: Modbus_Detailed; 645 | 646 | write_single_register_response$ts = network_time(); 647 | write_single_register_response$uid = c$uid; 648 | write_single_register_response$id = c$id; 649 | 650 | write_single_register_response$is_orig = F; 651 | write_single_register_response$source_h = c$id$resp_h; 652 | write_single_register_response$source_p = c$id$resp_p; 653 | write_single_register_response$destination_h = c$id$orig_h; 654 | write_single_register_response$destination_p = c$id$orig_p; 655 | 656 | write_single_register_response$tid = headers$tid; 657 | write_single_register_response$unit = headers$uid; 658 | write_single_register_response$func = Modbus::function_codes[headers$function_code]; 659 | write_single_register_response$request_response = "RESPONSE"; 660 | write_single_register_response$address = address; 661 | write_single_register_response$quantity = 1; 662 | write_single_register_response$values = fmt("%d", value); 663 | 664 | Log::write(LOG_DETAILED, write_single_register_response); 665 | } 666 | 667 | ############################################################################################################################# 668 | ################### Defines logging of modbus_write_multiple_coils_request event -> modbus_detailed.log ################### 669 | ############################################################################################################################# 670 | event modbus_write_multiple_coils_request(c: connection, 671 | headers: ModbusHeaders, 672 | start_address: count, 673 | coils: ModbusCoils) { 674 | 675 | local write_multiple_coils_request: Modbus_Detailed; 676 | 677 | write_multiple_coils_request$ts = network_time(); 678 | write_multiple_coils_request$uid = c$uid; 679 | write_multiple_coils_request$id = c$id; 680 | 681 | write_multiple_coils_request$is_orig = T; 682 | write_multiple_coils_request$source_h = c$id$orig_h; 683 | write_multiple_coils_request$source_p = c$id$orig_p; 684 | write_multiple_coils_request$destination_h = c$id$resp_h; 685 | write_multiple_coils_request$destination_p = c$id$resp_p; 686 | 687 | write_multiple_coils_request$tid = headers$tid; 688 | write_multiple_coils_request$unit = headers$uid; 689 | write_multiple_coils_request$func = Modbus::function_codes[headers$function_code]; 690 | write_multiple_coils_request$request_response = "REQUEST"; 691 | write_multiple_coils_request$address = start_address; 692 | write_multiple_coils_request$quantity = |coils|; 693 | write_multiple_coils_request$values = coils_to_bools_string (coils);; 694 | 695 | Log::write(LOG_DETAILED, write_multiple_coils_request); 696 | } 697 | 698 | ############################################################################################################################# 699 | ################## Defines logging of modbus_write_multiple_coils_response event -> modbus_detailed.log ################### 700 | ############################################################################################################################# 701 | event modbus_write_multiple_coils_response(c: connection, 702 | headers: ModbusHeaders, 703 | start_address: count, 704 | quantity: count) { 705 | 706 | local write_multiple_coils_response: Modbus_Detailed; 707 | 708 | write_multiple_coils_response$ts = network_time(); 709 | write_multiple_coils_response$uid = c$uid; 710 | write_multiple_coils_response$id = c$id; 711 | 712 | write_multiple_coils_response$is_orig = F; 713 | write_multiple_coils_response$source_h = c$id$resp_h; 714 | write_multiple_coils_response$source_p = c$id$resp_p; 715 | write_multiple_coils_response$destination_h = c$id$orig_h; 716 | write_multiple_coils_response$destination_p = c$id$orig_p; 717 | 718 | write_multiple_coils_response$tid = headers$tid; 719 | write_multiple_coils_response$unit = headers$uid; 720 | write_multiple_coils_response$func = Modbus::function_codes[headers$function_code]; 721 | write_multiple_coils_response$request_response = "RESPONSE"; 722 | write_multiple_coils_response$address = start_address; 723 | write_multiple_coils_response$quantity = quantity; 724 | 725 | Log::write(LOG_DETAILED, write_multiple_coils_response); 726 | } 727 | 728 | ############################################################################################################################# 729 | ################# Defines logging of modbus_write_multiple_registers_request event -> modbus_detailed.log ################# 730 | ############################################################################################################################# 731 | event modbus_write_multiple_registers_request(c: connection, 732 | headers: ModbusHeaders, 733 | start_address: count, 734 | registers: ModbusRegisters) { 735 | 736 | local write_multiple_registers_request: Modbus_Detailed; 737 | 738 | write_multiple_registers_request$ts = network_time(); 739 | write_multiple_registers_request$uid = c$uid; 740 | write_multiple_registers_request$id = c$id; 741 | 742 | write_multiple_registers_request$is_orig = T; 743 | write_multiple_registers_request$source_h = c$id$orig_h; 744 | write_multiple_registers_request$source_p = c$id$orig_p; 745 | write_multiple_registers_request$destination_h = c$id$resp_h; 746 | write_multiple_registers_request$destination_p = c$id$resp_p; 747 | 748 | write_multiple_registers_request$tid = headers$tid; 749 | write_multiple_registers_request$unit = headers$uid; 750 | write_multiple_registers_request$func = Modbus::function_codes[headers$function_code]; 751 | write_multiple_registers_request$request_response = "REQUEST"; 752 | write_multiple_registers_request$address = start_address; 753 | write_multiple_registers_request$quantity = |registers|; 754 | write_multiple_registers_request$values = registers_to_counts_string (registers);; 755 | 756 | Log::write(LOG_DETAILED, write_multiple_registers_request); 757 | } 758 | 759 | ############################################################################################################################# 760 | ################# Defines logging of modbus_write_multiple_registers_response event -> modbus_detailed.log ################ 761 | ############################################################################################################################# 762 | event modbus_write_multiple_registers_response(c: connection, 763 | headers: ModbusHeaders, 764 | start_address: count, 765 | quantity: count) { 766 | 767 | local write_multiple_registers_response: Modbus_Detailed; 768 | 769 | write_multiple_registers_response$ts = network_time(); 770 | write_multiple_registers_response$uid = c$uid; 771 | write_multiple_registers_response$id = c$id; 772 | 773 | write_multiple_registers_response$is_orig = F; 774 | write_multiple_registers_response$source_h = c$id$resp_h; 775 | write_multiple_registers_response$source_p = c$id$resp_p; 776 | write_multiple_registers_response$destination_h = c$id$orig_h; 777 | write_multiple_registers_response$destination_p = c$id$orig_p; 778 | 779 | write_multiple_registers_response$tid = headers$tid; 780 | write_multiple_registers_response$unit = headers$uid; 781 | write_multiple_registers_response$func = Modbus::function_codes[headers$function_code]; 782 | write_multiple_registers_response$request_response = "RESPONSE"; 783 | write_multiple_registers_response$address = start_address; 784 | write_multiple_registers_response$quantity = quantity; 785 | 786 | Log::write(LOG_DETAILED, write_multiple_registers_response); 787 | } 788 | 789 | ############################################################################################################################# 790 | #### Defines logging of modbus_read_write_multiple_registers_request event -> modbus_read_write_multiple_registers.log #### 791 | #### Defines logging of modbus_read_write_multiple_registers_request event -> modbus_detailed.log #### 792 | ############################################################################################################################# 793 | event modbus_read_write_multiple_registers_request(c: connection, 794 | headers: ModbusHeaders, 795 | read_start_address: count, 796 | read_quantity: count, 797 | write_start_address: count, 798 | write_registers: ModbusRegisters) { 799 | 800 | local read_write_multiple_registers_request: Read_Write_Multiple_Registers; 801 | local read_write_multiple_registers_request_detailed: Modbus_Detailed; 802 | 803 | read_write_multiple_registers_request$ts = network_time(); 804 | read_write_multiple_registers_request$uid = c$uid; 805 | read_write_multiple_registers_request$id = c$id; 806 | 807 | read_write_multiple_registers_request$is_orig = T; 808 | read_write_multiple_registers_request$source_h = c$id$orig_h; 809 | read_write_multiple_registers_request$source_p = c$id$orig_p; 810 | read_write_multiple_registers_request$destination_h = c$id$resp_h; 811 | read_write_multiple_registers_request$destination_p = c$id$resp_p; 812 | 813 | read_write_multiple_registers_request$tid = headers$tid; 814 | read_write_multiple_registers_request$unit = headers$uid; 815 | read_write_multiple_registers_request$func = Modbus::function_codes[headers$function_code]; 816 | read_write_multiple_registers_request$request_response = "REQUEST"; 817 | read_write_multiple_registers_request$read_start_address = read_start_address; 818 | read_write_multiple_registers_request$read_quantity = read_quantity; 819 | read_write_multiple_registers_request$write_start_address = write_start_address; 820 | read_write_multiple_registers_request$write_registers = write_registers; 821 | 822 | read_write_multiple_registers_request_detailed$ts = network_time(); 823 | read_write_multiple_registers_request_detailed$uid = c$uid; 824 | read_write_multiple_registers_request_detailed$id = c$id; 825 | 826 | read_write_multiple_registers_request_detailed$is_orig = T; 827 | read_write_multiple_registers_request_detailed$source_h = c$id$orig_h; 828 | read_write_multiple_registers_request_detailed$source_p = c$id$orig_p; 829 | read_write_multiple_registers_request_detailed$destination_h = c$id$resp_h; 830 | read_write_multiple_registers_request_detailed$destination_p = c$id$resp_p; 831 | 832 | read_write_multiple_registers_request_detailed$tid = headers$tid; 833 | read_write_multiple_registers_request_detailed$unit = headers$uid; 834 | read_write_multiple_registers_request_detailed$func = Modbus::function_codes[headers$function_code]; 835 | read_write_multiple_registers_request_detailed$request_response = "REQUEST"; 836 | read_write_multiple_registers_request_detailed$values = "see modbus_read_write_multiple_registers.log"; 837 | 838 | Log::write(LOG_READ_WRITE_MULTIPLE_REGISTERS, read_write_multiple_registers_request); 839 | Log::write(LOG_DETAILED, read_write_multiple_registers_request_detailed); 840 | } 841 | 842 | ############################################################################################################################# 843 | #### Defines logging of modbus_read_write_multiple_registers_response event -> modbus_read_write_multiple_registers.log ### 844 | #### Defines logging of modbus_read_write_multiple_registers_response event -> modbus_detailed.log ### 845 | ############################################################################################################################# 846 | event modbus_read_write_multiple_registers_response(c: connection, 847 | headers: ModbusHeaders, 848 | written_registers: ModbusRegisters) { 849 | 850 | local read_write_multiple_registers_response: Read_Write_Multiple_Registers; 851 | local read_write_multiple_registers_response_detailed: Modbus_Detailed; 852 | 853 | read_write_multiple_registers_response$ts = network_time(); 854 | read_write_multiple_registers_response$uid = c$uid; 855 | read_write_multiple_registers_response$id = c$id; 856 | 857 | read_write_multiple_registers_response$is_orig = F; 858 | read_write_multiple_registers_response$source_h = c$id$resp_h; 859 | read_write_multiple_registers_response$source_p = c$id$resp_p; 860 | read_write_multiple_registers_response$destination_h = c$id$orig_h; 861 | read_write_multiple_registers_response$destination_p = c$id$orig_p; 862 | 863 | read_write_multiple_registers_response$tid = headers$tid; 864 | read_write_multiple_registers_response$unit = headers$uid; 865 | read_write_multiple_registers_response$func = Modbus::function_codes[headers$function_code]; 866 | read_write_multiple_registers_response$request_response = "RESPONSE"; 867 | read_write_multiple_registers_response$read_registers = written_registers; 868 | 869 | read_write_multiple_registers_response_detailed$ts = network_time(); 870 | read_write_multiple_registers_response_detailed$uid = c$uid; 871 | read_write_multiple_registers_response_detailed$id = c$id; 872 | 873 | read_write_multiple_registers_response_detailed$is_orig = F; 874 | read_write_multiple_registers_response_detailed$source_h = c$id$resp_h; 875 | read_write_multiple_registers_response_detailed$source_p = c$id$resp_p; 876 | read_write_multiple_registers_response_detailed$destination_h = c$id$orig_h; 877 | read_write_multiple_registers_response_detailed$destination_p = c$id$orig_p; 878 | 879 | read_write_multiple_registers_response_detailed$tid = headers$tid; 880 | read_write_multiple_registers_response_detailed$unit = headers$uid; 881 | read_write_multiple_registers_response_detailed$func = Modbus::function_codes[headers$function_code]; 882 | read_write_multiple_registers_response_detailed$request_response = "RESPONSE"; 883 | read_write_multiple_registers_response_detailed$values = "see modbus_read_write_multiple_registers.log"; 884 | 885 | Log::write(LOG_READ_WRITE_MULTIPLE_REGISTERS, read_write_multiple_registers_response); 886 | Log::write(LOG_DETAILED, read_write_multiple_registers_response_detailed); 887 | } 888 | 889 | ############################################################################################################################# 890 | ##################### Defines logging of modbus_read_file_record_request event -> modbus_detailed.log ##################### 891 | ############################################################################################################################# 892 | @if (Version::at_least("6.1.0")) 893 | event modbus_read_file_record_request(c: connection, 894 | headers: ModbusHeaders, 895 | byte_count: count, 896 | refs: ModbusFileRecordRequests) 897 | @else 898 | event modbus_read_file_record_request(c: connection, 899 | headers: ModbusHeaders) 900 | @endif 901 | { 902 | 903 | local read_file_record_request: Modbus_Detailed; 904 | 905 | read_file_record_request$ts = network_time(); 906 | read_file_record_request$uid = c$uid; 907 | read_file_record_request$id = c$id; 908 | 909 | read_file_record_request$is_orig = T; 910 | read_file_record_request$source_h = c$id$orig_h; 911 | read_file_record_request$source_p = c$id$orig_p; 912 | read_file_record_request$destination_h = c$id$resp_h; 913 | read_file_record_request$destination_p = c$id$resp_p; 914 | 915 | read_file_record_request$tid = headers$tid; 916 | read_file_record_request$unit = headers$uid; 917 | read_file_record_request$func = Modbus::function_codes[headers$function_code]; 918 | read_file_record_request$request_response = "REQUEST"; 919 | 920 | Log::write(LOG_DETAILED, read_file_record_request); 921 | } 922 | 923 | ############################################################################################################################# 924 | #################### Defines logging of modbus_read_file_record_response event -> modbus_detailed.log ##################### 925 | ############################################################################################################################# 926 | @if (Version::at_least("6.1.0")) 927 | event modbus_read_file_record_response(c: connection, 928 | headers: ModbusHeaders, 929 | byte_count: count, 930 | refs: ModbusFileRecordResponses) 931 | @else 932 | event modbus_read_file_record_response(c: connection, 933 | headers: ModbusHeaders) 934 | @endif 935 | { 936 | 937 | local read_file_record_response: Modbus_Detailed; 938 | 939 | read_file_record_response$ts = network_time(); 940 | read_file_record_response$uid = c$uid; 941 | read_file_record_response$id = c$id; 942 | 943 | read_file_record_response$is_orig = F; 944 | read_file_record_response$source_h = c$id$resp_h; 945 | read_file_record_response$source_p = c$id$resp_p; 946 | read_file_record_response$destination_h = c$id$orig_h; 947 | read_file_record_response$destination_p = c$id$orig_p; 948 | 949 | read_file_record_response$tid = headers$tid; 950 | read_file_record_response$unit = headers$uid; 951 | read_file_record_response$func = Modbus::function_codes[headers$function_code]; 952 | read_file_record_response$request_response = "RESPONSE"; 953 | 954 | Log::write(LOG_DETAILED, read_file_record_response); 955 | } 956 | 957 | ############################################################################################################################# 958 | #################### Defines logging of modbus_write_file_record_request event -> modbus_detailed.log ##################### 959 | ############################################################################################################################# 960 | @if (Version::at_least("6.1.0")) 961 | event modbus_write_file_record_request(c: connection, 962 | headers: ModbusHeaders, 963 | byte_count: count, 964 | refs: ModbusFileReferences) 965 | @else 966 | event modbus_write_file_record_request(c: connection, 967 | headers: ModbusHeaders) 968 | @endif 969 | { 970 | 971 | local write_file_record_request: Modbus_Detailed; 972 | 973 | write_file_record_request$ts = network_time(); 974 | write_file_record_request$uid = c$uid; 975 | write_file_record_request$id = c$id; 976 | 977 | write_file_record_request$is_orig = T; 978 | write_file_record_request$source_h = c$id$orig_h; 979 | write_file_record_request$source_p = c$id$orig_p; 980 | write_file_record_request$destination_h = c$id$resp_h; 981 | write_file_record_request$destination_p = c$id$resp_p; 982 | 983 | write_file_record_request$tid = headers$tid; 984 | write_file_record_request$unit = headers$uid; 985 | write_file_record_request$func = Modbus::function_codes[headers$function_code]; 986 | write_file_record_request$request_response = "REQUEST"; 987 | 988 | Log::write(LOG_DETAILED, write_file_record_request); 989 | } 990 | 991 | ############################################################################################################################# 992 | ################### Defines logging of modbus_write_file_record_response event -> modbus_detailed.log ##################### 993 | ############################################################################################################################# 994 | @if (Version::at_least("6.1.0")) 995 | event modbus_write_file_record_response(c: connection, 996 | headers: ModbusHeaders, 997 | byte_count: count, 998 | refs: ModbusFileReferences) 999 | @else 1000 | event modbus_write_file_record_response(c: connection, 1001 | headers: ModbusHeaders) 1002 | @endif 1003 | { 1004 | 1005 | local write_file_record_response: Modbus_Detailed; 1006 | 1007 | write_file_record_response$ts = network_time(); 1008 | write_file_record_response$uid = c$uid; 1009 | write_file_record_response$id = c$id; 1010 | 1011 | write_file_record_response$is_orig = F; 1012 | write_file_record_response$source_h = c$id$resp_h; 1013 | write_file_record_response$source_p = c$id$resp_p; 1014 | write_file_record_response$destination_h = c$id$orig_h; 1015 | write_file_record_response$destination_p = c$id$orig_p; 1016 | 1017 | write_file_record_response$tid = headers$tid; 1018 | write_file_record_response$unit = headers$uid; 1019 | write_file_record_response$func = Modbus::function_codes[headers$function_code]; 1020 | write_file_record_response$request_response = "RESPONSE"; 1021 | 1022 | Log::write(LOG_DETAILED, write_file_record_response); 1023 | } 1024 | 1025 | ############################################################################################################################# 1026 | ################# Defines logging of modbus_mask_write_register_request event -> mask_write_register.log ################## 1027 | ################# Defines logging of modbus_mask_write_register_request event -> modbus_detailed.log ################## 1028 | ############################################################################################################################# 1029 | event modbus_mask_write_register_request(c: connection, 1030 | headers: ModbusHeaders, 1031 | address: count, 1032 | and_mask: count, 1033 | or_mask: count) { 1034 | 1035 | local mask_write_register_request: Mask_Write_Register; 1036 | local mask_write_register_request_detailed: Modbus_Detailed; 1037 | 1038 | mask_write_register_request$ts = network_time(); 1039 | mask_write_register_request$uid = c$uid; 1040 | mask_write_register_request$id = c$id; 1041 | 1042 | mask_write_register_request$is_orig = T; 1043 | mask_write_register_request$source_h = c$id$orig_h; 1044 | mask_write_register_request$source_p = c$id$orig_p; 1045 | mask_write_register_request$destination_h = c$id$resp_h; 1046 | mask_write_register_request$destination_p = c$id$resp_p; 1047 | 1048 | mask_write_register_request$tid = headers$tid; 1049 | mask_write_register_request$unit = headers$uid; 1050 | mask_write_register_request$func = Modbus::function_codes[headers$function_code]; 1051 | mask_write_register_request$request_response = "REQUEST"; 1052 | mask_write_register_request$address = address; 1053 | mask_write_register_request$and_mask = and_mask; 1054 | mask_write_register_request$or_mask = or_mask; 1055 | 1056 | mask_write_register_request_detailed$ts = network_time(); 1057 | mask_write_register_request_detailed$uid = c$uid; 1058 | mask_write_register_request_detailed$id = c$id; 1059 | 1060 | mask_write_register_request_detailed$is_orig = T; 1061 | mask_write_register_request_detailed$source_h = c$id$orig_h; 1062 | mask_write_register_request_detailed$source_p = c$id$orig_p; 1063 | mask_write_register_request_detailed$destination_h = c$id$resp_h; 1064 | mask_write_register_request_detailed$destination_p = c$id$resp_p; 1065 | 1066 | mask_write_register_request_detailed$tid = headers$tid; 1067 | mask_write_register_request_detailed$unit = headers$uid; 1068 | mask_write_register_request_detailed$func = Modbus::function_codes[headers$function_code]; 1069 | mask_write_register_request_detailed$request_response = "REQUEST"; 1070 | mask_write_register_request_detailed$values = "see modbus_mask_write_register.log"; 1071 | 1072 | Log::write(LOG_MASK_WRITE_REGISTER, mask_write_register_request); 1073 | Log::write(LOG_DETAILED, mask_write_register_request_detailed); 1074 | } 1075 | 1076 | ############################################################################################################################# 1077 | ################# Defines logging of modbus_mask_write_register_response event -> mask_write_register.log ################# 1078 | ################# Defines logging of modbus_mask_write_register_response event -> modbus_detailed.log ################# 1079 | ############################################################################################################################# 1080 | event modbus_mask_write_register_response(c: connection, 1081 | headers: ModbusHeaders, 1082 | address: count, 1083 | and_mask: count, 1084 | or_mask: count) { 1085 | 1086 | local mask_write_register_response: Mask_Write_Register; 1087 | local mask_write_register_response_detailed: Modbus_Detailed; 1088 | 1089 | mask_write_register_response$ts = network_time(); 1090 | mask_write_register_response$uid = c$uid; 1091 | mask_write_register_response$id = c$id; 1092 | 1093 | mask_write_register_response$is_orig = F; 1094 | mask_write_register_response$source_h = c$id$resp_h; 1095 | mask_write_register_response$source_p = c$id$resp_p; 1096 | mask_write_register_response$destination_h = c$id$orig_h; 1097 | mask_write_register_response$destination_p = c$id$orig_p; 1098 | 1099 | mask_write_register_response$tid = headers$tid; 1100 | mask_write_register_response$unit = headers$uid; 1101 | mask_write_register_response$func = Modbus::function_codes[headers$function_code]; 1102 | mask_write_register_response$request_response = "RESPONSE"; 1103 | mask_write_register_response$address = address; 1104 | mask_write_register_response$and_mask = and_mask; 1105 | mask_write_register_response$or_mask = or_mask; 1106 | 1107 | mask_write_register_response_detailed$ts = network_time(); 1108 | mask_write_register_response_detailed$uid = c$uid; 1109 | mask_write_register_response_detailed$id = c$id; 1110 | 1111 | mask_write_register_response_detailed$is_orig = F; 1112 | mask_write_register_response_detailed$source_h = c$id$resp_h; 1113 | mask_write_register_response_detailed$source_p = c$id$resp_p; 1114 | mask_write_register_response_detailed$destination_h = c$id$orig_h; 1115 | mask_write_register_response_detailed$destination_p = c$id$orig_p; 1116 | 1117 | mask_write_register_response_detailed$tid = headers$tid; 1118 | mask_write_register_response_detailed$unit = headers$uid; 1119 | mask_write_register_response_detailed$func = Modbus::function_codes[headers$function_code]; 1120 | mask_write_register_response_detailed$request_response = "RESPONSE"; 1121 | mask_write_register_response_detailed$values = "see modbus_mask_write_register.log"; 1122 | 1123 | Log::write(LOG_MASK_WRITE_REGISTER, mask_write_register_response); 1124 | Log::write(LOG_DETAILED, mask_write_register_response_detailed); 1125 | } 1126 | 1127 | 1128 | @if (Version::at_least("6.1.0")) 1129 | ############################################################################################################################# 1130 | ######################## Defines logging of modbus_diagnostics_request event -> modbus_detailed.log ####################### 1131 | ############################################################################################################################# 1132 | event modbus_diagnostics_request(c: connection, 1133 | headers: ModbusHeaders, 1134 | subfunction: count, 1135 | data: string) { 1136 | 1137 | local diagnostics_request: Modbus_Detailed; 1138 | 1139 | diagnostics_request$ts = network_time(); 1140 | diagnostics_request$uid = c$uid; 1141 | diagnostics_request$id = c$id; 1142 | 1143 | diagnostics_request$is_orig = T; 1144 | diagnostics_request$source_h = c$id$orig_h; 1145 | diagnostics_request$source_p = c$id$orig_p; 1146 | diagnostics_request$destination_h = c$id$resp_h; 1147 | diagnostics_request$destination_p = c$id$resp_p; 1148 | 1149 | diagnostics_request$tid = headers$tid; 1150 | diagnostics_request$unit = headers$uid; 1151 | diagnostics_request$func = Modbus::function_codes[headers$function_code]; 1152 | diagnostics_request$request_response = "REQUEST"; 1153 | diagnostics_request$address = subfunction; 1154 | diagnostics_request$values = data; 1155 | 1156 | Log::write(LOG_DETAILED, diagnostics_request); 1157 | } 1158 | 1159 | ############################################################################################################################# 1160 | ####################### Defines logging of modbus_diagnostics_response event -> modbus_detailed.log ####################### 1161 | ############################################################################################################################# 1162 | event modbus_diagnostics_response(c: connection, 1163 | headers: ModbusHeaders, 1164 | subfunction: count, 1165 | data: string) { 1166 | 1167 | local diagnostics_response: Modbus_Detailed; 1168 | 1169 | diagnostics_response$ts = network_time(); 1170 | diagnostics_response$uid = c$uid; 1171 | diagnostics_response$id = c$id; 1172 | 1173 | diagnostics_response$is_orig = F; 1174 | diagnostics_response$source_h = c$id$resp_h; 1175 | diagnostics_response$source_p = c$id$resp_p; 1176 | diagnostics_response$destination_h = c$id$orig_h; 1177 | diagnostics_response$destination_p = c$id$orig_p; 1178 | 1179 | diagnostics_response$tid = headers$tid; 1180 | diagnostics_response$unit = headers$uid; 1181 | diagnostics_response$func = Modbus::function_codes[headers$function_code]; 1182 | diagnostics_response$request_response = "RESPONSE"; 1183 | diagnostics_response$address = subfunction; 1184 | diagnostics_response$values = data; 1185 | 1186 | Log::write(LOG_DETAILED, diagnostics_response); 1187 | } 1188 | 1189 | ############################################################################################################################# 1190 | ####### Defines logging of modbus_read_device_identification_request event -> modbus_read_device_identification.log ####### 1191 | ############################################################################################################################# 1192 | function modbus_read_device_identification_request(c: connection, 1193 | headers: ModbusHeaders, 1194 | data: string) { 1195 | 1196 | 1197 | local read_device_identification_request: Read_Device_Identification; 1198 | 1199 | read_device_identification_request$ts = network_time(); 1200 | read_device_identification_request$uid = c$uid; 1201 | read_device_identification_request$id = c$id; 1202 | 1203 | read_device_identification_request$is_orig = T; 1204 | read_device_identification_request$source_h = c$id$orig_h; 1205 | read_device_identification_request$source_p = c$id$orig_p; 1206 | read_device_identification_request$destination_h = c$id$resp_h; 1207 | read_device_identification_request$destination_p = c$id$resp_p; 1208 | 1209 | read_device_identification_request$request_response = "REQUEST"; 1210 | read_device_identification_request$tid = headers$tid; 1211 | read_device_identification_request$unit = headers$uid; 1212 | read_device_identification_request$func = Modbus::function_codes[headers$function_code]; 1213 | read_device_identification_request$mei_type = "READ-DEVICE-IDENTIFICATION"; 1214 | read_device_identification_request$device_id_code = bytestring_to_count(data[0]); 1215 | read_device_identification_request$object_id_code = fmt("0x%02x",bytestring_to_count(data[1])); 1216 | read_device_identification_request$object_id = device_identification_read_object_id[bytestring_to_count(data[1])]; 1217 | 1218 | Log::write(LOG_READ_DEVICE_IDENTIFICATION, read_device_identification_request); 1219 | } 1220 | 1221 | 1222 | ############################################################################################################################# 1223 | ####### Defines logging of modbus_read_device_identification_response event -> modbus_read_device_identification.log ###### 1224 | ############################################################################################################################# 1225 | function modbus_read_device_identification_response(c: connection, 1226 | headers: ModbusHeaders, 1227 | data: string) { 1228 | 1229 | local read_device_identification_response: Read_Device_Identification; 1230 | 1231 | read_device_identification_response$ts = network_time(); 1232 | read_device_identification_response$uid = c$uid; 1233 | read_device_identification_response$id = c$id; 1234 | 1235 | read_device_identification_response$is_orig = F; 1236 | read_device_identification_response$source_h = c$id$resp_h; 1237 | read_device_identification_response$source_p = c$id$resp_p; 1238 | read_device_identification_response$destination_h = c$id$orig_h; 1239 | read_device_identification_response$destination_p = c$id$orig_p; 1240 | 1241 | read_device_identification_response$request_response = "RESPONSE"; 1242 | read_device_identification_response$tid = headers$tid; 1243 | read_device_identification_response$unit = headers$uid; 1244 | read_device_identification_response$func = Modbus::function_codes[headers$function_code]; 1245 | read_device_identification_response$mei_type = "READ-DEVICE-IDENTIFICATION"; 1246 | read_device_identification_response$device_id_code = bytestring_to_count(data[0]); 1247 | read_device_identification_response$conformity_level_code = fmt("0x%02x",bytestring_to_count(data[1])); 1248 | read_device_identification_response$conformity_level = device_identification_conformity_level[bytestring_to_count(data[1])]; 1249 | 1250 | local num_objects: count = bytestring_to_count(data[4]); 1251 | local object_index: count = 0; 1252 | local byte_index: count = 5; 1253 | local object_length: count; 1254 | 1255 | while(object_index < num_objects) 1256 | { 1257 | read_device_identification_response$object_id_code = fmt("0x%02x",bytestring_to_count(data[byte_index])); 1258 | read_device_identification_response$object_id = device_identification_object_id[bytestring_to_count(data[byte_index])]; 1259 | byte_index += 1; 1260 | object_length = bytestring_to_count(data[byte_index]); 1261 | byte_index += 1; 1262 | read_device_identification_response$object_value = fmt("%s", data[byte_index:byte_index+object_length]); 1263 | byte_index += object_length; 1264 | 1265 | object_index += 1; 1266 | Log::write(LOG_READ_DEVICE_IDENTIFICATION, read_device_identification_response); 1267 | } 1268 | } 1269 | 1270 | 1271 | ############################################################################################################################# 1272 | ################ Defines logging of modbus_encap_interface_transport_request event -> modbus_detailed.log ################# 1273 | ############################################################################################################################# 1274 | event modbus_encap_interface_transport_request(c: connection, 1275 | headers: ModbusHeaders, 1276 | mei_type: count, 1277 | data: string) { 1278 | 1279 | local encap_interface_transport_request: Modbus_Detailed; 1280 | 1281 | encap_interface_transport_request$ts = network_time(); 1282 | encap_interface_transport_request$uid = c$uid; 1283 | encap_interface_transport_request$id = c$id; 1284 | 1285 | encap_interface_transport_request$is_orig = T; 1286 | encap_interface_transport_request$source_h = c$id$orig_h; 1287 | encap_interface_transport_request$source_p = c$id$orig_p; 1288 | encap_interface_transport_request$destination_h = c$id$resp_h; 1289 | encap_interface_transport_request$destination_p = c$id$resp_p; 1290 | 1291 | encap_interface_transport_request$tid = headers$tid; 1292 | encap_interface_transport_request$unit = headers$uid; 1293 | encap_interface_transport_request$func = Modbus::function_codes[headers$function_code]; 1294 | encap_interface_transport_request$request_response = "REQUEST"; 1295 | 1296 | if (mei_type == 0x0D) 1297 | { 1298 | encap_interface_transport_request$values = "CANopen"; 1299 | } 1300 | else if (mei_type == 0x0E) 1301 | { 1302 | modbus_read_device_identification_request(c, headers, data); 1303 | encap_interface_transport_request$values = "see modbus_read_device_identification.log"; 1304 | } 1305 | else 1306 | { 1307 | encap_interface_transport_request$values = fmt("invalid encapsulated interface transport mei-(0x%02x)",mei_type); 1308 | } 1309 | 1310 | Log::write(LOG_DETAILED, encap_interface_transport_request); 1311 | } 1312 | 1313 | ############################################################################################################################# 1314 | ############### Defines logging of modbus_encap_interface_transport_response event -> modbus_detailed.log ################# 1315 | ############################################################################################################################# 1316 | event modbus_encap_interface_transport_response(c: connection, 1317 | headers: ModbusHeaders, 1318 | mei_type: count, 1319 | data: string) { 1320 | 1321 | local encap_interface_transport_response: Modbus_Detailed; 1322 | 1323 | encap_interface_transport_response$ts = network_time(); 1324 | encap_interface_transport_response$uid = c$uid; 1325 | encap_interface_transport_response$id = c$id; 1326 | 1327 | encap_interface_transport_response$is_orig = F; 1328 | encap_interface_transport_response$source_h = c$id$resp_h; 1329 | encap_interface_transport_response$source_p = c$id$resp_p; 1330 | encap_interface_transport_response$destination_h = c$id$orig_h; 1331 | encap_interface_transport_response$destination_p = c$id$orig_p; 1332 | 1333 | encap_interface_transport_response$tid = headers$tid; 1334 | encap_interface_transport_response$unit = headers$uid; 1335 | encap_interface_transport_response$func = Modbus::function_codes[headers$function_code]; 1336 | encap_interface_transport_response$request_response = "RESPONSE"; 1337 | 1338 | if (mei_type == 0x0D) 1339 | { 1340 | encap_interface_transport_response$values = "CANopen"; 1341 | } 1342 | else if (mei_type == 0x0E) 1343 | { 1344 | modbus_read_device_identification_response(c, headers, data); 1345 | encap_interface_transport_response$values = "see modbus_read_device_identification.log"; 1346 | } 1347 | else 1348 | { 1349 | encap_interface_transport_response$values = fmt("unknown encapsulated interface transport mei-(0x%02x)",mei_type); 1350 | } 1351 | 1352 | Log::write(LOG_DETAILED, encap_interface_transport_response); 1353 | } 1354 | @endif 1355 | 1356 | ############################################################################################################################# 1357 | ################################## Logs Modbus connection object to modbus_detailed.log ################################### 1358 | ############################################################################################################################# 1359 | event modbus_message(c: connection, 1360 | headers: ModbusHeaders, 1361 | is_orig: bool) &priority=-3 { 1362 | 1363 | local modbus_detailed_rec: Modbus_Detailed; 1364 | 1365 | if (( headers$function_code < 0x80)) { 1366 | 1367 | if (is_orig){ 1368 | modbus_detailed_rec$is_orig = T; 1369 | modbus_detailed_rec$source_h = c$id$orig_h; 1370 | modbus_detailed_rec$source_p = c$id$orig_p; 1371 | modbus_detailed_rec$destination_h = c$id$resp_h; 1372 | modbus_detailed_rec$destination_p = c$id$resp_p; 1373 | modbus_detailed_rec$request_response = "REQUEST"; 1374 | }else{ 1375 | modbus_detailed_rec$is_orig = F; 1376 | modbus_detailed_rec$source_h = c$id$resp_h; 1377 | modbus_detailed_rec$source_p = c$id$resp_p; 1378 | modbus_detailed_rec$destination_h = c$id$orig_h; 1379 | modbus_detailed_rec$destination_p = c$id$orig_p; 1380 | modbus_detailed_rec$request_response = "RESPONSE"; 1381 | } 1382 | 1383 | if ( !handled_modbus_funct_list (c$modbus$func)) { 1384 | modbus_detailed_rec$ts = network_time(); 1385 | modbus_detailed_rec$uid = c$uid; 1386 | modbus_detailed_rec$id = c$id; 1387 | modbus_detailed_rec$tid = headers$tid; 1388 | modbus_detailed_rec$unit = headers$uid; 1389 | modbus_detailed_rec$func = Modbus::function_codes[headers$function_code]; 1390 | 1391 | Log::write(LOG_DETAILED, modbus_detailed_rec); 1392 | } 1393 | } 1394 | } 1395 | 1396 | ############################################################################################################################# 1397 | ###################### Defines logging of modbus_exception event -> modbus.log & modbus_detailed.log ###################### 1398 | ############################################################################################################################# 1399 | event modbus_exception(c: connection, 1400 | headers: ModbusHeaders, 1401 | code: count) &priority=-4 { 1402 | 1403 | local exception_detailed: Modbus_Detailed; 1404 | 1405 | exception_detailed$ts = network_time(); 1406 | exception_detailed$uid = c$uid; 1407 | exception_detailed$id = c$id; 1408 | 1409 | exception_detailed$is_orig = F; 1410 | exception_detailed$source_h = c$id$resp_h; 1411 | exception_detailed$source_p = c$id$resp_p; 1412 | exception_detailed$destination_h = c$id$orig_h; 1413 | exception_detailed$destination_p = c$id$orig_p; 1414 | 1415 | exception_detailed$tid = headers$tid; 1416 | exception_detailed$unit = headers$uid; 1417 | exception_detailed$func = c$modbus$func; 1418 | exception_detailed$request_response = "RESPONSE"; 1419 | exception_detailed$values = c$modbus$exception; 1420 | 1421 | Log::write(LOG_DETAILED, exception_detailed); 1422 | } -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | .tmp 2 | .btest.failed.dat 3 | -------------------------------------------------------------------------------- /tests/analyzer/basic.zeek: -------------------------------------------------------------------------------- 1 | # @TEST-EXEC: zeek -C -r ${TRACES}/modbus_example.pcap $PACKAGE %INPUT 2 | # @TEST-EXEC: grep -v "$(printf '\t')REQ$(printf '\t')" modbus.log > modbus.tmp && mv modbus.tmp modbus.log 3 | # @TEST-EXEC: zeek-cut -n tid unit pdu_type < modbus.log > modbus.tmp && mv modbus.tmp modbus.log 4 | # @TEST-EXEC: sed 's/2\t-\t\\x00\\x00/-\t-\t-/g' modbus_detailed.log > modbus_detailed.tmp 5 | # @TEST-EXEC: sed 's/see modbus_read_device_identification.log/-/g' modbus_detailed.tmp > modbus_detailed.log 6 | # @TEST-EXEC: btest-diff modbus_detailed.log 7 | # @TEST-EXEC: btest-diff modbus.log 8 | # @TEST-EXEC: btest-diff modbus_mask_write_register.log 9 | # @TEST-EXEC: btest-diff modbus_read_write_multiple_registers.log 10 | # 11 | # @TEST-DOC: Test MODBUS analyzer extennsions with small trace. 12 | -------------------------------------------------------------------------------- /tests/baseline/analyzer.basic/conn.log: -------------------------------------------------------------------------------- 1 | ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 | #separator \x09 3 | #set_separator , 4 | #empty_field (empty) 5 | #unset_field - 6 | #path conn 7 | #open XXXX-XX-XX-XX-XX-XX 8 | #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents 9 | #types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] 10 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 tcp modbus 0.006324 292 393 SF T T 0 ShADadFf 50 2900 48 2897 - 11 | XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 192.168.1.6 37699 3.15.227.20 502 tcp modbus 0.100079 12 9 OTH T F 0 Dd 1 64 1 61 - 12 | #close XXXX-XX-XX-XX-XX-XX 13 | -------------------------------------------------------------------------------- /tests/baseline/analyzer.basic/modbus.log: -------------------------------------------------------------------------------- 1 | ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_COILS - 3 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_COILS - 4 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_DISCRETE_INPUTS - 5 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_DISCRETE_INPUTS - 6 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_HOLDING_REGISTERS - 7 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_HOLDING_REGISTERS - 8 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_INPUT_REGISTERS - 9 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_INPUT_REGISTERS - 10 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 WRITE_SINGLE_COIL - 11 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 WRITE_SINGLE_REGISTER - 12 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_EXCEPTION_STATUS - 13 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 DIAGNOSTICS - 14 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 WRITE_MULTIPLE_COILS - 15 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 WRITE_MULTIPLE_REGISTERS - 16 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 REPORT_SLAVE_ID - 17 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_FILE_RECORD - 18 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 WRITE_FILE_RECORD - 19 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 MASK_WRITE_REGISTER - 20 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 MASK_WRITE_REGISTER - 21 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_WRITE_MULTIPLE_REGISTERS - 22 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_WRITE_MULTIPLE_REGISTERS - 23 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 READ_FIFO_QUEUE - 24 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 ENCAP_INTERFACE_TRANSPORT - 25 | XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 192.168.1.6 37699 3.15.227.20 502 READ_HOLDING_REGISTERS_EXCEPTION ILLEGAL_DATA_ADDRESS 26 | -------------------------------------------------------------------------------- /tests/baseline/analyzer.basic/modbus_detailed.log: -------------------------------------------------------------------------------- 1 | ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 | #separator \x09 3 | #set_separator , 4 | #empty_field (empty) 5 | #unset_field - 6 | #path modbus_detailed 7 | #open XXXX-XX-XX-XX-XX-XX 8 | #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p is_orig source_h source_p destination_h destination_p tid unit func request_response address quantity values 9 | #types time string addr port addr port bool addr port addr port count count string string count count string 10 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 1 4 READ_COILS REQUEST 1 1 - 11 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 1 4 READ_COILS RESPONSE - 8 T,F,F,F,F,F,F,F 12 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 2 5 READ_COILS REQUEST 1 8 - 13 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 2 5 READ_COILS RESPONSE - 8 T,F,F,T,F,T,T,T 14 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 3 4 READ_DISCRETE_INPUTS REQUEST 1 1 - 15 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 3 4 READ_DISCRETE_INPUTS RESPONSE - 8 F,F,F,F,F,F,F,F 16 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 4 5 READ_DISCRETE_INPUTS REQUEST 1 8 - 17 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 4 5 READ_DISCRETE_INPUTS RESPONSE - 8 F,F,T,T,F,T,F,F 18 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 5 4 READ_HOLDING_REGISTERS REQUEST 1 1 - 19 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 5 4 READ_HOLDING_REGISTERS RESPONSE - 1 170 20 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 6 5 READ_HOLDING_REGISTERS REQUEST 1 8 - 21 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 6 5 READ_HOLDING_REGISTERS RESPONSE - 8 170,170,187,204,61316,58347,40843,58561 22 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 7 4 READ_INPUT_REGISTERS REQUEST 1 1 - 23 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 7 4 READ_INPUT_REGISTERS RESPONSE - 1 36395 24 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 8 5 READ_INPUT_REGISTERS REQUEST 1 8 - 25 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 8 5 READ_INPUT_REGISTERS RESPONSE - 8 36395,8059,39755,33361,7162,56207,619,44695 26 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 9 6 WRITE_SINGLE_COIL REQUEST 1 1 T 27 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 9 6 WRITE_SINGLE_COIL RESPONSE 1 1 T 28 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 10 7 WRITE_SINGLE_REGISTER REQUEST 1 1 43981 29 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 10 7 WRITE_SINGLE_REGISTER RESPONSE 1 1 43981 30 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 11 2 READ_EXCEPTION_STATUS REQUEST - - - 31 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 11 2 READ_EXCEPTION_STATUS RESPONSE - - - 32 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 12 2 DIAGNOSTICS REQUEST - - - 33 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 12 2 DIAGNOSTICS RESPONSE - - - 34 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 13 7 WRITE_MULTIPLE_COILS REQUEST 1 4 T,F,F,T 35 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 13 7 WRITE_MULTIPLE_COILS RESPONSE 1 4 - 36 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 14 7 WRITE_MULTIPLE_REGISTERS REQUEST 1 4 170,187,204,221 37 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 14 7 WRITE_MULTIPLE_REGISTERS RESPONSE 1 4 - 38 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 15 2 REPORT_SLAVE_ID REQUEST - - - 39 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 15 2 REPORT_SLAVE_ID RESPONSE - - - 40 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 16 8 READ_FILE_RECORD REQUEST - - - 41 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 16 8 READ_FILE_RECORD RESPONSE - - - 42 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 17 8 WRITE_FILE_RECORD REQUEST - - - 43 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 17 8 WRITE_FILE_RECORD RESPONSE - - - 44 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 18 4 MASK_WRITE_REGISTER REQUEST - - see modbus_mask_write_register.log 45 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 18 4 MASK_WRITE_REGISTER RESPONSE - - see modbus_mask_write_register.log 46 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 19 4 MASK_WRITE_REGISTER REQUEST - - see modbus_mask_write_register.log 47 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 19 4 MASK_WRITE_REGISTER RESPONSE - - see modbus_mask_write_register.log 48 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 20 6 READ_WRITE_MULTIPLE_REGISTERS REQUEST - - see modbus_read_write_multiple_registers.log 49 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 20 6 READ_WRITE_MULTIPLE_REGISTERS RESPONSE - - see modbus_read_write_multiple_registers.log 50 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 21 6 READ_WRITE_MULTIPLE_REGISTERS REQUEST - - see modbus_read_write_multiple_registers.log 51 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 21 6 READ_WRITE_MULTIPLE_REGISTERS RESPONSE - - see modbus_read_write_multiple_registers.log 52 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 22 10 READ_FIFO_QUEUE REQUEST - - - 53 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 22 10 READ_FIFO_QUEUE REQUEST 0 - - 54 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 22 10 READ_FIFO_QUEUE RESPONSE - - - 55 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 22 10 READ_FIFO_QUEUE RESPONSE - 0 (empty) 56 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 23 1 ENCAP_INTERFACE_TRANSPORT REQUEST - - - 57 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 23 1 ENCAP_INTERFACE_TRANSPORT RESPONSE - - - 58 | XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 192.168.1.6 37699 3.15.227.20 502 T 192.168.1.6 37699 3.15.227.20 502 1 0 READ_HOLDING_REGISTERS REQUEST 600 10 - 59 | XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 192.168.1.6 37699 3.15.227.20 502 F 3.15.227.20 502 192.168.1.6 37699 1 0 READ_HOLDING_REGISTERS_EXCEPTION RESPONSE - - ILLEGAL_DATA_ADDRESS 60 | #close XXXX-XX-XX-XX-XX-XX 61 | -------------------------------------------------------------------------------- /tests/baseline/analyzer.basic/modbus_mask_write_register.log: -------------------------------------------------------------------------------- 1 | ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 | #separator \x09 3 | #set_separator , 4 | #empty_field (empty) 5 | #unset_field - 6 | #path modbus_mask_write_register 7 | #open XXXX-XX-XX-XX-XX-XX 8 | #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p is_orig source_h source_p destination_h destination_p tid unit func request_response address and_mask or_mask 9 | #types time string addr port addr port bool addr port addr port count count string string count count count 10 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 18 4 MASK_WRITE_REGISTER REQUEST 10 57005 0 11 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 18 4 MASK_WRITE_REGISTER RESPONSE 10 57005 0 12 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 19 4 MASK_WRITE_REGISTER REQUEST 10 65535 57005 13 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 19 4 MASK_WRITE_REGISTER RESPONSE 10 65535 57005 14 | #close XXXX-XX-XX-XX-XX-XX 15 | -------------------------------------------------------------------------------- /tests/baseline/analyzer.basic/modbus_read_write_multiple_registers.log: -------------------------------------------------------------------------------- 1 | ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 | #separator \x09 3 | #set_separator , 4 | #empty_field (empty) 5 | #unset_field - 6 | #path modbus_read_write_multiple_registers 7 | #open XXXX-XX-XX-XX-XX-XX 8 | #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p is_orig source_h source_p destination_h destination_p tid unit func request_response write_start_address write_registers read_start_address read_quantity read_registers 9 | #types time string addr port addr port bool addr port addr port count count string string count vector[count] count count vector[count] 10 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 20 6 READ_WRITE_MULTIPLE_REGISTERS REQUEST 2 255 1 1 - 11 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 20 6 READ_WRITE_MULTIPLE_REGISTERS RESPONSE - - - - 170 12 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 T 127.0.0.1 47785 127.0.0.1 502 21 6 READ_WRITE_MULTIPLE_REGISTERS REQUEST 2 170,187,204 1 4 - 13 | XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 47785 127.0.0.1 502 F 127.0.0.1 502 127.0.0.1 47785 21 6 READ_WRITE_MULTIPLE_REGISTERS RESPONSE - - - - 170,170,187,204 14 | #close XXXX-XX-XX-XX-XX-XX 15 | -------------------------------------------------------------------------------- /tests/btest.cfg: -------------------------------------------------------------------------------- 1 | [btest] 2 | TestDirs = analyzer 3 | TmpDir = %(testbase)s/.tmp 4 | BaselineDir = %(testbase)s/baseline 5 | IgnoreDirs = .tmp 6 | IgnoreFiles = *.tmp *.swp #* *.trace .DS_Store 7 | 8 | [environment] 9 | ZEEKPATH=`%(testbase)s/scripts/get-zeek-env zeekpath` 10 | ZEEK_PLUGIN_PATH=`%(testbase)s/scripts/get-zeek-env zeek_plugin_path` 11 | ZEEK_SEED_FILE=%(testbase)s/files/random.seed 12 | PATH=`%(testbase)s/scripts/get-zeek-env path` 13 | PACKAGE=%(testbase)s/../scripts 14 | TZ=UTC 15 | LC_ALL=C 16 | TRACES=%(testbase)s/traces 17 | TMPDIR=%(testbase)s/.tmp 18 | TEST_DIFF_CANONIFIER=%(testbase)s/scripts/diff-remove-timestamps 19 | -------------------------------------------------------------------------------- /tests/files/random.seed: -------------------------------------------------------------------------------- 1 | 2983378351 2 | 1299727368 3 | 0 4 | 310447 5 | 0 6 | 1409073626 7 | 3975311262 8 | 34130240 9 | 1450515018 10 | 1466150520 11 | 1342286698 12 | 1193956778 13 | 2188527278 14 | 3361989254 15 | 3912865238 16 | 3596260151 17 | 517973768 18 | 1462428821 19 | 0 20 | 2278350848 21 | 32767 22 | -------------------------------------------------------------------------------- /tests/scripts/diff-remove-timestamps: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # Replace anything which looks like timestamps with XXXs (including the #start/end markers in logs). 4 | 5 | # Get us "modern" regexps with sed. 6 | if [ `uname` == "Linux" ]; then 7 | sed="sed -r" 8 | else 9 | sed="sed -E" 10 | fi 11 | 12 | $sed 's/(0\.000000)|([0-9]{9,10}\.[0-9]{2,8})/XXXXXXXXXX.XXXXXX/g' | \ 13 | $sed 's/^ *#(open|close).(19|20)..-..-..-..-..-..$/#\1 XXXX-XX-XX-XX-XX-XX/g' 14 | -------------------------------------------------------------------------------- /tests/scripts/get-zeek-env: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # BTest helper for getting values for Zeek-related environment variables. 4 | 5 | base=$(dirname $0) 6 | zeek_dist=$(cat ${base}/../../build/CMakeCache.txt 2>/dev/null | grep ZEEK_DIST | cut -d = -f 2) 7 | 8 | if [ -n "${zeek_dist}" ]; then 9 | if [ "$1" = "zeekpath" ]; then 10 | ${zeek_dist}/build/zeek-path-dev 11 | elif [ "$1" = "zeek_plugin_path" ]; then 12 | ( cd ${base}/../.. && pwd ) 13 | elif [ "$1" = "path" ]; then 14 | echo ${zeek_dist}/build/src:${zeek_dist}/aux/btest:${base}/:${zeek_dist}/aux/zeek-cut:$PATH 15 | else 16 | echo "usage: $(basename $0) " >&2 17 | exit 1 18 | fi 19 | else 20 | # Use Zeek installation for testing. In this case zeek-config must be in PATH. 21 | if ! which zeek-config >/dev/null 2>&1; then 22 | echo "zeek-config not found" >&2 23 | exit 1 24 | fi 25 | 26 | if [ "$1" = "zeekpath" ]; then 27 | zeek-config --zeekpath 28 | elif [ "$1" = "zeek_plugin_path" ]; then 29 | # Combine the local tree and the system-wide path. This allows 30 | # us to test on a local build or an installation made via zkg, 31 | # which squirrels away the build. --cpk 32 | echo "$(cd ${base}/../.. && pwd):$(zeek-config --plugin_dir)" 33 | elif [ "$1" = "path" ]; then 34 | echo ${PATH} 35 | else 36 | echo "usage: $(basename $0) " >&2 37 | exit 1 38 | fi 39 | fi 40 | -------------------------------------------------------------------------------- /tests/traces/modbus_example.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cisagov/icsnpp-modbus/a6f343047af451c2c08becd9326270460565b0d7/tests/traces/modbus_example.pcap -------------------------------------------------------------------------------- /zkg.meta: -------------------------------------------------------------------------------- 1 | [package] 2 | script_dir = scripts 3 | test_command = cd tests && btest -c btest.cfg 4 | description = Modbus script for detailed logging of the Modbus protocol - CISA ICSNPP 5 | credits = Brett Rasmussen & Stephen Kleinheider 6 | tags = modbus, Modbus, ics, ICS, CISA, INL, ICSNPP, icsnpp, zeek scripting, log writer, protocol analyzer 7 | depends = 8 | zkg >=2.10 9 | zeek >=4.2.0 --------------------------------------------------------------------------------