modbus
是基于TCP/IP
工业现场的总线协议,Modbus
作为一种应用层的报文传输协议,既可以在物理层面上选择串口进行简单的串行通信,也可以使用TCP
的方式进行传输。
Modbus
的协议栈是在传统ISO/OSI
模型的基础上对数据链路层和应用层做了定义。
modbus
是一种主从协议,主设备的一方向从设备的一方下达指令,从设备的一方根据指令做出反应并回复主设备,主设备可以有多个从设备。具体来说,工作人员的计算机可认为是master,而PLC
之类的具体设备就是slave
了。每个设备有自己的“代号”,主设备通过“代号”来找到某一个对应的设备,当然也可以使用广播的方式,代号0即为广播。
Modbus
有自己的数据链路层定义,其实主要是对于传输数据格式和校验等方面的规定。
modbus
定义了自己的数据单元,功能码与具体的数据组成了PDU
(协议数据单元 Protocol Data Unit
),所谓的功能码也就是代表了主向从下达的指令是什么,这是很重要的一个知识,后面我们会具体讲功能代码指的功能,数据也就是这次指令要用到的“参数”。
只有PDU
是不够的,我们还需要知道从设备的“代号”才能知道数据往哪发,还要想办法保证数据的完整性、一致性和可靠性。所以在PDU
的基础上我们还需要添加一个地址,和一个差错校验,这就构成了ADU
(Application Data Unit
)。但要注意,由于三种Modbus
在传输中存在差异,所以ADU
,特别是校验部分会有不同。
modbus
功能码:
功能码有效范围在1~255之间。其中大部分都是保留的,如128-255为异常响应保留:
01 读线圈状态
02 读离散输入状态
03 读保持寄存器
04 读输入寄存器
05 写单个线圈
modbus
可以说是将读写指令分为了两大类,一类是离散的,也就是位操作,非1即0;第二类是模拟的,也就是数字,可以叫做字操作。
DO
(digital output
数字量输出),所谓线圈就是离散的输出状态,01即读一个离散的输出状态,举个不恰当的栗子,你家灯泡接到某个控制器上(实际上并不会存在这种情况……),我们可以通过01加上数据,比如1,让他亮,加上0,让他灭。
DI
(digital input
数字量输入),所谓的离散输入就是它,还是上面的栗子,我们想知道灯的开关是咋样的呢?就用02指令看看,如果是1,哦,按下去了,如果是0就是没按。通过这个不恰当的栗子我们大概也可以猜到,这是不可写的(如果你随便一个指令把开关给按死了,那我这灯不是彻底开不了了?),可以理解为外部对工控系统所带来的“开关”影响。
AO
(AnalogOutput
模拟输出),保持寄存器的功能,和DO
最大的不同就是它不再是0或1,可以是一个数值,比如,我们设定的PID
运行参数,或者是温度的上下限等等。
AI
(Analog Input
模拟输入),也就是输入寄存器,和DI
一样,可读但不可写,可以理解为外部对于系统的多位输入。
15就是写多个线圈,16是写多个保持寄存器。
读文件记录的20,写文件记录的21,获取异常状态的08等等。
在defcon
上展示过的fun with 0x5a
,这个0x5a
的功能码是由施耐德自己实现的非标准的功能码。
Modbus
标准未允许的功能:
获取项目和PLC
信息
开启、停止PLC
下载程序
更改程序
Modbus TCP
,通过wireshark
对Modbus
的流量包进行抓取进而观察Modbus TCP
的数据格式。
Transaction identifier
: 事务标识符。
Protocol identifier
: 默认为0。
Length
: 数据的长度。
Unit identifier
: 从机地址,因为使用了TCP/IP
所以用ip
地址来标识从机,所以该位可忽视,或者做进一步分发。
Function code
: modbus
的功能码
Data
:具体的数据
遵从TCP/IP
的基础上Modbus
加了自己的修改,主要有以下三个部分:
由于TCP/IP
本身具有数据校验部分,所以ADU
的差错校验没有了。
实用ip
可以确定从机,ADU
的附加地址也不再有效。但是目标可以继续是一个主机,再向其他从机发送数据,这时ADU
的附加地址可以作为下一个主机分发数据包时的地址。
增加了TCP/IP
的头部,比如length
、协议标识符等。