ModBus协议分析 – Suricata Keywords
  • 前言

最近在看 Suricata Docs 时遇到了ModBus协议,这是一种工控协议,我之前并没有接触过,看的比较痛苦。

尽管网络上针对ModBus协议的讲解文章非常多,但是每篇文章的观点好像都不太一样,内容更是难以理解。

因此,本文将从ModBus官方文档 ( https://modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf ) 入手,来看一看ModBus协议到底是什么, 以及Suricata中对应的关键词


什么是 ModBus 协议

ModBus协议是一种广泛应用于工业控制领域的消息传递协议,位于OSI参考模型的应用层,不同类型的总线(buses)或网络设备能够根据该协议建立通信。

这种通信是一种端到端的请求/应答通信,即一端发送请求,另一端负责接收,并根据请求解析结果做出应答。


ModBus 三种通信模式

ModBus默认有三种通信模式:

  1. 异步串行通信( ModBus串口通信 )

    在这种通信模式下,ModBus协议数据通过 EIA/TIA-232-E、EIA-422、EIA/TIA-485-A、fiber、radio等媒介传输。发送数据的一端被成为主站( Master ),接收数据的一端被称为从站( Slave )。

    只有主站能够初始化ModBus网络,与网络中的从站进行通信,从站与从站之间不能直接通信,也不能主动向主站发起请求。这么做的目的是为避免冲突,试想如果一个网络中存在两个权重相同的主站,那么肯定会产生数据混乱。

    主站可以以"单播"的方式与某一台从站通信,也可以以"广播"的方式与所有从站进行通信。如果主站以"单播"的方式通信,则从站会返回一个应答信息; 如果主站以"广播"的方式通信,则从站不会返回应答信息。

    异步串行通信模式又存在两种通信方式:

    • ModBus-RTU

    • ModBus-ASCII

    这两种通信方式的异同涉及到ModBus协议结构,我们放到后面再说。

  2. 以太网通信( ModBus TCP/IP通信 )

    在这种通信模式下,ModBus协议数据通过以太网以一种对等的方式进行传输,所有节点的权重都是相同的,不再存在"主站/从站"的关系; 任何设备都能进行初始化操作并向其他设备发起请求。

    这么做的目的是:数据链路层已经实现了冲突检测(CSMA/CD,载波侦听多路访问/冲突检测机制),因此上层协议不再需要担心数据混乱; ModBus网络中也允许同时进行多个数据传输过程。

    发送请求的设备被称为客户端( Client ), 接收请求的设备被称为服务端( Server ),客户端与服务端之间采用TCP 502端口建立通信。

  3. 高速令牌传递网络通信( ModBusPlus、MB+ 通信 )

    ModBusPlusModBus的一个扩展协议,该协议需要一个专门的协处理器来处理类似HDLC的高速令牌旋转。该扩展协议是Modicon产品专有的,比较少见。


ModBus协议模型

我们先来看一看ModBus协议模型。

ModBus协议定义了一个与物理层无关的协议数据单元(PDU、Protocol Data Unit); 为了能在不同类型的总线或者网络设备中传输数据,ModBus协议引入了一些附加字段,在数据通信时映射成应用数据单元(ADU、Application Data Unit)。

前文提到了三种常用的ModBus通信模式。实际上这三种通信模式在数据模型和功能调用上都是相同的,只是封装方式上存在不同; 也就是说,这三种ModBus通信模式的PDU都是完全相同的,而ADU中的附加字段会不一样。


ModBus-PDU

ModBus PDU一共由两部分组成,第一部分为"功能码(Function Code)",第二部分为"数据域(Data)"。

  • Function Code(功能代码)

    在主站访问从站时,功能代码(Function Code)告诉从站需要执行哪种操作; 当从站响应主站时,功能代码用于显示是正常响应(未发生错误)或异常响应(产生某种异常)。

    对于正常响应,从站会返回相同的原始功能代码; 对于异常响应,从站会返回与原始功能代码等效的异常功能代码(原始功能代码 + 0x80H).

    举个例子, 比如请求的功能代码为 0x08H

    • 如果是正常响应, 则响应的功能代码为 0x08H.
    • 如果是异常响应, 则响应的功能代码为 0x88H.

    ModBus共定义了255Function Code,范围为:Function Code 1 ... Function Code 255,注意 Function Code 0是无效的.

    官方PDF中将这些功能码分为四种类型。

    • Public Function Codes

      该类型的功能代码由两个部分组成:

      1. 第一部分是经过MODBUS-IDA.ORG社区验证过的功能代码,它们经过可用性的测试并留有公开记录,功能代码定义明确且唯一。

      2. 第二部分是未分配的功能代码,目前保留留作将来使用。

    • User-Defined Function Codes

      这部分功能代码是用户实现的,规范中并不包含这些功能代码,因此这部分功能代码不能确保是唯一的。

    • Reserved Function Codes

      这里的保留功能代码是指一些公司产品专用的功能代码,并不公开使用的。

    • Exception Function Codes

      这部分功能代码主要用于异常响应, 它们与请求数据包的功能代码成一一等效关系, 本身没有什么特殊含义.

      异常功能代码的值为请求数据包的功能代码 + 0x80H, 假设请求功能代码为0x08H, 如果从站在解析处理时触发了异常,那么响应功能代码为0x88H.

    除此之外,ModBus还支持一种"子功能代码(Sub-Function Codes)"; 这些子功能代码用于定义Function Code`的多个动作,就好像写代码时父函数中会定义子函数一样。

  • Data Field(数据区域)

    在发起请求时,不仅仅需要Function Code,还需要一些额外信息; 比如要访问寄存器的地址、要处理的项目个数、数据区域中的实际字节数等等,就像调用含参函数时需要传入参数一样。这部分额外信息就放在数据区域(Data Field)里。

    当然,数据区域也可能长度为"0",就像调用无参函数一样,函数本身就能实现某一功能。


ModBus-ADU

要想ModBus协议能够在不同总线和设备中传输,仅有PDU是不够的,最直观的就是没有通信地址,发送方不知道协议数据发给谁。对此,不同的ModBus通信模式会添加一些专有字段,将PDU封装成ADU

不同的ModBus通信模式添加的字段也不相同,先来看看ModBus异步串行通信模式


ModBus-异步串行通信模式-ADU

ModBus异步串行通信模式的ADU添加了Additional AddressError CheckSTARTEND共4个字段,其通用ADU组成结构如下:

  • Additional Address

    ModBus网络中, 每台设备都有一个唯一的地址, Master在访问Slave时就需要借助这个唯一地址.

    Slave接收到Master发来的请求时, 就会匹配Additional Address字段, 如果发现请求地址是自己,就会将自己的地址填充到响应数据包的Additional Address字段中,告诉主站是谁在做出响应. 如果请求地址不是自己, 就会丢弃该请求.

    Additional Address的地址范围为0-247;其中0为广播地址,Salve设备能够使用的是1-247.

  • Error Check

    差错校验是为了对发送的数据区域(Data Field)做校验, 防止协议数据在发送过程中因电磁干扰而导致数据出错.

  • START | END

    STARTEND用于标记ADU的开始与结尾. 例如MasterSlave连续发送了三个ADU,那么Slave就需要知道这段数据中从哪到哪是一个完整的ADU,从哪到哪是下一个完整的ADU; 然后才能解析并处理划分出来的ADU, 反之亦然.

前文有提到ModBus异步串行通信存在 ModBus-ASCII-异步串行通信ModBus-RTU-异步串行通信 两种方式。其实这两种通信方式在ADU中添加的字段属性并不一样,来看看区别在哪。

这部分内容可以出参考邓小俊前辈的[MODBUS协议详解]一文,写的非常清楚!下文为部分引用内容。


ModBus-RTU-异步串行通信方式

RTU(Remote Terminal Unit)这种通信方式中,ADU内部会设置一个定时器来实现多个ADU之间的划分.

假设1 Byte传输的时间为T, 那么当两个ADU之间的传输时间间隔大于3.5T时, 则接收设备就能分辨出这是不同的ADU.

在通信时, ADU中的每个字节(8bits)都由两个4bits的十六进制字符组成.

举个例子, 比如Master想要发送数据 0x5BHSlave. 那么传输的实际数据是二进制0101 1011.

此外, RTU通讯方式采用CRC(循环冗余校验码)的方式来实现错误检测.


ModBus-ASCII-异步串行通信方式

ASCII(AmericanStandard Code for Information Interchange)这种通信方式中,ADU会以填充ASCII字符的方式来实现多个ADU之间的划分.

接收设备会以冒号(:,0x3AH)作为ADU的开始;以回车换行符号(CRLF,0x0D0AH)作为ADU的结尾;以此来区分不同的ADU.

在通信时, ADU中的每个字节(8bits)都由两个ASCII字符组成.

举个例子, 比如Master想要发送数据 0x5BHSlave;

  1. 根据ASCII编码对照表, 字符"5"对应十进制53, 字符"B"对应十进制66;
  2. 十进制53对应二进制0011 0101,十进制66对应二进制0100 0010;
  3. 因此, 传输的实际数据是 0011 0101 0100 0010.

此外, ASCII通讯方式采用LRC(纵向冗余校验)的方式来实现错误检测.


ModBus-以太网通信-ADU

在这种通信方式中, ModBus没有添加像Additional AddressError Check 等字段; 取而代之的是一个MBAP Header报文头字段.

MBAP Header字段由 "事务处理标识符"、"协议标识符"、"数据长度"、"单元标识符" 四部分组成.

  • 事务处理标识符(Transaction Identifier)

    该字段占用2个字节,用于标识报文的序列号, 每次"请求/应答"通信后, 该字段值都会加1, 以区分不同的通信数据报文.

  • 协议标识符(Protocol Identifier)

    该字段用于标识协议类型, 一般情况下为00 00, 代表ModBus TCP协议.

  • 报文长度(Length)

    该字段表示接下来的数据长度, 单位为字节(Bytes).

  • 单元标识符号(Unit Identifier)

    该字段可以理解为ModBus TCP通信网络中的设备地址.

关于为什么这么修改, 孤情剑客前辈在[modbus通讯协议详解]一文中讲得很清楚, 这里引用如下:

  1. 取消了Error Check, 是因为TCP/IP是面向可靠连接的协议, 在数据链路层就已经实现了CRC-32校验; 在ModBus TCP通信时没必要再加上校验位.

  2. 取消了Additional Address, 是因为在ModBus TCP网络中,各个设备根据IP地址进行路由寻址, 不再需要Additional Address来标识每台设备了.

    但是如果ModBus TCP网络中还有异步串行通信模式的设备(比如ModBus RTUModBus ASCII),那么就需要网关来实现通信协议的转换.此时, ModBus可以通过添加的Unit Identifier字段来识别每一个串行通信设备.

  3. 添加了Length字段, 代表后面的字节总数. 用于应对TCP/IP的应用层分包传输.

  4. 添加了Transaction IdentifierProtocol Identifier字段, 这两个字段通常为0. 由Client生成, Server在会复制这两个字段的值到到响应数据包中.

在官方文档中有这么一个例子, 可以作为参考.

Request: 从单元标识符9中读取保持寄存器偏移4个地址的数据
Response: 返回5

Request: 00 00 00 00 00 06 09 03 04 00 01
Response: 00 00 00 00 00 05 03 02 00 05

在上面的例子中, 我提到了"保持寄存器", 这个寄存器实际上是ModBus数据模型中的一种数据类型. 接下来我们就来看一看什么是ModBus数据模型.


ModBus数据模型

ModBus网络中, 数据可以分为两大类型, 即"位变量"和"字变量", 每一种数据又根据读写方式的不同分为"只读"和"读写".

  • 线圈状态 : 线圈状态可以理解为开关量, 一个Bit对应一个信号的开关状态. 比如请求设备可以读取或修改响应设备某一个开关的状态(开启或者关闭).

  • 离散输入 : 离散输入同样可以理解位开关量, 但是请求设备只能读取开关量的值, 而不能修改. 比如请求设备可以读取响应设备的某一个开关是开启还是关闭的.

  • 输入寄存器 : 输入寄存器的单位是2 Bytes,寄存器中可以存在具体的数据量(一般用于存在实时数据), 可由请求设备来读取.

  • 保持寄存器 : 保持寄存器中同样存放具体的数据量, 其值可由请求设备读取或者修改. 比如保持寄存器中存放了设备时间信息, 那么请求设备就可以读取或者修改这个时间.

有了基本数据类型, 那么如何操作这些数据类型呢? 这离不开前文提到的Function Code(功能代码). 如果按照操作类型来分类常用的功能代码, 那么可以得到如下结果:

  1. 操作位变量

  2. 操作字变量

上面只记录了一些常见的功能代码, 还有大量不常用的功能代码或是私有功能代码没有提到, 感兴趣的师傅可以参考[Modbus_Application_Protocol_V1_1b.pdf] 一文,这里不再过多阐述.


一些实际案例

ICS-Security-Tools项目中存在一些工控协议的Pcap数据包, 我们以modbus_test_2.pcap数据包为例, 看一看其传输的实际案例


读取保持寄存器

从上面这个Pcap数据包截图可知: 在Wireshark中,ModBus/TCP字段是指ModBus TCP/IP MBAP Header; 而ModBus字段是指ModBus TCP/IP PDU.

ModBus/TCP字段信息可知, Transaction IdentifierProtocol Identifier字段值均为0,表明这是一个标准的ModBus TCP协议数据; Length字段值为6,表明之后还有6个字节的数据; Unit Identifier字段值为7, 表明Client尝试请求单元标识符为7Server设备.

ModBus字段信息可知, Client执行的功能代码为0x03H(读取保存寄存器). 我们打开官方文档, 阅读0x03H功能代码的定义, 了解执行该功能需要哪些参数.

从功能定义中我们得知: 0x03H功能代码需要两个参数,一个是起始地址,一个是读取的保存寄存器的个数, 对应到Pcap数据包中,也就是: 从地址偏移0x04H处开始,读取接下来2个保持寄存器中的值.

接下来来看一看响应数据包.

响应数据包中的功能代码为0x03H,说明这是一个正常的响应,没有抛出异常(抛出异常则功能代码应该为0x83H). 而Transaction IdentifierProtocol IdentifierServer从请求数据包中复制出来的,应当是一样的.

根据0x30H功能代码针对响应数据包的定义, 响应数据包中应有一个Byte Count字段,其值为读取寄存器个数的两倍; 还应包含寄存器的值.因此我们能读取如下结论.

  • Register Number 4 : 0x03E8H
  • Register Number 5 : 0x36C1H

Register Number 是从哪里来的呢? 保持寄存器的大小为无符号双字节整型,也就是16位, 刚好是0x01H.

请求读取的是偏移0x04H后两个寄存器的值, 也就是偏移3个寄存器后两个寄存器的值( 寄存器1的地址是0x00H,功能代码定义里有说明 ); 因此, 这里读取的也就是第四个寄存器和第五个寄存器的值了.


写入输入寄存器

再来看一个调用0x04H功能代码向输入寄存器写入数据的例子

和前一个例子相同, 先去看一下功能代码0x04H是如何定义的.

与功能代码0x03H的参数完全相同, 那么就很容易理解这里要做什么了: 从第1个寄存器开始,连续读取14个寄存器的值.

查看响应数据包, 发现响应数据包也的确返回了这些寄存器存储的数据.


PostScript1 : 异步串行通信的 Tips

在[Modbus协议深入讲解] 一文中有提到: 每个数据区块分别存在一个前缀,具体对应关系如下:

官方文档在附录中提到了这一个点, 举了R1MR2M两种系列产品的例子; 这种前缀划分仅用于ModBus异步串行通信中, 在ModBus TCP网络中不再使用.


PostScript2 : 子功能代码(Sub-FunctionCodes)

前文有提到过, 某些功能代码还支持子功能代码,这些子功能代码定义了不同的动作. 那么到底哪些功能代码携带子功能代码呢? 这就要提到一个特殊的功能代码: Diagnostic(0x08H)

Diagnostic 是一个Public Function Code, 其翻译为"诊断程序"; 顾名思义, 该功能代码提供了一系列的系统功能测试, 用于检查发送端和接收端之间的通信是否正常

要测试的系统功能肯定不止一个, 那么如何区分这些不同的动作呢? 此时就需要使用到子功能代码. 来看下该功能代码的PDU.

可见, 在Diagnostic功能代码中, 除了常规8 bitsFunction Code, 还添加了一个16 BitsSub-Function Code字段. 该字段会描述诊断程序的不同动作.

官方也给出了当前Diagnostic支持的各个子功能的代码和定义, 内容具体如下:

对ModBus感兴趣的师傅可以进一步去了解各个子功能代码的含义, 本文不再深入分析.


PostScript3 : 异常响应报文

之前一直提到如果Server无法处理请求,则会返回异常响应, 同时返回的Function Code会增加0x80H; 但并没有专门提过异常响应的报文结构.其实这个非常简单.

Client发起请求时, 肯定都希望Server正常执行功能代码, 返回正常响应. 那么在哪些情况下会发生异常呢?

  • Server由于通信错误没有收到Client发来的请求, 因此无法做出响应; Client最终会将该请求作为超时处理.

  • Server接收到了Client发来的请求,但是在解析处理时发现数据错误(比如LRC/CRC校验错误), 因此不会做出响应; Client最终会将该请求作为超时处理.

  • Server接收到了Client发来的请求, 且没有数据错误, 但是客户端请求的对象错误(比如请求了不存在的寄存器); 那么将返回异常响应, 告诉客户端异常原因.

可见, 在第三种情况下, Server需要返回异常响应. 那么让我们来看一看异常响应报文是怎么组成的.

  • Function Code(功能代码)

    如果为异常响应数据包, 则响应数据包中的Function Code为请求数据包中的Function Code + 0x80H

  • Exception Code(异常代码)

    异常代码一共有9种,具体如下:

    这块内容可以参考PDF文末, 本身没有什么特别需要关注的点.


Suricata KeyWord

那么Suricata(5.0.6)能够命中ModBus协议的哪些字段呢?

Suricata定义了以下三个KeyWords:

  • Function
  • Access
  • Unit

Function

  1. 该关键字可用于匹配Function CodeSub-Function Code字段.
  2. 该关键字可用于匹配三种类型的Function Code.
    • 使用public属性命中Public Function Code类型
    • 使用user属性命中User-Defined Function Code类型
    • 使用reserved属性命中Reserved Function Code类型
  3. 该关键字可用命中某一个Public Function Code是否被分配
    • 使用assigned属性命中已分配的Public Function Code.
    • 使用unassigned属性命中未分配的Public Function Code.

Suricata Syntax:

modbus: function <value>
modbus: function <value>, subfunction <value>
modbus: function [!] <assigned | unassigned | public | user | reserved | all>

Suricata Demos:

// 命中访问写文件记录功能的流量(0x15)
modbus: function 21

// 命中强制仅监听模式的流量(吐槽一下Suricata Docs中写错了)
modbus: function 8 subfunction 4

// 命中访问已分配功能的流量
modbus: function assigned

// 命中访问被Modbus.org Community定义的功能的流量
modbus: function public

// 命中访问用户自定义功能的流量
modbus: function user

// 命中访问保留功能的流量
modbus: function reserved

// 命中访问非保留功能的流量
modbus: function !reserved


Access

  1. 该关键字可用于匹配某种数据访问方式(read , write).
  2. 该关键字可用于匹配某种数据类型.
    • discretes : 离散输入
    • coils : 线圈状态
    • input : 输入寄存器
    • holding : 保持寄存器
  3. 该关键字可用于匹配某个地址访问范围.
  4. 该关键字可用于匹配某个被写入的值.

Suricata Syntax:

modbus: access <read | write>
modbus: access read <discretes | coils | input | holding>
modbus: access read <discretes | coils | input | holding>, address <value>
modbus: access write < coils | holding>
modbus: access write < coils | holding>, address <value>
modbus: access write < coils | holding>, address <value>, value <value>

// 其中, <value> 的语法格式如下:

  • value 111 : value值为111.
  • value 111<>222 : value值大于111小于222.
  • value >111 : value值大于111
  • value <111 : value值小于111

Suricata Demos

// 命中以读权限访问的流量
modbus: access read

// 命中以写权限访问的流量
modbus: access write

// 命中以读权限访问输入寄存器的流量
modbus: access read input

// 命中以写权限访问线圈状态的流量
modbus: access write coils

// 命中以读权限访问地址小于100的离散输入的流量
modbus: access read discretes, address <100

// 命中以写权限向地址为500的保持寄存器中写入大于200的数据的流量
modbus: access write holding, address 500, value 200


Unit

  1. 该关键字实际就是用于匹配Unit Identifier.

    ModBus TCP网络中, 设备之间通过路由寻址. 此时如果网络中还有通过异步串行通信的设备, 此时目的IP地址为网桥本身, 而被访问的设备在网桥后, 需要通过Unit Identifier(单元标识符)来转发请求到正确的设备上.

    在使用该关键字时, 可以使用FunctionAccess中的所有属性.

Suricata Syntax

modbus: unit <value>
modbus: unit <value>, function <value>
modbus: unit <value>, function <value>, subfunction <value>
modbus: unit <value>, function [!] <assigned | unassigned | public | user | reserved | all>
modbus: unit <value>, access <read | write>
modbus: unit <value>, access read <discretes | coils | input | holding>
modbus: unit <value>, access read <discretes | coils | input | holding>, address <value>
modbus: unit <value>, access write < coils | holding>
modbus: unit <value>, access write < coils | holding>, address <value>
modbus: unit <value>, access write < coils | holding>, address <value>, value <value>

Suricata Demos

// 命中访问 Unit Identifier 10 的流量
modbus: unit 10 >
// 命中访问 Unit Identifier 10, 且命中访问写文件记录功能的流量(0x15)
modbus: unit 10, function 21

// 命中访问 Unit Identifier 10, 且命中强制仅监听模式的流量(吐槽一下Suricata Docs中又写错了)
modbus: unit 10, function 8, subfunction 4

// 命中访问 Unit Identifier 10, 且命中访问已分配功能的流量
modbus: unit 10, function assigned

// 命中访问 Unit Identifier 10, 且命中访问非保留功能的流量
modbus: unit 10, function !reserved

// 命中访问 Unit Identifier 10, 且以读权限访问的流量
modbus: unit 10, access read

// 命中访问 Unit Identifier 10, 且以写权限访问线圈状态的流量
modbus: unit 10, access write coils

// 命中访问 Unit Identifier > 10的位置, 且以读权限访问地址小于100的离散输入的流量
modbus: unit >10, access read discretes, address <100

// 命中访问 Unit Identifier 大于10并小于20的位置, 且以写权限向地址500的保存寄存器中写入大于200的数据的流量.
modbus: unit 10<>20, access write holding, address 500, value >200


Suricata Default Rules

在 Suricata 的app-layer-modbus.c中有默认定义一些关联ModBus协议的规则, 不过这些规则只是用于判断协议本身是否合法, 并不对协议内容进行匹配.

// Modbus Protocol version field is incorrect (Modbus version = 0)
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Protocol version"; app-layer-event:modbus.invalid_protocol_id; classtype:protocol-command-decode; sid:2250001; rev:2;)

// Response (answer) we didn't see a Request for. Could be packet loss.
alert modbus any any -> any any (msg:"SURICATA Modbus unsolicited response"; app-layer-event:modbus.unsolicited_response; classtype:protocol-command-decode; sid:2250002; rev:2;)

// Malformed request or response. Malformed means length field is wrong
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Length"; app-layer-event:modbus.invalid_length; classtype:protocol-command-decode; sid:2250003; rev:2;)

// Unit identifier field is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Unit Identifier"; app-layer-event:modbus.invalid_unit_identifier; classtype:protocol-command-decode; sid:2250004; rev:2;)

// Modbus Function code is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Function code"; app-layer-event:modbus.invalid_function_code; classtype:protocol-command-decode; sid:2250005; rev:2;)

// Modbus Request/Response value field is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Value"; app-layer-event:modbus.invalid_value; classtype:protocol-command-decode; sid:2250006; rev:2;)

// Modbus Expception code is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus Exception code invalid"; flow:to_client; app-layer-event:modbus.invalid_exception_code; classtype:protocol-command-decode; sid:2250007; rev:2;)

// Value field in Modbus Response does not match with Modbus Request
alert modbus any any -> any any (msg:"SURICATA Modbus Data mismatch"; flow:to_client; app-layer-event:modbus.value_mismatch; classtype:protocol-command-decode; sid:2250008; rev:2;)

// Request Flood Detected
alert modbus any any -> any any (msg:"SURICATA Modbus Request flood detected"; flow:to_server; app-layer-event:modbus.flooded; classtype:protocol-command-decode; sid:2250009; rev:2;)


总结

本文主要介绍了一下ModBus协议是怎么样的, 文中大部分内容都是官方文档内容 + 个人理解. 希望能对各位师傅和同学有所帮助.

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇