1. Modbus协议基础回顾:帧结构、功能码、地址模型与数据模型

各位同学,咱们今天先聊聊Modbus协议的基础。说实话,这玩意儿虽然老,但在工控圈子里,你绕不开它。我做了十几年工控安全,见过太多因为不懂协议细节而出的事故。所以,咱们先把地基打牢。

1.1 Modbus的三种“方言”:RTU、ASCII、TCP

Modbus协议有三种常见的变体。嗯,你可以把它们理解成同一种语言的不同方言。它们核心的逻辑是一样的,但“说话”的方式不同。

1.1.1 Modbus RTU

这是工业现场用得最多的。它采用二进制编码,数据紧凑,传输效率高。我个人习惯在PLC与变频器、智能仪表之间通信时首选它。

  • 帧结构: 地址码(1字节) + 功能码(1字节) + 数据区(N字节) + CRC校验(2字节)
  • 特点: 每个字节以16进制发送,没有起始/停止位(在字节层面有)。
  • 避坑指南: 我曾经在一个项目中,因为CRC校验计算错误,导致整个产线数据乱跳。排查了整整两天,最后发现是CRC的初始值设错了。记住,Modbus RTU的CRC是0xFFFF开始。

1.1.2 Modbus ASCII

这个现在用得少了,但老系统里还能见到。它把每个字节拆成两个ASCII字符发送,可读性强,但效率低。

  • 帧结构: 起始符":"(1字节) + 地址码(2字符) + 功能码(2字符) + 数据区(N字符) + LRC校验(2字符) + 结束符CR/LF
  • 特点: 字符间允许有1秒的间隔,适合低速链路。
  • 个人经验: 我记得有一次调试一个90年代的进口设备,只能用ASCII模式。看着那一串串"3A 30 31 30 33...",说实话,眼睛都快瞎了。但没办法,老设备就认这个。

1.1.3 Modbus TCP

这是现代工业以太网的主流。它把RTU的CRC校验去掉了,因为TCP/IP协议本身已经保证了传输可靠性。

  • 帧结构: MBAP头(7字节) + 功能码(1字节) + 数据区(N字节)
  • MBAP头包含: 事务处理标识(2字节) + 协议标识(2字节,固定为0x0000) + 长度(2字节) + 单元标识(1字节)
  • 注意: 这里的“单元标识”其实就是RTU里的“地址码”。很多新手会搞混,以为TCP里不需要地址了。其实不是,它只是换了个位置。

核心对比: 三种模式的帧结构差异,说白了就是“包装方式”不同。RTU是“裸数据+校验”,ASCII是“转成字符+校验”,TCP是“加个TCP头”。但里面的功能码和数据,逻辑完全一样。

1.2 功能码分类:读、写、诊断

功能码是Modbus协议的灵魂。它告诉从站:你要干什么?我把它分成三大类,这样好记。

类别 功能码 名称 作用
位操作 0x01 读线圈 读取DO(数字量输出)
0x02 读离散输入 读取DI(数字量输入)
0x05 写单个线圈 控制单个DO
字操作 0x03 读保持寄存器 读取AO或参数(可读写)
0x04 读输入寄存器 读取AI(只读)
0x06 写单个寄存器 写入单个AO或参数
0x10 写多个寄存器 批量写入参数
诊断 0x08 诊断 回环测试、清计数器等

我的小技巧: 你不需要背下所有功能码。记住0x01-0x04是读,0x05-0x06是写单个,0x0F-0x10是写多个。剩下的,查表就行。我电脑桌面上永远贴着一张功能码速查表。

1.3 地址模型与数据模型

这是很多初学者最晕的地方。Modbus的地址模型,说白了就是“怎么找到你要的数据”。

1.3.1 四种数据对象

Modbus定义了四种基本数据对象,它们分别对应不同的物理或逻辑设备:

  • 线圈(Coil): 可读可写,对应DO。地址范围:00001-09999(PLC习惯用5位十进制表示)
  • 离散输入(Discrete Input): 只读,对应DI。地址范围:10001-19999
  • 输入寄存器(Input Register): 只读,对应AI。地址范围:30001-39999
  • 保持寄存器(Holding Register): 可读可写,对应AO或参数。地址范围:40001-49999

⚠️ 重要提醒: 这里的地址是“协议地址”,不是“物理地址”。比如保持寄存器40001,在协议报文里实际发送的是0x0000。为什么?因为协议地址从0开始编号,而PLC习惯从1开始。这个偏移量,我见过太多人搞错。曾经有个同事,把40001直接当0x40001发出去,结果从站直接报错。

1.3.2 数据模型:大端与小端

Modbus协议规定,多字节数据采用大端模式(Big-Endian)。也就是高字节在前,低字节在后。

// 举例:数值 0x1234 在报文中的顺序
// 大端(Modbus标准):0x12 0x34
// 小端(某些PLC内部):0x34 0x12

为什么会这样?因为Modbus诞生的时候,大多数处理器都是大端的。但现在很多ARM、x86处理器是小端的。所以,你在做协议转换时,一定要做字节序转换。我建议你写一个通用的字节序转换函数,省得每次都要手动调。

1.4 知识体系总览

下面这张图,是我自己画的Modbus协议知识体系。你可以把它当成一张地图,随时回来看看。

Modbus协议知识体系 Modbus RTU Modbus ASCII Modbus TCP 帧结构 地址码 + 功能码 + 数据区 + 校验码(RTU/ASCII) / MBAP头 + 功能码 + 数据区(TCP) 功能码分类 地址模型 数据模型 位操作 0x01 读线圈 0x02 读离散输入 0x05 写单个线圈 四种数据对象 线圈(DO) / 离散输入(DI) 输入寄存器(AI) 保持寄存器(AO/参数) 大端字节序 高字节在前 低字节在后 需注意平台差异 理解这三层结构,你就掌握了Modbus协议的80%

总结一下: 这一章我们聊了Modbus的三种变体、帧结构、功能码分类,还有地址模型和数据模型。这些东西看起来琐碎,但它们是后续所有安全防护方案的基础。你想想看,如果你连协议报文里哪个字节是功能码都分不清,你怎么做深度包检测?怎么防篡改?

下一章,我们会深入分析Modbus协议的安全漏洞。嗯,那才是真正有意思的部分。

专注资料整理