4. Modbus/TCP通信流量基线建立与异常识别
为什么 Modbus/TCP 需要“基线”而不是“规则”
Modbus/TCP 本身没有认证与加密,功能码 1/2/3/4 读、5/6/15/16 写都是合法操作。仅凭“出现写操作”无法判断异常,必须知道正常生产时谁在什么频率下访问哪些寄存器。基线就是这份“正常画像”,异常识别本质是找偏离画像的通信。
基线由三个维度构成,缺一不可:
- 功能码分布:正常时段各功能码的占比,例如只读采集站通常 3 号功能码占 90% 以上。
- 寄存器访问范围:每个主站实际访问的单元标识(Unit ID)与寄存器地址区间。
- 请求频率:每对“主站 IP → 从站 IP:Unit ID”的每秒请求数(req/s)与突发特征。
采集与统计方法
用 tcpdump 抓取正常生产时段(建议覆盖一个完整生产班次,≥4 小时)的 502 端口流量,再用 tshark 提取字段做聚合。Modbus/TCP 报文结构固定:MBAP 头 7 字节(事务 ID 2 + 协议 ID 2 + 长度 2 + 单元 ID 1),其后是 PDU,PDU 第 1 字节即功能码。
# 1. 抓包:镜像口或 SPAN 口,覆盖完整生产班次
tcpdump -i eth1 -s 0 -w modbus_baseline.pcap 'tcp port 502'
# 2. 提取功能码分布(tshark 字段 modbus.func_code)
tshark -r modbus_baseline.pcap -Y 'modbus' \
-T fields -e ip.src -e ip.dst -e modbus.unit_id -e modbus.func_code \
| sort | uniq -c | sort -rn
# 3. 提取寄存器访问范围(读保持寄存器 3 号功能码的起始地址与数量)
tshark -r modbus_baseline.pcap -Y 'modbus.func_code==3' \
-T fields -e ip.src -e modbus.unit_id \
-e modbus.reference_num -e modbus.word_cnt
# 4. 请求频率:按秒统计每对主从的请求数
tshark -r modbus_baseline.pcap -Y 'modbus' \
-T fields -e frame.time_relative -e ip.src -e ip.dst -e modbus.unit_id \
| awk '{s=int($1); key=$2"->"$3":"$4; cnt[key"_"s]++} END{for(k in cnt) print k, cnt[k]}'
基线画像示例
下表为某水处理厂正常时段(4 小时)统计出的基线画像,作为后续比对基准:
| 主站 IP | 从站 IP:Unit | 功能码 | 寄存器范围 | 平均频率 | 角色 |
|---|---|---|---|---|---|
| 10.10.1.50 | 10.10.2.11:1 | 03 | 40001–40100 | 2 req/s | SCADA 采集 |
| 10.10.1.50 | 10.10.2.12:1 | 03 | 40001–40080 | 2 req/s | SCADA 采集 |
| 10.10.1.60 | 10.10.2.11:1 | 06 | 40150–40155 | 0.1 req/s | HMI 设定值下发 |
| 10.10.1.70 | 10.10.2.11:1 | 01 | 00001–00064 | 1 req/s | 工程师站监视 |
异常识别判据
将实时流量与基线比对,命中以下任一条即标记为偏离:
- 新主站:出现基线中不存在的主站 IP 向从站发起请求。
- 新功能码:某主站使用了基线中未出现过的功能码(如采集站突然发 16 号写多寄存器)。
- 越界访问:寄存器地址超出该主站在基线中的访问区间。
- 频率突变:req/s 超过基线均值 3 倍,或出现基线中不存在的突发模式。
- Unit ID 异常:访问了基线中该从站未使用的 Unit ID。
基线不是一次性的。工艺配方调整、设备增减、SCADA 组态变更都会改变正常通信画像,建议在每次变更后重新采集一个班次并更新基线,否则误报率会快速上升。
Modbus/TCP 无认证,攻击者可直接伪造合法主站 IP 和功能码。基线只能发现“偏离正常画像”的通信,无法识别完全模仿正常行为的恶意读写。基线应与网络分区、访问控制、深度包检测配合使用。