第1章:Modbus协议帧结构详解

大家好,我是老周。在工控安全这行摸爬滚打了十几年,要说哪个协议最让我又爱又恨,那非Modbus莫属。爱它简单,恨它太不安全。今天咱们就从帧结构开始,把这层窗户纸捅破。

很多人觉得Modbus帧结构就是几个字节的事,没什么好讲的。但我在项目里见过太多因为帧结构理解不到位,导致通信故障甚至安全事件的案例。所以,咱们还是老老实实把基础打牢。

1.1 RTU帧格式

RTU模式,说白了就是串口通信用的。我最早接触Modbus,就是用RS485连PLC,那时候还没什么网络安全的概念。

RTU帧的结构其实很清晰,就四个部分:

字段 长度 说明
地址码 1字节 从站地址,范围1-247
功能码 1字节 操作类型,比如读、写
数据区 N字节 具体的数据内容
CRC校验 2字节 循环冗余校验

嗯,这里要注意一个细节。RTU帧的字节之间不能有超过1.5个字符时间的间隔。我曾经在一个老旧的水处理项目里,就因为串口线太长,信号衰减导致帧间隔超时,折腾了两天才找到原因。

关键点:RTU帧的CRC校验是Modbus协议里唯一的安全机制。但说实话,这个CRC只是用来检测传输错误的,根本不是用来防攻击的。你想想看,攻击者完全可以重新计算CRC,然后替换掉原来的值。

1.2 TCP帧格式

TCP模式就简单多了。它是在RTU的基础上,去掉了CRC校验,加了一个MBAP头。为什么会这样?因为TCP/IP协议栈自己会做校验,Modbus就不用重复造轮子了。

TCP帧的结构是这样的:

字段 长度 说明
事务标识符 2字节 用于匹配请求和响应
协议标识符 2字节 固定为0x0000
长度字段 2字节 后续数据的字节数
单元标识符 1字节 相当于RTU的地址码
功能码 1字节 同RTU
数据区 N字节 具体数据

我个人习惯在分析Modbus TCP流量时,先看事务标识符。如果发现连续多个请求的事务标识符都一样,那八成是有人在重放攻击。这个技巧我在一次渗透测试中就用上了,直接抓到了攻击者的把柄。

实战技巧:抓包时注意观察MBAP头的长度字段。如果长度字段的值和实际数据长度不符,要么是协议实现有bug,要么就是有人在搞鬼。我曾经在某个工控系统里发现,攻击者就是通过篡改长度字段,让缓冲区溢出。

1.3 功能码分类

功能码是Modbus协议的核心。我把它分成三大类:

  • 位操作:读线圈状态(0x01)、读离散输入(0x02)、写单个线圈(0x05)、写多个线圈(0x0F)
  • 寄存器操作:读保持寄存器(0x03)、读输入寄存器(0x04)、写单个寄存器(0x06)、写多个寄存器(0x10)
  • 诊断和扩展:读异常状态(0x07)、诊断(0x08)、其他扩展功能码

你想想看,功能码只有1个字节,最多256种。但实际常用的就那么十几种。为什么?因为Modbus设计之初就没考虑那么多花里胡哨的功能,它追求的就是简单可靠。

我记得有一次,客户说他们的PLC经常莫名其妙重启。我过去一看日志,发现有人在疯狂发送功能码0x08(诊断)的请求。这明显是DoS攻击的前兆。后来加了白名单,问题就解决了。

安全警告:功能码0x08的子功能码0x00(返回查询数据)经常被用来做扫描探测。攻击者通过发送这个请求,可以判断目标设备是否存活。我在做安全评估时,第一步就是过滤掉这个功能码。

1.4 地址映射规则

地址映射这块,说实话,是Modbus协议里最容易让人迷糊的地方。很多新手搞不清楚线圈地址和寄存器地址的区别。

简单来说:

  • 线圈(Coil):可读可写,对应PLC的DO(数字量输出)
  • 离散输入(Discrete Input):只读,对应PLC的DI(数字量输入)
  • 输入寄存器(Input Register):只读,对应PLC的AI(模拟量输入)
  • 保持寄存器(Holding Register):可读可写,对应PLC的AO(模拟量输出)

地址范围也有讲究:

类型 地址范围 PLC地址示例
线圈 00001-09999 00001对应Q0.0
离散输入 10001-19999 10001对应I0.0
输入寄存器 30001-39999 30001对应AIW0
保持寄存器 40001-49999 40001对应AQW0

这里有个坑,我必须要说。不同厂商的PLC,地址映射规则可能不一样。比如西门子的S7-200,它的保持寄存器地址是从40001开始的,但有些国产PLC可能从40000开始。我曾经因为这个问题,在调试时浪费了一整天。

避坑指南:在做Modbus地址映射时,一定要先看设备的手册。不要想当然地认为所有设备都遵循同一个标准。我建议你在代码里加一个地址偏移量的配置项,这样换设备时只需要改配置,不用改代码。

好了,关于Modbus协议的帧结构,咱们就聊到这儿。下一章我会讲Modbus协议的安全漏洞,那才是真正精彩的部分。记住,理解帧结构是基础,但更重要的是知道怎么保护它。

课后思考:如果你是一个攻击者,你会怎么利用Modbus帧结构的这些特点来发动攻击?想清楚这个问题,你就能更好地理解为什么我们需要做安全防护。