Modbus协议取证实战:从功能码到内存镜像的线索挖掘

Modbus协议取证实战:从功能码到内存镜像的线索挖掘 工控安全这个圈子不大做取证的人更少但这两年找过来问Modbus取证的人明显多了。前阵子处理一个PLC异常动作的案子上位机在事发当晚就被人重启了主机里的实时数据区被覆盖了一部分最后靠交换机镜像口留下的PCAP文件硬是把一条写线圈的08功能码报文翻了出来。整个过程让我意识到Modbus这种老协议在取证这个视角下确实值得单独拉出来理一遍。这篇文章就把我这段时间的学习笔记整理出来从协议底子到流量侧、内存侧、主机侧的具体实践一次性说透。1. Modbus协议底层逻辑从物理接口到数据模型的结构化拆解很多人一上来就抱着Modbus协议规范啃结果啃到一半被RTU、ASCII、TCP三种模式绕晕。我先说一个结论Modbus本质上是一个应用层协议它定义的是数据怎么组织、功能码怎么表达、主从之间怎么交互至于走串口还是走以太网那是下面几层的事情。1.1 主从模型与寻址机制Modbus最核心的模型是主从Master/Slave架构。一台设备作为主站通常是PLC、上位机、SCADA、HMI主动发起请求其余设备作为从站被动响应。主从之间是一问一答一个事务必然由主站发起从站只能应答主动上报数据在标准Modbus里是不被允许的。这一点在取证时非常关键抓到一条从站主动上发的报文本身就是可疑信号。从站地址范围是1到2470是广播地址。一条标准请求报文里从站地址字节决定这条命令发给谁。内部数据寻址则靠数据模型线圈Coil可读可写1 bit按位寻址。离散输入Discrete Input只读1 bit。保持寄存器Holding Register可读可写16 bit。输入寄存器Input Register只读16 bit。这套模型的最大特点是简单但代价是没有任何认证、加密、会话状态。只要是能触达链路的主站就可以直接对从站发起写操作。攻击者在内网拿到一个IP后连猜测通信密钥的过程都省了。1.2 RTU、ASCII、TCP三种封装方式Modbus在链路层的封装方式直接影响报文长度、校验方式和时序搞清楚区别对取证时判断这条流量到底是不是Modbus很有帮助。特性RTUASCIITCP物理层RS-232/485等串行同上以太网报文分隔3.5字符静默时间冒号起始、CR/LF结尾长度字段校验CRC16LRCMBAP头无校验传输效率高低约2倍开销高是否易被中间人截获是总线广播是是RTU和TCP是现在最常用的两种形态。串口上位机如果配置成RTU在总线上抓包时一条命令和一帧响应之间会有严格的时间间隙TCP则要看MBAP头里的协议标识符Protocol Identifier正常情况下固定为0x0000只要看到这个字段不是0基本可以判定为非标准流量或某厂商魔改版本。1.3 功能码是取证地图的第一个标记功能码Function Code是Modbus事务里最直观的行为指纹。读取流程、写入流程、诊断流程都由不同功能码区分。我在学习时把常用功能码整理成了一张速查表取证实战中对着这张表定位异常行为非常顺手功能码含义数据类型读/写典型恶意行为0x01Read Coils线圈读扫描从站资产0x02Read Discrete Inputs离散输入读读取传感器状态0x03Read Holding Registers保持寄存器读读取工艺参数0x04Read Input Registers输入寄存器读读取实时采样0x05Write Single Coil线圈写启停电机、修改阀门0x06Write Single Register保持寄存器写修改设定值0x0FWrite Multiple Coils线圈写批量启停0x10Write Multiple Registers保持寄存器写批量改参数0x08Diagnostics诊断读/写回环测试、清除计数器0x2BEncapsulated Interface扩展读/写设备固件读取等从20年的ICS-CERT告警和MetaSploit的modbus模块来看攻击者最喜欢的组合是先用01/03/04扫描资产再用05/06/10进行篡改。报警阈值可以简单粗暴一点短时间大量读请求的是扫描频率不高但精准命中特定地址的写请求是人或自动化攻击脚本在操作。2. 流量取证实战在PCAP里定位一条非法的功能码请求流量取证是Modbus取证里成果最清晰的路径前提是你手里有流量。无论是交换机镜像口灌出来的还是IDS设备里存储的PCAP只要链路层数据完整就能把上位机的操作行为还原成一条条报文。2.1 Wireshark过滤语法与关键字段定位Wireshark对Modbus TCP的解析相当成熟抓包后的过滤是取证的第一个技能点。几个常用过滤表达式实测下来效率最高# 只看Modbus TCP协议 modbus # 只看某种功能码比如写单个线圈 modbus.func_code 5 # 只看写多个寄存器 modbus.func_code 16 # 只看单元ID从站地址为10的设备 modbus.unit_id 10 # 查看MBAP头的协议标识符正常情况下为0 modbus.proto_id 0 # 查看数据区和寄存器地址 modbus.reference_num 0x100 modbus.word_cnt 4如果一个PCAP文件里Modbus报文量很大先按时间排序再用功能码分组统计通常一眼就能看出规律。正常轮询是周期性读请求节奏稳定异常写入往往夹杂在轮询间隙里时间戳上没有规律功能码也突变。2.2 从PCAP中还原一次非法写入逐字节拆解以16功能码Write Multiple Registers为例我拿一个实际抓包来还原整个报文的取值逻辑。原始数据段黄色高亮部分通常长这样00 01 00 00 00 09 01 10 00 64 00 02 04 00 01 00 02逐字段拆解如下字段值含义事务标识符00 01第1号事务主站侧请求计数协议标识符00 000固定长度00 09后续字段长度9字节单元标识符01从站地址1功能码10写多个寄存器起始地址00 64起始寄存器地址100寄存器数量00 02连续写2个寄存器字节数04后续数据字节数4数据00 01 00 02写入值寄存器1001寄存器1012如果这几条命令把启动压力和温度设定值都改了那从站设备的实际运行状态必然发生变化。取证时可以用数值翻查对应的物理量结合被控设备的DCS趋势曲线或SCADA历史记录进行交叉验证。工具方面我常用tshark做批量导出效率和准确性都让人放心tshark -r capture.pcap -Y modbus.func_code 10 -T fields -e frame.time -e ip.src -e ip.dst -e modbus.unit_id -e modbus.reference_num -e modbus.word_cnt -e modbus.value2.3 异常请求的特征模式识别读流量找规律、写流量找异常这是我在几次实战后总结出的思路。具体来说重点关注这几类无TCP握手直接出现的Modbus报文正常的TCP连接一定会先完成三次握手。如果PCAP里Modbus报文出现时没有SYN、SYN-ACK、ACK过程说明这条流量是被工具直接注入的典型的攻击痕迹。从站主动发起的请求标准Modbus只有主站能发请求。有些工控协议网关或渗透工具会伪装成从站主动发送扫描性报文。抓到这类报文时大概率是蜜罐、扫描器或中间人工具在运行。功能码与地址组合异常比如向只读的输入寄存器区域发起写操作或对非连续地址进行大批量写入。这种操作在上位机组态逻辑里几乎不会出现属于攻击者的盲目尝试。短周期内高频写操作工业过程控制强调稳定性正常运行时的参数调整频率很低。如果某个从站地址在几秒内被写了几十次要么是操作员在紧急抢修要么是自动化攻击脚本在跑。流量侧取证的另外一个价值点是时间戳对齐。以PCAP的纳秒时间戳为准去比对上位机操作系统日志、操作员交接班记录、DCS报警历史能大幅压缩嫌疑人范围。千万不要一开始就盯着报文内容分析先把时间轴拉出来人机交互证据和流量证据对上了案件轮廓立刻清晰。3. 内存取证从RAM里抠出上位机与PLC的交互证据流量缺失是Modbus取证经常碰到的情况。很多中小型工厂根本没有交换机镜像口工控机、HMI一体机上也未必装了抓包软件。这时候内存取证就派上了用场——上位机软件要把Modbus命令发出去必然在某个时刻把报文内容、寄存器地址、写入值放到内存里。拿到内存镜像就等于拿到了运行时的中间状态。3.1 内存镜像获取的常见路径内存镜像获取路径取决于设备运行的系统Windows工控机用FTK Imager、WinPmem或Magnet RAM Capture制作镜像操作时注意先记录系统时间与运行进程列表。Linux部署的网关或数据采集器使用/dev/mem、LiME模块等方式导出。虚拟化场景直接从Hypervisor层面挂起虚拟机导出vmem文件。制作内存镜像时必须遵循先封存后取证的顺序。先不要碰运行中的进程不要关闭弹出的对话框不要在目标机上执行额外程序否则会覆盖掉可能存在的Modbus报文缓存区。3.2 Volatility 2在工控场景下的插件应用提到Windows内存取证绕不开Volatility。工控上位机镜像通常使用Volatility 2Vol2做第一轮分析因为插件生态更丰富、社区资料也更全。# 确认镜像基本信息 volatility -f image.mem imageinfo # 列出进程筛选SCADA/上位机相关进程 volatility -f image.mem --profileWin7SP1x64 pslist # 查看网络连接 volatility -f image.mem --profileWin7SP1x64 netscan # 从内存中提取cmd命令历史 volatility -f image.mem --profileWin7SP1x64 cmdscan在Modbus场景里netscan的输出特别重要。上位机软件的TCP连接记录会在这里暴露出来IP端口连接状态的组合能直接定位PLC的IP和端口号默认502。拿到对端IP后再去匹配流量记录或运维台账就能确定被访问的是哪一台PLC。3.3 提取报文字节串与协议痕迹Volatility本身没有针对Modbus协议的原生插件但可以通过字符串提取和十六进制转储来还原报文。我常用的流程是这样# 提取整个镜像中的ASCII字符串 strings image.mem mem_strings.txt # 在字符串中搜索Modbus功能码和503端口等特征 grep -E 502|modbus|功能码 mem_strings.txt # 针对目标进程导出其内存空间 volatility -f image.mem --profileWin7SP1x64 memdump -p 1234 -D dump_dir/ # 对导出的进程内存在十六进制视图里搜modbus关键值 xxd dump_dir/1234.dmp | grep -A 10 0010 0064有一次我在一个网关的内存dump里直接搜到了0110006400020400010002这串十六进制正好和PCAP文件里的写寄存器报文一致等于拿到了双重印证。内存里报文缓存存在的时间窗口通常只有几秒到几分钟但对取证来说这个痕迹本身就是上位机确实发生过这次操作的直接证据。3.4 Vol2可视化内存取证GUI的辅助价值很多同行不习惯纯命令行操作尤其面对多台工控机批量镜像时命令行效率低且容易出错。Vol2可视化内存取证GUI比如Volatility Workbench或VolUtility这类封装工具把这步简化了不少加载镜像后自动识别profile进程列表、网络连接、DLL列表都以表格形式呈现双击任意进程就能导出内存空间。个人经验是可视化工具适合初筛和汇报展示正式出报告时还是要回到命令行工具交叉验证。GUI封装了一层代码逻辑有时对异常镜像的处理不够透明遇到解析报错时不好定位问题。4. 主机取证Windows工控上位机里的痕迹专项梳理Modbus取证不能只盯着报文和内存。上位机本身是人和协议之间的桥梁人员操作、软件配置、历史记录都会在主机上留下痕迹。Windows主机安全痕迹排查与取证是工控事件响应里的基础课但Modbus这个场景又有一些独特的检索重点。4.1 组态软件与Modbus客户端的历史记录绝多数上位机软件都有操作日志或通信日志只是日志路径各异取证时不能只会翻Windows事件查看器。以常见的组态软件为例Kepware / OPC UA Gateway在安装目录下的日志文件里记录设备通信状态、读写操作、连接断开时间。TIA Portal / Step7PLC程序的上装下载记录、在线诊断缓冲区和项目历史版本会留下Modbus通信配置。自带第三方Modbus调试工具的PC像Modbus Poll这种工具会自动保存最近打开的配置文件和寄存器轮询记录这些文件里的地址表与轮询周期设置同样是判断操作人员是否绕过组态软件直接操作PLC的证据。筛选文件时不要只搜用户目录整个磁盘的文件都值得过一遍。用Everything这类工具按时间排序把事发前后半小时内被创建或修改的文件全部列出来很多直接线索会自己跳出来。4.2 Windows注册表与快捷方式痕迹Modbus上位机软件通常会在注册表里写入串口参数、以太网连接参数、最近打开的项目路径等信息。用RegRipper或手动分析以下路径HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBFTDIBUS和USB枚举项能看出设备是否接了USB转串口线这往往是测试人员或攻击者接入调试工具的关键证据。RunMRU里出现过plc_tool.exe或modbus_poll.exe基本可以断定有人在主机上手工运行过Modbus调试工具。4.3 跳板排查从主机日志还原攻击链路如果主机是被入侵后作为跳板对内网PLC发起Modbus命令的需要把多种日志联合起来分析Windows安全日志Security.evtx登录类型、登录IP、账户权限变化PowerShell脚本块日志ScriptBlockLog很多攻击者用PowerShell建立TCP连接、发送二进制Modbus报文Prefetch文件可执行程序运行痕迹定位plc attack类工具的首次运行时间浏览器历史记录攻击者下载漏洞利用或Modbus工具库时会在浏览器缓存里残留。我遇到过一条完整链路是这样的攻击者通过RDP暴力破解进入Windows 7工控机在PowerShell里使用.NET的TcpClient类向PLC的502端口发送了写寄存器报文随后清除了一部分日志。但PowerShell脚本块日志默认开启状态加上内存镜像里的.NET类型加载记录还是把整个操作还原了出来。PowerShell的TcpClient使用方法在社区里到处都有检测手段却不能只靠一种多层日志交叉关联才是稳妥的办法。5. RTU模式下的取证差异时序、CRC与物理层溯源Modbus RTU虽然在逐渐被TCP取代但存量设备依然庞大特别是伺服电机控制、变频器、智能仪表这类单机设备控制命令基本都是RTU模式。RTU取证和TCP取证有几个本质差异展开说说。5.1 RTU报文的时序特征与帧界定RTU报文没有长度字段也没有像TCP那样的边界标记帧与帧之间靠静默时间分隔。标准规定两个帧之间至少有3.5个字符时间的静默期波特率9600bps时1个字符时间约1ms3.5字符时间约3.5ms。抓包时如果连续出现间隔不足3.5字符时间的报文可以怀疑有人在用非标准工具注入伪造帧。RTU报文结构如下[从站地址 1字节] [功能码 1字节] [数据 N字节] [CRC低 1字节] [CRC高 1字节]CRC16的计算规则是多项式0xA001从站收到帧后会用同样的算法校验。取证时如果想验证一个伪造帧是否有效直接在Python里算一遍CRC就行。5.2 串口总线上抓到可疑帧的实际案例有一次在伺服电机控制现场排查异常动作总线上用串口探针抓包。正常控制逻辑是主站每50ms发一次速度设定功能码06从站返回相同值。异常时段里出现了一条目标地址相同但寄存器值突变的报文CRC校验无误时间间隔也符合RTU规范说明这个报文是合法工具发出来的不是噪声干扰。顺着寄存器地址查组态软件发现那个地址在项目里映射的是电子齿轮比参数。有人通过Modbus RTU调试工具直接改了这个参数导致伺服驱动器的实际转速和设定值不一致。由于RTU总线本身是广播式共享链路只要物理接入总线就能监听和注入这个特性决定了RTU场景的取证重点不是谁通过IP访问了PLC而是谁物理接触了这条总线。5.3 物理层溯源与监控摄像头联动RTU取证不能只停留在协议层物理层溯源同样重要。排查思路通常包括查看串口服务器的连接日志确认哪些设备在事发时段处于活动状态查看PLC或网关的串口扩展模块是否有非计划内的RS485总线接入和现场监控摄像头的时间轴对齐看事发时段是否有人员携带笔记本电脑靠近控制柜。我见过一个案子就是靠监控画面看到某个工程师在事发前20分钟把笔记本接到了控制柜的维修口再配合串口服务器日志和修改后的寄存器值才把整个事实链闭合。协议取证给出的是发生了什么物理层溯源才能回答谁干的。6. 取证实践中的坑误报、丢包与时间同步问题再扎实的方法论放到真实工业现场也会遇到各种干扰。这些年踩过的坑挑几个最典型的写下来。6.1 误把正常轮询当扫描Modbus主站在启动时会枚举所有从站短时间内向地址1到247逐个发送读请求。这个行为看起来很像资产扫描但它其实是正常的站点初始化流程。区别方式正常轮询的间隔非常规律几乎是固定周期扫描行为的间隔则会随网络延迟和工具实现抖动。另外某些自控系统在上电时会执行一次全量数据同步短时间读大量寄存器看起来像数据爬取其实是系统设计的一部分。取证人拿到PCAP后先和工控系统的组态工程师确认一次上电初始化流程是怎样的能省去大量无谓分析。6.2 交换镜像口丢包导致报文不完整不少工厂的交换机是老旧百兆设备镜像口带宽不足时高流量下丢包严重。丢了报文意味着时间线上出现空洞取证分析时就要特别小心。处理这类问题的原则抓包前确认镜像口带宽和实际流量峰值必要时干脆用TAP设备代替镜像口PCAP时间戳存在跳变时先用tshark检查是否有TCP序列号断裂关键时段缺包的优先从内存取证的主机侧痕迹补位做笔录时明确标注哪些流量是可能缺失的避免过度解读。6.3 系统时间偏差导致的证据时间轴错位工业现场很多工控机的时钟并不同步有些甚至慢了几小时。内存取证时从内核时间戳还原出的时间和PCAP时间对不上非常常见。解决办法取证开始前拍照记录目标系统时间与标准时间对比偏差值从事件日志中搜索NTP同步记录或时间修正事件分析时把所有证据统一换算成UTC时间轴而不是直接对比本地时间。我在处理一起跨区域事件时上位机时间和DCS历史站差了整整47分钟一开始以为不是同一事件换算成UTC后才发现时间轴完全吻合。这个坑如果踩了很容易导致错误结论。关于取证边界的一点个人体会做了几轮Modbus取证实战和复现之后我的一个切身感受是证据链的交叉印证远比单点深挖重要。流量报文证明发了什么内存镜像证明哪个进程发的主机日志证明谁在什么时候操作的物理层信息证明谁在物理上接了线。四条线里能对上两三条这个案子的证据链才算站得住。另外也想提醒一句Modbus本身是个没有任何安全机制的老协议取证能做的更多是事后还原和事件响应而不是实时防御。真正想提高工控系统的安全性还是要在网络边界、访问控制、串口管理和入侵检测上多下功夫。取证不是为了让攻击者无处遁形而是为了让事件响应团队在最短时间内恢复现场并还原真相。后面我会继续整理一些针对特定工控协议比如S7comm、EtherNet/IP、PROFINET的取证案例到时候再和大家细聊。