ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Modbus协议流量取证实战:从报文到工控事件还原

2026/9/18 7:47:27 拓冰建站 浏览量
Modbus协议流量取证实战:从报文到工控事件还原 半夜两点半工厂值班室的电话突然响了。产线上的三号PLC在一分钟内连续收到了几十条写寄存器指令把一组关键的工艺参数全部改成了0整条流水线直接停摆。事后溯源时所有人做的第一件事几乎都是相同的——找到交换机的镜像口把当天的报文拉出来一条一条翻Modbus记录。Modbus协议在一套工业控制系统里往往显得毫不起眼可一旦进入取证环节它就变成还原攻击路径、定位被控对象、评估影响范围的最重要线索。这篇笔记想做的事就是把Modbus协议本身的细节和围绕它的流量取证方法放在一起按一套可以复用的思路整理下来。适合工控安全工程师、电子数据取证人员、工厂信息化运维以及对ICS/OT安全感兴趣、想入手协议分析的同学。1. 为什么说Modbus取证是工控事件的第一现场1.1 一个典型的工控攻击路径先把视野拉高一点看攻击者是怎么打到PLC的。常见的路径是先从IT网络入口进入企业内网通过钓鱼、漏洞利用或弱口令拿到一台主机再横向移动到OT网络找到与PLC通信的上位机或HMI最后直接用Modbus协议向下发指令。整个链条里最容易被忽略也最关键的一步就是最后这段Modbus通信。这里有一个很现实的问题Modbus在设计之初几乎没有安全机制。没有内置加密没有身份认证功能码和数据基本是明文传输。攻击者只要掌握了报文格式就可以自行构造请求让PLC读写任意寄存器。传统IT安全里依赖的防病毒、EDR这类主机侧手段在PLC这种嵌入式设备上基本无效而OT网段的审计日志又往往缺失。结果就是当攻击已经造成停产或设备损坏时能够还原攻击者操作过程的只剩流量。网上很多案例复盘里提到的Stuxnet、Industroyer这类事件都会把流量解析当作核心环节原因也在这。Modbus流量像一个透明的操作记录仪把攻击者在工控网络上做过的所有操作几乎原样保留下来。1.2 电子数据取证体系中的定位取证方法通常分成几路介质取证、内存取证、网络流量取证、日志取证。Modbus事件最核心的往往是网络流量取证因为PLC本身几乎没有审计日志概念很多老型号PLC连基础的安全日志都没有它不会记录“谁在几点几分改过哪个参数”。上位机的操作日志倒是可能有但攻击者往往会先清理现场、删日志。所以遇到这类事件我会按这样的优先级安排工作第一时间保护并采集交换机的镜像流量、工业防火墙日志、上位机与PLC之间的通信记录同步对相关上位机和工程师站做内存取证和主机痕迹排查最后才去分析PLC固件、程序块和组态文件确认设备底层是否被改动。流量之所以排在最前面是因为它独立于主机侧攻击者很难在交换机上删除已经抓到的包。只要采集点和采集时长覆盖了攻击窗口即使主机被格式化Modbus报文依然能清楚指出设备被做了什么。1.3 用“读/写功能码”快速刻画攻击意图Modbus报文里的功能码本身就能体现攻击阶段侦察阶段大量读请求比如读保持寄存器、读输入寄存器用于摸清设备寄存器布局执行阶段出现写入类功能码如写单个线圈、写单个寄存器、写多个寄存器直接改变设备运行参数维持阶段周期性轮询数据伪装成正常的SCADA采集流量便于长期观察和再次操控。掌握这个对应关系后拿到pcap可以先用功能码做一次粗筛再看时间分布基本就能判断这个网络里是否发生了恶意操作。这是后面所有深入分析的起点。2. Modbus协议拆解取证前必须吃透的分层和帧结构2.1 先分清“应用层”和“承载方式”Modbus是一个应用层协议可以跑在多种物理链路上。最常见的两种形态是Modbus RTU和Modbus TCP。很多初学者把这两者混在一起结果在取证时看串口日志时不知道从哪里切帧看以太网抓包时又把RTU封装进TCP的流量当成原生Modbus TCP去解析导致功能码和长度字段全错。记住一条Modbus RTU二进制编码典型跑在RS-232/RS-485串口上以从站地址开头以CRC校验结尾Modbus TCP跑在TCP/IP之上默认端口502带有MBAP报文头没有CRC字段Modbus ASCII用ASCII字符编码带LRC校验目前在工业现场已很少见但老系统中仍有存量。取证时看到以太网抓包首先要确认pcap里是原生Modbus TCP还是把RTU封装进TCP的网关流量。分辨方法很简单原生Modbus TCP在协议栈里能直接解析出modbus字段而RTU-over-TCP的TCP payload里是一串十六进制字节开头是地址码结尾是CRCWireshark不会自动把它识别成Modbus TCP。2.2 Modbus TCP的MBAP头与PDU原生Modbus TCP报文由两部分组成MBAP头Modbus Application Protocol header PDU协议数据单元。MBAP头一共7字节字段字节数说明取证关注点事务标识符2用于匹配请求和响应高位低位事务异常可提示扫描/注入协议标识符20x0000表示Modbus非0值暗示非标准协议或防火墙封装长度2后续字节数与报文实际长度不符时判断畸形报文单元标识符1标识从站通常对应PLC地址用于确认具体受害设备PDU部分第一字节是功能码之后是数据。举一个非常典型的请求报文示例00 01 00 00 00 06 01 03 00 6B 00 03逐字节翻译00 01事务标识符1说明这是主机发出的第一个请求00 00协议标识符0标准Modbus00 06长度6表示后面还有6字节01单元标识符1目标是1号站03功能码3读保持寄存器00 6B起始寄存器地址0x6B10700 03读取3个寄存器。这个报文的意思是请1号从站把寄存器地址107开始的3个保持寄存器值发给我。攻击者一旦通过这类读操作获取设备参数后面的写入往往接踵而至。2.3 Modbus RTU的帧切分串口取证的重点Modbus RTU的帧是连续的字节流没有以太网帧边界取证时要从原始串口数据里把帧切出来。RTU帧结构[从站地址1字节] [功能码1字节] [数据N字节] [CRC低字节] [CRC高字节]例如读请求01 03 00 6B 00 03 74 0E01从站地址03功能码读保持寄存器00 6B起始寄存器地址00 03寄存器数量74 0ECRC校验值低字节在前。切帧时最关键的是找到CRC正确的位置。计算从地址到数据结尾所有字节的CRC16与帧尾两字节比对一致就说明这是一个完整且未损坏的RTU帧。数据量大的串口日志里只要找到CRC校验点就能准确切出每一条指令。RTU帧里没有源地址概念只有从站地址因此取证时无法靠IP区分发起方只能通过链路本身、网关日志或上游设备的转发记录来定位谁在发指令。这一点在写报告时要特别说明不能拿串口数据直接断言某个IP执行了写操作。2.4 功能码取证时最值得盯住的一组数字Modbus功能码决定了报文的意图也是取证时最值得盯住的一组数字。常用的功能码如下功能码名称方向取证关注度0x01读线圈主→从中侦察阶段0x02读离散输入主→从中侦察阶段0x03读保持寄存器主→从高侦察参数0x04读输入寄存器主→从高侦察实时量0x05写单个线圈主→从极高直接改变设备状态0x06写单个寄存器主→从极高直接改写参数0x0F写多个线圈主→从极高批量篡改0x10写多个寄存器主→从极高批量篡改最危险0x17读/写多个寄存器主→从高复合操作从安全角度看只要在pcap里发现0x05、0x06、0x0F、0x10这类写入功能码都需要立即核实发送方是否在白名单内、时间点是否在正常运维窗口内、数据内容是否与工艺参数匹配。异常响应码同样值得注意异常响应会在原功能码的最高位置1比如0x83是0x03读请求的异常响应后面跟随异常码01对应非法功能02对应非法数据地址03对应非法数据值。攻击者反复收到这些异常码往往说明其在“盲写”——并不了解PLC寄存器布局只能暴力试探。这种特征对判断攻击者水平、还原攻击意图很有帮助。2.5 字节序和寄存器地址映射Modbus协议规范里寄存器是16位多字节数据在网络上采用大端序Big-Endian高字节在前。但厂商实现并不统一有些PLC内部存储是小端序有些干脆按字交换顺序给取证解读带来很大麻烦。举个例子一个寄存器原始字节是12 34。如果按大端解读值是0x1234即4660如果按小端解读值是0x3412即13330。同一个字节序列解读方式不同得到的数值天差地别。在写取证报告时如果没确认PLC的具体型号和厂商字节序规则最稳妥的写法是“寄存器原始字节为12 34按大端序对应十进制4660按小端序对应十进制13330需结合设备文档确认实际含义”而不是直接给出一个确定值。寄存器地址也存在“协议地址”和“数据模型地址”的对应关系。比如保持寄存器协议地址0x6B在很多组态工具里显示为4010840001107。这个偏移来源于早期的Modicon寻址规范不同厂家文档写法有差异。取证时最好同时标注协议地址和组态地址避免报告阅读者因习惯不同而误解。3. 流量取证的现场操作从采集点到哈希固定3.1 采集点怎么选才不添乱工业网络的流量采集最难的不是工具而是“不能影响生产”。在PLC与上位机之间的链路上串接一台抓包设备从网络上是可行的但在生产环境中极可能引入额外延迟、单点故障甚至直接导致链路中断。绝大多数现场不允许这样操作。推荐的采集方式有两种交换机端口镜像SPAN/RSPAN把连接PLC或上位机的交换机端口流量镜像到一个专用抓包口不影响原链路网络TAP在链路上部署分光器或无源TAP物理上复制流量不改变链路原有转发路径。实际操作中我会先确认交换机的型号和配置方式再明确需要镜像的是哪个VLAN或哪个物理端口。如果不确定攻击发生在哪个设备上优先镜像连接工程师站、HMI和核心PLC的三个重点端口如果交换机支持把整个OT核心VLAN镜像一份更好。3.2 抓包时的最小可靠操作现场抓包要按最小可靠原则操作。最小是尽量不改动生产配置可靠是保证证据完整、时间连续。以下是一套我实测稳定的流程在分析机上禁用无关服务只保留抓包程序使用dumpcap或tcpdump抓包保存原始报文不做任何过滤设置文件循环覆盖单文件1GB保留最近48小时到72小时避免磁盘占满抓包机的时间与标准时间同步记录抓包机的UTC偏移抓包结束后立即对每个pcap文件做哈希固定。抓包命令可以直接这样写dumpcap -i eth0 -w /evidence/plc3_20250102_0130.pcapng \ -b filesize:1048576 -b files:10-b filesize:1048576表示单文件1GB后滚动生成新文件-b files:10表示最多保留10个文件超过后覆盖最旧的。注意dumpcap默认保存pcapng格式在Wireshark里完全兼容但部分司法鉴定工具或旧版分析软件只认pcap。如果目标环境较老旧可以在抓包后统一转换editcap -F pcap input.pcapng output.pcap关于保存时间很多人问到底要抓多久。如果在事件发生后的12小时内介入建议至少保留48小时的镜像流量如果事件发现较晚、攻击时间窗口不确定则尽量覆盖发现时间点之前7天的流量。当然这取决于存储空间所以我通常准备一个4TB以上的外置存储并优先保存完整pcap后续分析用副本原文件不动。3.3 证据固定哈希和记录链流量抓下来只是开始还需要保证证据在法律意义上可用。取证固定就三步对每个pcap文件计算SHA-256把原始文件和哈希值一起存入只读介质或经写保护的存储设备记录采集时间、采集人、采集设备、接入端口、交换机配置截图等元信息。sha256sum /evidence/plc3_20250102_0130.pcapng输出的哈希值必须记录在勘验笔录中。后续所有分析都应使用副本不直接打开原始文件这是电子数据取证的基本规矩。如果事件可能进入法律程序这一步不能省即使只是内部复盘做好哈希也能防止“有人事后改了文件”的争议。3.4 用Wireshark快速定位可疑Modbus流量拿到pcap后先用Wireshark加载快速查看是否有Modbus流量。最常用的过滤器modbus显示所有被识别为Modbus的报文。如果抓包环境里还有其他工控协议可以进一步限定端口和协议tcp.port 502 modbus想只看写入类操作modbus.func_code 6 || modbus.func_code 16 || modbus.func_code 5 || modbus.func_code 15Wireshark里功能码以十进制显示所以0x06对应60x10对应160x05对应50x0F对应15。想定位特定从站modbus.unit_id 2想查看某个寄存器范围内的访问modbus.reg 0x6B这些过滤器配合统计菜单里的IO Graph、Protocol Hierarchy可以快速看出Modbus流量在时间轴上的分布。正常轮询通常是周期性的、流量平稳的尖峰如果是攻击往往会在某个时间点出现突发的大量读写请求IO Graph上会呈现明显的脉冲。4. 从报文到事件还原构建完整证据链4.1 先看五个维度再谈结论拿到Modbus流量分析时不要一开始就陷入某个报文的细节。我习惯按五个维度建立全局视角时间读写请求是否集中在非工作时间段源地址发送方IP是否属于白名单内的上位机或HMI目标地址被访问的PLC是否涉及关键工艺环节功能码是否出现大量写入类操作寄存器地址和数据值操作对象是否与事件影响吻合。这五个维度相互印证单靠其中一两个不能下结论。比如某个IP在凌晨三点对PLC执行了0x06写单个寄存器但恰恰那天值班工程师在做参数调整且有工单记录那就不能直接定性为攻击反过来如果源IP从未出现在历史流量中操作指令又是批量写入即使设备还没发生故障也应立刻按安全事件处理。4.2 还原攻击时序读、写、异常码的排列组合攻击者操作PLC往往有清晰顺序。最常见的时序是用0x03或0x04读取寄存器摸清设备参数用0x06逐个改写关键寄存器如果目标参数是批量配置改用0x10一次写入如果写入失败出现异常响应0x83 02或0x86 02之类的报文攻击者会换地址继续尝试。举一个真实案例的简化还原10:02:01.123 10.10.1.5 - 10.10.2.20 Modbus/TCP 读取寄存器, 起始0x0000, 数量10 10:02:01.456 10.10.2.20 - 10.10.1.5 Modbus/TCP 响应, 数据10个寄存器 10:02:02.001 10.10.1.5 - 10.10.2.20 Modbus/TCP 写单个寄存器, 地址0x0003, 值0x0000 10:02:02.240 10.10.2.20 - 10.10.1.5 Modbus/TCP 响应成功 10:02:02.511 10.10.1.5 - 10.10.2.20 Modbus/TCP 写多个寄存器, 地址0x0010, 数量8这条时间线可以清楚证明某台主机在2秒内先侦察后写入而且写入的寄存器地址正好是控制逻辑中定义关键参数的地址。响应码全部成功说明PLC确实接受了这些修改。这时候即使上位机的操作日志被删除流量也把全过程钉在了时间轴上。4.3 畸形报文和灰色行为不能忽视的线索除了明显的读写操作流量里还容易出现几类“灰色行为”大量不带事务标识符递增规律的请求说明发送方可能是脚本工具而非正常HMI多次重复相同的写请求攻击者在持续覆盖PLC寄存器值即使是同一个值也可能是某种“状态保持”策略MBAP长度字段与后续实际字节数不符属于畸形报文常见于手工构造的攻击载荷TCP连接频繁重建攻击者可能需要不断重连才能发送下一组指令这一点与正常上位机长连接有明显差异。这些行为不一定单独指向攻击但组合出现时分析人员应提高警惕。取证报告里也应单独列一节“异常行为描述”把时间、报文特征、可能的解释写清楚。4.4 从寄存器值到工程值翻译的边界流量分析能告诉你“哪个寄存器被改成了什么值”但要回答“这个值到底影响了设备的什么动作”还需要PLC组态文件或变量映射表。比如寄存器地址0x0003原始值是00 00十进制0它可能对应罐体液位的设定值0表示液位下限触发排空阀动作也可能只是某个累计器清零影响完全不同。没有组态文件时取证报告只能写“寄存器0x0003被写为0”不能在报告中推测“这导致液位下降”这类结论。可以在分析过程中提出假设但正式结论必须区分“已证事实”和“待确认推断”。如果拿到了组态文件还需注意组态地址与协议地址的对应关系。很多组态软件显示的是40001地址偏移而流量里是原始地址换算时一定要小心否则会在报告中写错对象。5. 避坑清单取证中容易翻车的细节5.1 字节序同一个字节序列两种截然不同的结论这是Modbus取证中最容易翻车的点。前面已经提过12 34的例子实际操作中还会遇到32位浮点数四个字节的排列顺序在厂商之间差别更大。比如相同寄存器数据3F 80 00 00按大端序解读是浮点数1.0但如果PLC按字交换顺序存储实际含义可能是某个极小值或异常值。因此在确认设备型号和协议实现之前所有数值解读都要标注“待确认字节序”。如果案件影响大建议直接联系厂商技术部门提供寄存器定义文档。5.2 RTU流量与TCP流量的分析差异串口上的Modbus RTU和网络上的Modbus TCP取证方法完全不同。网络侧有IP、端口、TCP状态可以建立完整会话流串口侧没有IP概念只有从站地址无法判断指令来自哪个具体网络节点RTU帧有CRC可用于切帧和判断报文是否被干扰串口服务器或协议网关可能会把RTU封装进TCP报文里既有网络层信息payload里又是RTU格式。很多取证工具默认只解析Modbus TCP遇到RTU-over-TCP时解析不出来需要先用十六进制查看payload手工识别帧边界。判断的关键是payload第一个字节是否像从站地址通常1-255之间随后是否紧跟功能码数据区之后是否有符合CRC规则的校验字节。5.3 事务标识符与单元标识符的核对Modbus TCP里的事务标识符用来匹配请求和响应正常情况下是递增或回绕的。如果某个请求的事务标识符总是0或同一事务ID被反复使用很可能不是正式商用HMI的行为而是一个简单脚本在发送报文。单元标识符用于标识从站但要注意它不一定等于PLC的实际Modbus地址。某些网关设备会把多个PLC映射到一个单元ID上或者把所有站统一映射成1号站。追查具体设备时不能只靠unit_id下结论还要结合IP和网关的转发表来定位。5.4 时间同步缺失时序错乱的根源ICS环境里没有NTP服务、设备时间漂移严重的情况非常普遍。PLC自带的时钟可能和实际时间相差几个小时甚至几天。流量取证依赖抓包机时间抓包机如果没做时间同步拿到的时间戳就不可靠尤其需要注意。我在现场的做法是在分析机抓包的同时用一台支持GPS或NTP的时间源对时并记录PLC面板显示的本地时间与标准时间的差值。报告里的所有时间线都注明“以抓包机UTC时间为准”避免用设备自带时钟对时间线产生误导。5.5 源IP并不是“幕后黑手”的全部答案Modbus TCP是明文协议源IP可以被伪造攻击者也可能通过内网跳板机对PLC发指令。流量里的源IP只是一个网络端点不等于操作者身份。要想确认到底是谁在控制还需要结合主机取证、进程分析、登录日志和内存取证综合判断。比如热词里提到的netscan内存取证、Volatility可视化内存取证就是用于从主机内存里找关联网络连接的证据。如果流量显示某台工程师站发起了写PLC请求内存取证又在该主机里找到了与攻击脚本匹配的进程网络连接这才算把“人—机—网—设备”的链条完整串起来。6. 工具链、多维取证串联与学习路径6.1 一套够用的Modbus取证工具链真正做Modbus取证分析免费工具完全够用。以下是我日常使用的组合抓包采集dumpcap、tcpdump稳定且开销低协议分析Wireshark、tshark自带Modbus解析器报文复现pymodbus、modbus_tk用Python搭建模拟从站复现攻击指令确认设备逻辑影响文件转换与切分editcap、mergecap内存取证Volatility或Volatility 3配合netscan等插件从主机内存中提取网络连接主机痕迹Windows事件日志、远程桌面记录、进程执行历史、最近打开文件列表等。用tshark可以直接把Modbus请求导出成可读表格tshark -r capture.pcapng -Y modbus.func_code 6 \ -T fields -e frame.time -e ip.src -e ip.dst \ -e modbus.unit_id -e modbus.reg -e modbus.value这样得到的结果是时间、源、目标、从站、寄存器地址、写入值的透明列表后续写报告时可以直接引用。6.2 流量、主机、内存三个方向如何串联一次完整的工控事件取证很少只靠流量一项就能收尾。建议这样串联流量取证回答“设备发生了什么”哪个寄存器被读、被写何时发生主机取证回答“谁在什么机器上做了什么”事件日志、进程启动、远程访问记录内存取证回答“内存中有什么临时线索”正在运行的攻击工具、未落盘的脚本、网络连接的原始状态。我在实际项目中会把三条线汇总成一张时间线表每条记录都标注来源。比如时间来源事件10:02:01PCAP10.10.1.5 对 10.10.2.20 发起写指令10:02:03主机日志10.10.1.5 上 powershell.exe 启动10:02:05内存取证10.10.1.5 内存中发现未保存的modbus_write.py进程这样形成的证据链既有网络侧客观记录又有主机侧的关联痕迹比单条线索更有说服力。6.3 学习路径从模拟环境到真实复盘如果想把Modbus协议和取证技能做扎实我给的建议是第一步搭一个最小模拟环境。用两台虚拟机一台跑pymodbus模拟从站一台用Wireshark抓包同时用modbus_tk发送报文。过程很简单却能直观看到请求和响应的字节结构。第二步练习手工解析。拿到报文后不要只依赖Wireshark先手工把MBAP头、功能码、寄存器地址、数据值拆开再交给工具验证。这个练习做十次以后对协议的理解会有一个质的提升。第三步找公开的Modbus pcap样本做分析。先用过滤器列出所有写入请求再还原时间线最后尝试写出一个完整的分析报告。报告里明确哪些是已证实的事实哪些是推断。第四步收集厂商文档。不同PLC的寄存器定义、字节序、组态地址差异非常大这些知识只能靠平时积累临时抱佛脚很容易出错。最后分享一点体会做Modbus取证这几年我最深的感受是这不是一个拼工具多高级的领域而是一个拼协议熟悉程度和细心程度的领域。很多时候破案的关键就是那一条写指令、那个异常的寄存器地址、那一段本该出现却被抹掉的响应。协议本身不复杂但藏在协议背后的业务逻辑、字节序、时间线、设备映射关系才是真正决定分析质量的东西。真到了事件现场能一边翻报文一边说出“这条写入针对哪个寄存器、影响什么参数、对应设备的哪部分工艺”比任何自动化工具都可靠。希望这篇笔记能帮你跨过最开始的坎少走一些我走过的弯路。