跳到主要内容

Modbus

Modbus 是 1979 年 Modicon 公司为其可编程逻辑控制器(PLC, Programmable Logic Controller)配套设计的应用层协议,Modicon 后来被施耐德电气(Schneider Electric)收购。规范由 Modbus 组织(Modbus Organization)公开发布、免费下载,这份开放性是它到今天仍占工业现场存量首位的主要原因。被引用最多的文档是《MODBUS Application Protocol Specification》V1.1b3,另有串行链路与 TCP/IP 两份实现指南配套。

协议是主从模型:主站发起请求,从站响应,从站之间不互相通信。较新的规范修订把这对角色改称客户端(Client)与服务器(Server)。不设会话,不做能力协商,一次请求对应一个报文。

传输变体​

变体承载编码完整性校验默认端口
Modbus RTURS-485 / RS-232 串行链路二进制CRC-16,低字节在前无
Modbus ASCIIRS-485 / RS-232 串行链路可打印 ASCIILRC无
Modbus TCP以太网 / TCP二进制无,仅依赖 TCP 校验和502
Modbus TCP Security以太网 / TLS over TCP二进制TLS802

RTU 帧以 3.5 个字符时间的线路静默作为帧定界;ASCII 帧以 : 起始、CR LF 结束。Modbus TCP 去掉了串行链路的校验字段,改用 7 字节的 MBAP 报头。

报文结构​

协议数据单元(PDU, Protocol Data Unit)只有两部分:1 字节功能码,加上该功能码定义的数据域。PDU 与传输方式无关,各变体在外面套不同的头部:

RTU [从站地址 1B] [PDU] [CRC-16 2B]
ASCII ":" [地址 2 hex] [PDU 每字节展开为 2 字符] [LRC] [CR LF]
TCP [MBAP 7B] [PDU]

MBAP(Modbus Application Protocol header)共 7 字节:

字段长度说明
事务标识符 Transaction Identifier2 字节主站生成,从站原样返回,用于匹配请求与响应
协议标识符 Protocol Identifier2 字节Modbus 固定为 0x0000
长度 Length2 字节其后所有字节的数量,即单元标识符加 PDU
单元标识符 Unit Identifier1 字节串行从站编号,取值 1–247,248–255 保留;串行链路上地址 0 为广播

数据模型与功能码​

Modbus 把从站数据抽象为四类基本对象,地址空间为 16 位:

对象位宽读写功能码
线圈 Coils1 位读写0x01 读、0x05 写单个、0x0F 写多个
离散输入 Discrete Inputs1 位只读0x02 读
保持寄存器 Holding Registers16 位读写0x03 读、0x06 写单个、0x10 写多个
输入寄存器 Input Registers16 位只读0x04 读

上述功能码在 TCP 与串行链路上均可用。0x07 读异常状态、0x08 诊断、0x0B 读通信事件计数器、0x0C 读通信事件日志、0x11 报告从站标识只在串行链路上有。跨两种传输的还有 0x14 读文件记录、0x15 写文件记录、0x16 屏蔽写寄存器、0x17 读写多个寄存器、0x18 读 FIFO 队列,以及 0x2B 封装接口传输——其中 MEI 类型 0x0E 为读设备标识,是设备指纹识别的常用入口。

异常响应的功能码为原功能码加 0x80,数据域第一个字节是异常码:0x01 非法功能、0x02 非法数据地址、0x03 非法数据值、0x04 从站设备故障、0x06 从站设备忙。

寻址有个「差一」问题:协议地址从 0 开始,工程文档与设备面板却习惯用从 1 开始的 PLC 地址,保持寄存器 40001 对应协议地址 0。两套编号混用是现场误写寄存器的高频原因。

安全暴露面​

设计层面的问题,协议自身无解。Modbus 不定义任何身份验证——能到达 502 端口的主机、能挂上 RS-485 总线的设备,就是合法主站。寄存器值与线圈状态明文传输,工程站与 PLC 之间的全部控制数据都能被旁路读走。Modbus TCP 连串行链路那点 CRC 都省了,只剩 TCP 校验和,挡不住主动篡改;事务标识符由主站自选,多数实现不严格校验其连续性,伪造响应与注入响应的成本很低。

地址空间又只有 1–247,一轮扫描即可穷举在线从站及其支持的功能码集合。串行链路上向地址 0 发写请求,所有从站同时执行且不返回响应,单次操作就能影响整条总线。

最要紧的是写操作的后果是物理性的:改写保持寄存器或线圈直接改变执行机构动作,阀门开度、泵启停、联锁条件都在里面。损害无法通过恢复数据回滚,这一点与企业 IT 环境根本不同。

实现层面的缺陷可以随固件收敛,但存量设备更新极慢:

  • 畸形 PDU、超长数据域、非法长度字段导致解析崩溃或设备重启,这类问题在多家实现中都出现过,具体 CVE 编号与受影响固件版本 待验证。
  • 许多 PLC 的 Modbus 服务与厂商专有编程协议共用同一网口甚至同一端口。封禁 Modbus 不等于封禁程序下载通道,实际监听情况 待验证。
  • Modbus TCP Security 的存量支持面很窄,老设备能否通过固件升级获得 TLS 支持 待验证。

检测方法​

被动优先。Modbus 是周期性轮询协议,流量高度规律,这既是它的弱点也是它的检测优势。

抓包在镜像口或分光点采集即可。Wireshark 内置解析器,显示过滤器 mbtcp 对应 Modbus TCP,modbus 对应串行变体。入侵检测这一侧,Suricata 提供 Modbus 应用层解析与 modbus 规则关键字,可按功能码、单元标识符与寄存器地址范围建白名单规则;Zeek 对 Modbus 的解析是否包含在核心发行版中 待验证。

主动枚举用 nmap --script modbus-discover -p 502 <目标>,可枚举从站标识与功能码支持情况。这个脚本发的是真实协议请求,未授权网络与运行中的生产网络上一律禁用;其他工控协议的枚举脚本名与可用性随 nmap 版本变化,待验证。互联网侧的资产测绘可以用 port:502 这类通用过滤条件在 Shodan、Censys 等平台检索暴露资产,各平台的工控协议专用过滤字段 待验证。

真正对 Modbus 有效的是基线偏离告警:对轮询周期、在线从站集合、出现过的功能码集合、寄存器取值区间建立基线,新增从站或首次出现的写功能码都应触发告警。这招放在别的协议上误报太多,放在 Modbus 上却好使,因为正常流量几乎没有变化。

要在实验环境复现异常报文,可以用 pymodbus、mbpoll,或者 modpoll(主站)配 diagslave(从站);Scapy 的 scapy.contrib.modbus 层便于构造畸形 PDU。

Modbus 协议自身不产生审计日志。操作溯源只能依赖旁路探针,或者设备侧的厂商专有日志,后者的可用性与留存粒度 待验证。

加固建议​

分区是前提。参照 IEC 62443 的 Zone / Conduit 模型划分控制层与业务层,跨区流量走显式白名单,不做默认允许。白名单的粒度要下到寄存器:工控防火墙或单向网关按「单元标识符 + 功能码 + 地址范围」建基线,只放行实际使用的组合,只读从站直接禁掉全部写功能码。

边界上,关闭不必要的 TCP 502 入站,远程维护走 VPN 或专用运维通道,不在边界防火墙上直接做端口映射。RS-485 总线要按物理安全来对待——一旦接入即等于获得完全控制权,机柜上锁、端子加护罩,并对总线上新增的主站做电气层面的告警。工程站、HMI 与办公终端不放在同一广播域,否则办公网侧的横向移动会直接落到控制网。

新建项目可以选 Modbus TCP Security;存量设备先做兼容性与停机窗口评估,评估结论 待验证。

写操作纳入变更流程,程序下载与批量写线圈、写寄存器都走审批。PLC 侧的保护模式或钥匙开关位置由厂商定义,具体行为 待验证。最后是备份:写入会不可逆地改变控制逻辑,恢复依赖离线备份而不是设备自身。

参考​

  • Modbus 组织官网与规范下载:https://modbus.org/
  • 本页表格如与规范原文冲突,以规范原文为准。