Modbus协议取证实战:从流量分析到内存痕迹排查
2026/9/14 3:31:09 网站建设 项目流程

记不清具体是从哪一天开始,我的日常工作里就离不开Modbus了。做了几年安全事件处置和电子数据取证,尤其是接触工控环境之后,我愈发确认一个事实:在工业控制系统领域,Modbus协议就像国民级基础设施一样无处不在。水处理厂、变电站、楼宇自控、生产车间的PLC、HMI、电表、传感器,几乎都能看到它的身影,而且很多设备一跑就是十几年不换。

但恰恰是这样一个承载着核心生产业务的协议,在设计之初几乎没有考虑任何安全问题。它有清晰的帧结构、完善的功能码体系,但没有任何认证、加密或权限控制。谁在网络里能碰到它,谁就能直接读写寄存器。对做安全的人来说,理解Modbus协议是基本功,能不能在事件发生后把Modbus流量、主机痕迹、内存镜像里的线索串成一条可信的证据链,才是真正拉开差距的地方。

这篇笔记是我自己学习Modbus协议及取证方法的完整记录。我会从协议本身的结构讲起,到流量抓包分析、内存取证、主机痕迹排查,再手把手带你把Modbus Poll和Modbus Slave搭成一套可复现的实验环境。适合刚接触工控安全的入门者,也适合正在准备安全事件处置、电子数据取证相关工作的朋友做参考。

1. Modbus协议核心知识拆解

1.1 为什么是Modbus:工业现场的事实标准

Modbus诞生于1979年,最初是Modicon(后来的施耐德电气)为其PLC产品设计的一种串行通信协议。让我印象最深的是它的开放策略:协议规范完全公开,任何厂商、任何开发者都可以免费使用和实现。在那个工业总线群雄并起的年代,这一招直接让Modbus成了事实标准。

它之所以能延续到今天,在我看来有三点原因:

  • 实现简单。整个协议的核心就是地址、功能码、数据、校验这几个部分,一个8位单片机就能跑起来。
  • 硬件成本低。基于RS-232、RS-485或者以太网就能工作,其中RS-485两线制可以挂32个从站,组网成本很低。
  • 生态成熟。从PLC、仪表到组态软件,几乎所有工控设备都内置了Modbus支持。

需要强调的是,Modbus的普及度本身就是一个安全劣势。攻击者不需要学习私有协议,只要会用Wireshark抓包,再对照公开的Modbus协议文档,就能直接构造恶意报文。做过工控安全评估的朋友应该都有体会,现场扫一圈,用Modbus功能码03读一下保持寄存器,设备状态全暴露了——没有任何阻拦。

1.2 Modbus RTU与Modbus TCP的帧格式拆解

Modbus协议在传输层上可以分为两种最常见的变体:基于串行链路的Modbus RTU和基于以太网的Modbus TCP。两者的数据模型是一致的,但帧封装方式完全不同。

Modbus RTU帧格式如下:

  • 从站地址(1字节):取值范围0-247,0为广播地址,1-247为从站地址。
  • 功能码(1字节):指示要执行的操作。
  • 数据(N字节):具体数据内容,根据功能码而定。
  • CRC16校验(2字节):对前面所有字节做循环冗余校验。

RTU有一个特殊要求:每帧之间必须保持3.5个字符时间的静默间隔。这个间隔如果太短,接收方会认为是一帧连续数据;太长则会判断为帧超时。实际调试串口时,这个时序问题很容易踩坑,尤其是用USB转串口线的时候,延迟不稳定会导致经常性通信失败。

Modbus TCP帧则在RTU基础上增加了一个MBAP头:

  • 事务处理标识符(2字节):用于匹配请求和响应。
  • 协议标识符(2字节):Modbus固定为0x0000。
  • 长度字段(2字节):表示剩余字节数。
  • 单元标识符(1字节):等同于RTU的从站地址,用于网关转发时标识最终从站。
  • 功能码(1字节)和数据(N字节):与RTU一致。

Modbus TCP默认使用502端口,这也是取证时最重要的流量特征之一。RTU帧里的CRC16在TCP帧中不再需要,因为TCP本身有校验和与可靠传输机制,MBAP头里的长度字段也解决了粘包分包问题。理解这个区别对抓包分析很有用:你看TCP流的时候,不要继续找CRC字节,否则会把响应末尾的多余数据误认为负载。

1.3 常用功能码与识别特征

Modbus的功能码整体分为位操作和字操作两大类。做取证时,我习惯先快速判断流量中的功能码,立刻知道攻击者或异常操作者在做什么:

功能码名称操作对象取证关注点
01读线圈输出位查看设备状态
02读离散输入输入位查看外部开关量
03读保持寄存器输出寄存器最常见的读操作
04读输入寄存器输入寄存器读取模拟量数据
05写单个线圈输出位单点启停控制
06写单个寄存器保持寄存器修改单个参数
0F(15)写多个线圈输出位批量启停控制
10(16)写多个寄存器保持寄存器批量参数修改

从取证角度,写操作功能码(05、06、0F、10)是重中之重。攻击者如果想破坏或篡改工控过程,绝大多数情况下是通过写功能码下发的。我在实际分析中,会先用Wireshark过滤modbus.func_code == 16看看有没有批量写寄存器的行为,再结合时间判断是否异常。

2. 取证视角下的Modbus痕迹体系

2.1 流量层面的痕迹

Modbus取证最直接的证据来源就是网络流量。在工控现场,流量抓取点一般选择交换机的镜像端口、工业防火墙的旁路接口,或者在网关设备上做流量复制。Modbus TCP流量特征非常明显:固定源或目的的502端口、MBAP头中协议标识符为0、长度字段合理、数据区不含标准文件头。用Wireshark打开pcap文件,如果大量出现Modbus/TCP协议标识,基本可以确定这是工控网络流量。

流量层取证的关注点包括:

  • 谁在主动连接502端口。通常主站是固定的上位机IP,一旦出现陌生IP发起扫描,就要重点关注。
  • 谁在超大批量读取寄存器。这可能是扫描行为,攻击者想快速获取全部点位状态。
  • 谁在非工作时间执行写操作。凌晨两三点对PID参数做修改,这个时间点本身就说明问题。
  • 是否存在功能码错误响应。比如从站返回异常码02(非法数据地址),可能说明攻击者在盲目探测寄存器区间。

Wireshark里的Statistics -> Conversations可以快速统计通信双方的连接数、包数和字节数。我一般会先看端口502的会话列表,找出通信量最大的IP对,然后再深入追踪流。

2.2 主机与内存层面的痕迹

工控环境中有大量Windows主机,例如工程师站、HMI、数据采集服务器。这些主机上运行着组态软件、OPC客户端、Modbus Poll等测试工具,一旦发生安全事件,主机上的痕迹往往比流量还丰富。

主机痕迹主要包括:

  • 进程信息:正在运行或历史上运行过的进程,特别是Modbus相关工具。
  • 网络连接:进程对应的TCP连接,恢复出远端IP和端口。
  • 工程文件:组态软件的工程目录中,可能保存着点位表、脚本程序。
  • 日志记录:Windows事件日志、PowerShell操作日志、RDP登录记录。
  • 注册表与启动项:自启动程序、服务配置。

如果现场情况不允许直接操作原机,或者主机已经关机,就需要做内存取证。内存镜像里保存着进程、网络连接、命令输入等大量瞬态信息。Volatility中的netscan插件就是专门用来扫描内存镜像中网络连接对象的,它可以恢复出进程对应的本地地址、远端地址、连接状态和PID,这是我在内存取证中使用频率最高的插件之一。

2.3 现场设备与日志痕迹

PLC本身一般没有日志功能,尤其老型号的Modbus从站设备,你把它重启一下,内部寄存器状态就全部恢复默认了。所以要取PLC里的当前值,必须在设备断电前通过上位机读取。这个操作要非常谨慎,因为连接PLC读寄存器也有可能导致某些程序逻辑变化,最好在工艺窗口期操作。

现场真正有价值的日志痕迹包括:

  • 上位机历史数据库。很多数据采集系统会把实时数据存入数据库,包含点位值变化的时间戳,能复原案发前后的状态变化。
  • HMI操作记录。部分HMI软件有操作审计,记录了操作员点击了哪些按钮、切换过哪些画面。
  • DCS或SCADA系统的报警事件记录,通常是Event Log形式。
  • 边缘网关的转发日志,记录了对下Modbus轮询和对上MQTT/OPC UA转发的情况。

3. 搭建Modbus取证实验环境

3.1 工具选型与许可说明

要学习Modbus协议分析和取证,光看书是不够的,必须动手搭一套环境自己抓包看帧。最常用的模拟工具是Modbus Poll(主站模拟)和Modbus Slave(从站模拟)。一个充当上位机不停读数据,一个充当PLC响应请求,中间用Wireshark抓包,整个过程清晰直观。

这里要特别说明一下工具授权问题。Modbus Poll和Modbus Slave官方提供评估版本,可以正常使用大部分功能,但评估版有使用时间限制,比如21天后需要注册。网上流传的各种注册码和破解文件,我完全不建议使用——首先法律风险不言而喻,其次工控环境中工具一定要可控可信,从非官方渠道下载的软件本身就可能携带恶意代码。如果你需要长期使用,可以向官方购买授权,价格并不离谱。如果不想花钱,也有不错的免费替代方案:

  • Modpoll命令行工具,简单直接的Modbus主站请求工具。
  • Python的pymodbus库和minimalmodbus库,可以自己写脚本模拟任意主站和从站行为。
  • 谷哥的Eximo(已经不再更新)或FreeModbus开源协议栈,可以自己编译一个从站。
  • Wireshark自带modbus解析器,抓包分析不用额外付费工具。

我的建议是:初学者先用Modbus Poll/Slave评估版入门,因为图形界面直观,配置参数可以点选,能帮你快速理解主从交互流程。后面写自动化分析脚本时再用pymodbus。

3.2 主从站连接配置实操

第一步:准备环境。一台Windows电脑,安装Modbus Poll、Modbus Slave和Wireshark。如果只有一台电脑,可以通过Modbus Poll连接本机的Modbus Slave,走TCP回环地址127.0.0.1,或者走虚拟串口对。

第二步:配置Modbus Slave为从站。打开软件后选择Setup -> Slave Definition或直接点击新建,关键配置如下:

  • Slave ID:默认填1,表示从站地址。
  • Function:选择03(读保持寄存器)。
  • Address:起始地址填0,Quantity填10,读10个寄存器。
  • View:选择Word或Hex格式显示寄存器值。

我在从站里手动设置几个有含义的寄存器值,比如地址0为温度100.5,地址1为压力2.34,方便后面抓包时对照数据。

第三步:配置Modbus Poll为主站。打开软件后选择Connection -> Connect,连接方式选TCP/IP,填入127.0.0.1和502端口;如果走串口就选RTU,填串口号、波特率9600、数据位8、停止位1、无校验。然后设置读寄存器:

  • Slave ID:1。
  • Function:03。
  • Address:0。
  • Quantity:10。

点击OK后,如果一切正常,Modbus Poll的主界面会显示从站返回的10个寄存器值,并且按设定的轮询周期自动刷新。这一步连不通时,常见的报错是Connection failed,原因多半是从站ID不一致、端口被占用或Windows防火墙拦截了502端口的入站连接。

第四步:启动Wireshark抓包。选择回环接口或者实际使用的物理网卡,设置过滤条件tcp.port == 502 || modbus,然后让Modbus Poll和Slave跑几秒。你会在抓包列表里看到大量的Modbus/TCP协议包。

3.3 流量抓取与理解

打开一个请求包,展开Modbus协议层,你能看到非常清晰的层次结构。比如请求包中MBAP头包含事务ID、协议ID、长度;随后是单元ID、功能码03、起始地址0x0000、寄存器数量0x000A。响应包则会返回字节计数和寄存器数据,例如Byte Count: 20后面跟20个字节的数据,换算成10个16位寄存器值。把响应数据和Modbus Slave界面里的寄存器值对照,你会发现完全一致,这就是最能加深印象的一次练习。

我通常会保留这套实验拓扑作为后续取证练习的基础。需要模拟攻击行为时,可以让Modbus Poll对从站执行写操作(功能码06或16),把正常的寄存器值篡改掉,同时在Wireshark中记录全过程。这样你就有了一个完整的、含恶意操作的pcap样本。

4. Modbus流量取证实战分析

4.1 关键过滤表达式

在流量取证中,Wireshark过滤表达式的熟练程度直接影响效率。下面是我常用的一组Modbus过滤语句:

  • modbus:显示出所有Modbus协议包。
  • tcp.port == 502:按端口过滤,抓取Modbus TCP通信。
  • modbus.func_code == 3:只看功能码03的读保持寄存器请求。
  • modbus.func_code == 16:只看功能码16的写多个寄存器请求。
  • modbus.func_code == 6:只看功能码06的写单个寄存器请求。
  • modbus.exception_code:只看异常响应的包。
  • modbus && modbus.word_cnt > 100:筛选出读取或写入寄存器数量超过100的包,可能是扫描行为。

如果需要更复杂的分析,可以用tshark命令行。例如统计pcap中所有Modbus功能码的分布:

tshark -r capture.pcap -Y "modbus" -T fields -e modbus.func_code | sort | uniq -c

统计完成后,你能看到整个流量中以读功能码03为主,还是混入了大量写功能码。正常轮询场景下读操作远多于写操作,如果写操作比例明显偏高,就要重点溯源了。

4.2 从流量还原攻击行为

讲一个我在实际项目中处置过的典型场景。

某生产车间的上位机报出多台仪表数据跳变,运维人员怀疑是PLC遭受干扰。拿到工控交换机镜像口的pcap后,我先做了一次整体功能码统计,发现一个从未见过的IP 192.168.1.101频繁访问多个从站设备的502端口,并且使用了大量功能码06去写单个寄存器。对比正常上位机192.168.1.10的行为,规律完全不同:正常上位机是每隔500毫秒轮询一次,功能码只有03和04;异常IP则是随机间隔发起连接,而且每次连接都执行写操作。

接下来用Wireshark的Follow -> TCP Stream功能追踪其中一条完整的写操作流,可以看到异常IP把仪表内部地址0x0001的寄存器从0x0064改成了0x0000。结合设备手册查0x0001地址对应的是量程上限参数,基本可以断定这是一次蓄意的参数篡改。

处理这种样本时,我固定会做以下动作:

  • 记录关键包的时间戳,精确到毫秒。
  • 导出异常IP的所有数据包为单独pcap,保留原始证据。
  • 用capinfos计算pcap的统计信息,记录包数、起止时间、文件哈希值,作为证据链完整性的一部分。
  • 截图保存Wireshark中的过滤结果和字段展开情况,方便写报告。

4.3 一个完整的分析样本

假设你拿到一个pcap,里面有一段写操作。以功能码16(写多个寄存器)为例,一个典型的构造如下:

请求包:事务ID 0x1234,协议ID 0,长度 15,单元ID 1,功能码 16,起始地址 0x0000,寄存器数量 0x0002,字节计数 04,数据 AABB CCDD。

响应包:事务ID 0x1234,协议ID 0,长度 6,单元ID 1,功能码 16,起始地址 0x0000,寄存器数量 0x0002。

取证分析时,我会把请求包和响应包放在一起看,确认一次成功的写操作。关键信息提取出来如下:

  • 源IP、目标IP。
  • 源端口、目标端口(通常是502)。
  • 事务ID:用于匹配请求/响应对。
  • 起始地址和寄存器数量:精确到被篡改的数据地址范围。
  • 写入的原始数据:从字节中还原出十进制或浮点值。
  • 时间戳:事件发生时间。

有了这些要素,加上从站设备那边的日志或历史数据库,我就能基于时间线把一个写操作讲出完整的上下文。比如凌晨2点15分23秒456毫秒,IP 192.168.1.101向PLC发送功能码16写请求,起始地址100的2个寄存器被改为0x0000,随后历史数据库中温度值开始异常,间隔约3秒,这个改动生效导致设备跳停。

5. 内存取证与主机痕迹排查

5.1 Volatility netscan内存取证

在事件处置中,内存镜像的价值非常高。很多恶意行为在主机运行期间只存在于内存中,关机后就会消失。工控主机特别典型:工程师站的组态软件、临时打开的Modbus Poll工具、可能残留的远程连接,都可以从内存镜像中找到痕迹。

推荐的内存采集工具是FTK Imager或DumpIt,前者可以图形界面采集并同时计算哈希值,后者适合现场快速制作内存镜像。在工控现场执行内存采集时,要注意尽量通过只读方式接入移动硬盘,避免在目标主机上安装额外驱动造成污染。

拿到内存镜像后用Volatility2分析。首先确认系统profile:

volatility -f mem.dmp imageinfo

确认profile为Win7SP1x64或Win10x64_19041等之后,运行netscan插件:

volatility -f mem.dmp --profile=Win7SP1x64 netscan

输出中有几个关键字段:Proto表示TCP/UDP,Local Address和Foreign Address是连接的五元组,State是ESTABLISHED、LISTEN、CLOSE_WAIT等状态,PID和Owner指明了连接归属进程。

我在分析时会把输出结果导入Excel或直接复制到文本文件,然后筛选出Foreign Address为内网工控网段的连接,特别是502端口的连接。一个常见的发现是:某个工程师站进程残留着一个ESTABLISHED状态的连接,而远端IP在流量里已经找不到了,这说明攻击者可能远程连接过后台,并且痕迹已经断开。进程信息方面可以配合pstree插件查看进程父子关系,看Modbus Poll是不是被其他进程启动的。

另外,如果你在流量取证时发现通信对端的IP在内存里查不到进程,不要急着下结论。TCP连接最终是绑定到进程的,netscan输出的Owner字段对应的就是创建该连接对象的进程。结合pslist得到进程的可执行路径,能进一步确认是哪个软件建立的连接。

5.2 Windows主机痕迹排查流程

Windows主机的痕迹排查是事件处置的常规动作。我通常按照以下顺序操作,确保覆盖主要痕迹点:

第一步,采集系统基本信息。命令如下:

systeminfo net user tasklist /v netstat -ano
wmic process get ProcessId,ParentProcessId,ExecutablePath,CommandLine

第二步,检查网络连接和可疑进程。结合netstat -ano中的PID,可以在任务管理器或wmic中定位是哪个进程在监听502端口。如果现场不允许GUI操作,可以用PowerShell:

Get-Process -Id <PID> | Format-List *

第三步,排查自启动和服务:

reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run reg query HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run Get-Service | Where-Object {$_.Status -eq "Running"} Get-ScheduledTask | Where-Object {$_.State -ne "Disabled"}

第四步,检查日志。重点看Windows事件日志中的登录类型3(网络登录)、登录类型10(远程交互式),以及PowerShell的日志。

Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624,4625} | Select-Object TimeCreated,Message Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'} | Select-Object TimeCreated,Message

第五步,查看最近打开的文档、下载记录和USB设备使用记录。Modbus相关工具有时候是运维人员用U盘拷进去的,USB记录往往能反映文件来源。

5.3 工控主机排查的特殊注意事项

工控主机的排查和普通办公网主机有本质区别。办公网的Windows可以随便跑全盘杀毒、深度扫描,但工控主机上可能运行着关键组态软件,任何额外驱动、网络扫描都可能干扰PLC通信,导致生产事故。在这方面我踩过一次坑,此后都坚持以下原则:

  • 先镜像、后分析。能采集内存镜像就采集,能在测试环境里分析就不要在目标机上反复执行命令。
  • 分析命令尽量使用只读工具,避免安装可能有冲突的驱动。
  • 如果必须实时查看进程或网络连接,优先使用系统自带命令,如tasklist、netstat,不要额外安装第三方安全软件。
  • 工控主机的取证时间窗要尽量短,避免业务中断太久。
  • 时间线对齐时,要考虑现场设备的时钟漂移。PLC、HMI、上位机的时间可能相差几分钟甚至更久,我一般会在取证时记录每台设备的本地时间和UTC偏移,后续统一换算。

6. 常见问题与避坑指南

6.1 常见问题速查表

我整理了一张问题排查表,涵盖我学习Modbus取证实操时遇到最多的几个问题:

问题描述可能原因解决办法
Wireshark抓不到Modbus TCP包接口选错或过滤条件过严确认网卡和抓包点,过滤放宽为tcp.port == 502
Modbus Poll连接不上Slave从站ID不一致、端口错误核对Slave ID、端口号,检查Windows防火墙
串口RTU通信失败波特率、校验位不一致主从站配置必须完全一致,确认USB转串口驱动正常
功能码显示UnknownWireshark版本旧或私有功能码升级Wireshark,查阅设备手册确认功能码
请求能发出但无响应从站地址被占用,或轮询间隔太短检查从站列表,增加轮询间隔
netscan没有任何结果profile选择错误重新用imageinfo确定profile
内存镜像分析时插件失败镜像不完整或加密重新采集,确认采集工具没问题
pcap时间序列混乱各设备时钟不同步记录各设备时钟偏移,统一换算为UTC

6.2 实操避坑经验

第一,现场取证时,抓流量优先于动主机。我在工控环境里的习惯是:先想办法在交换机镜像口或网关旁路抓一份长期流量,再上主机做静态分析。流量是客观的,CPU再高也不会丢失已经抓下来的数据。主机一旦操作,很多东西就被改了,比如进程列表的瞬态、网络连接的实时状态,你敲下netstat那一下,某些不断开连接就消失了。

第二,Modbus Poll/Slave的工程文件里可能藏着操作记录。很多人不知道Modbus Poll的配置信息会保存在.mbp文件里,里面记录了连接的目标IP、端口、从站ID、功能码、起始地址、寄存器数量。这些信息在排查异常连接时很有价值,我曾在最紧急的处置中靠一个.mbp文件锁定了攻击者利用过的Modbus映射点。

第三,取证必须保证证据链完整性。每采集一个镜像、一个pcap,第一时间计算SHA256哈希并记录在案。报告里写清楚采集时间、采集人、采集工具版本,这能省掉后面很多口径拉扯。

第四,不要把流量取证和内存取证割裂开。一次完整的取证结论,往往是先通过流量看到写操作,再到内存里找到执行写操作的进程,再到主机日志里找到进程启动的路径或RDP登录记录,最后形成一条完整的时间线。单一维度的证据说服力有限。

最后再分享一个小体会

我学习Modbus取证的初衷其实很朴素:工控现场的资料少,很多老师傅只懂设备正常运行,一出安全事件就手足无措。后来经历了几个真实项目,我才发现这套老协议的取证工作量大头不在协议本身,而在如何把散落在流量、内存、主机、设备里的线索串成证据链。现在每当我拿到一份新的pcap或内存镜像,都会习惯先整理一下时间线,再开始分析,这个习惯帮我省了很多事。Modbus这个协议恐怕还会在工业现场活跃很多年,与其抱怨它不安全,不如把它的每一个字节、每一个功能码都研究透。希望这篇笔记能让你少走几个弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询