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协议的安全漏洞。嗯,那才是真正有意思的部分。