官方文档

笔记

func功能码

十进制 十六进制 英文描述(官方) 中文习惯叫法 读/写 常用过滤示例
1 0x01 Read Coils 读线圈 modbus.func_code==1
2 0x02 Read Discrete Inputs 读离散输入 modbus.func_code==2
3 0x03 Read Holding Registers 读保持寄存器 modbus.func_code==3
4 0x04 Read Input Registers 读输入寄存器 modbus.func_code==4
5 0x05 Write Single Coil 写单个线圈 modbus.func_code==5
6 0x06 Write Single Register 写单个寄存器 modbus.func_code==6
15 0x0F Write Multiple Coils 写多个线圈 modbus.func_code==15
16 0x10 Write Multiple Registers 写多个寄存器 modbus.func_code==16
20 0x14 Read File Record 读文件记录 modbus.func_code==20
21 0x15 Write File Record 写文件记录 modbus.func_code==21
22 0x16 Mask Write Register 屏蔽写寄存器 modbus.func_code==22
23 0x17 Read/Write Multiple regs 读写多个寄存器 读+写 modbus.func_code==23
43 0x2B Encap. Interface Transport 封装接口(MEI) 诊断 modbus.func_code==43

注:8、9、10、11、12、13、14 为诊断/固件类,CTF 出现频率低,未列出。

wireshark筛选exp

# 1. 只看写操作(改值、插旗、写恶意寄存器)
modbus.func_code == 5 or modbus.func_code == 6 or modbus.func_code == 15 or modbus.func_code == 16

# 2. 只看读操作(确认是否被批量读取)
modbus.func_code <= 4

# 3. 快速定位“写多个寄存器”——很多题目把 flag 一次性写进去
modbus.func_code == 16

# 4. 找异常响应(错误码 = 原始功能码 + 0x80)
modbus.func_code >= 0x80

# 5. 指定从站地址(Unit Identifier)
modbus.unit_id == 1

# 6. 组合:1 号从站 + 写多个寄存器
modbus.unit_id == 1 and modbus.func_code == 16

# 7. 想看完整报文,再跟 TCP 流
right-click → Follow → TCP Stream
  • 快速脚本(tshark 批量导出写寄存器值):
  • 输出格式:帧号 源IP 起始地址 寄存器数量 值1,值2,...
tshark -r capture.pcapng -Y 'modbus.func_code==16' \
-T fields -e frame.number -e ip.src -e modbus.reference_num -e modbus.word_cnt \
-e modbus.regval_uint16

wireshark info字段名含义

层级 字段名(Wireshark 显示) 长度 官方文档出处 含义/取值
MBAP Trans / Transaction Identifier 2 B 第三部分 §3.1.3 事务 ID,用于唯一匹配请求与响应,客户端任意递增
MBAP Protocol Identifier 2 B 第三部分 §3.1.3 协议标识符,固定 0x0000(Modbus 协议)
MBAP Length 2 B 第三部分 §3.1.3 后续字节数(Unit + PDU 总长度)
MBAP Unit / Unit Identifier 1 B 第三部分 §3.1.3 单元标识符,在 TCP 中等同于“从站地址”;<br>0=广播(仅串行),1-247=单个从站,0xFF=无效/直连
PDU Function Code / Func 1 B 第一部分 §4.1 + §6 功能码,决定操作类型(01/02/03/04/05/06/15/16…)
PDU Reference Number / Address 2 B 各功能码章节 寄存器/线圈起始地址(0 基址
PDU Bit Count / Word Count / Quantity 2 B 各功能码章节 读/写数量(上限 2000 线圈,125 寄存器)
PDU Byte Count 1 B 功能码 01-04、15、16 后续数据域字节数(仅变长响应/写请求)
PDU Data (Coil/Register values) N B 各功能码章节 原始数据,按大端(Big-Endian)排列
PDU Exception Code 1 B 第一部分 §7 仅异常响应出现,取值 01-0B(见下表)
CRC* CRC-16 (RTU only) 2 B 第二部分 §2.5.1.2 串行链路校验,TCP 模式无此字段
LRC* LRC (ASCII only) 1 B 第二部分 §2.5.2.2 ASCII 模式校验,TCP 模式无此字段

异常码速查

异常码 名称 触发场景
01 Illegal Function 不支持的功能码
02 Illegal Data Address 地址越界
03 Illegal Data Value 数据值非法(如数量超限)
04 Server Device Failure 从站内部故障
05 Acknowledge 长任务已收,请轮询
06 Server Device Busy 从站忙,稍后重试
08 Memory Parity Error 文件记录校验错
0A Gateway Path Unavailable 网关路由不可用
0B Gateway Target No Response 目标设备无响应

> 注:TCP 模式完全去掉 Address、CRC、Start/End,以上三项不会出现在 Wireshark 的 Modbus/TCP 解析树中。

coil address(Reference Number)

在 Modbus 协议里,“线圈地址”(人们口语中常说的 coil address)与 “参考号”(Reference Number)本来就是同一个值,只是叫法不同、单位不同:

  1. 官方文档(GB/T ×××× 对应 Modbus-IDA 规范)对功能码 5 的请求 PDU 定义如下:

    Function Code   : 1 byte  → 0x05
    Output Address : 2 bytes → 0x0000 … 0xFFFF
    Output Value : 2 bytes → 0x0000 或 0xFF00

    这里的 Output Address 就是 Wireshark 里解析出来的 Reference Number

  2. 该字段的语义是“从哪个线圈开始操作”,协议规定它用 0 基址,即:
    字段值 0 → 实际线圈 1
    字段值 12 → 实际线圈 13
    字段值 13 → 实际线圈 14 …

  3. 因此,当抓包看到

    Reference Number: 13

    就等于 线圈地址(coil address)= 14Wireshark 为了让人一眼看懂,把字段值直接标成“线圈地址”,只是忘了加“(0-base)”提示,于是看起来像是“解析出了 coil_address = Reference Number”。

  4. 总结一句话:
    “Reference Number”是协议层面的字段名,“coil address”是用户层面的习惯叫法;二者数值相同,仅基址基准不同,所以工具直接把它们画上了等号。

Modbus 功能码 05 (写单个线圈) Data 字段含义表

Data 数值 (16进制) 含义 说明
0000 OFF / 停止 / 断开 使线圈(Coil)断电或复位至 0 状态
FF00 ON / 启动 / 闭合 使线圈(Coil)通电或置位至 1 状态

注意

  1. 根据标准协议,除了 0000FF00 之外的其他任何数值通常都是非法的,从机(Server)应该保持线圈状态不变(或者有些设备可能会返回异常)。
  2. 虽然数值是 16 位的(2字节),但实际上只有高 8 位起作用(00FF),低 8 位始终为 00