
搞工控的人天然会对Modbus有感情。这门上世纪70年代末诞生的协议到今天依然是PLC、仪表、传感器之间通信的“通用语”。可正因为太老、太开放它也成了工控安全取证里绕不开的焦点一旦现场控制系统被入侵、PLC被篡改、上位机被操控第一件事就是从协议报文和主机痕迹里还原出攻击者到底做了什么。我写这篇学习笔记就是想把自己在Modbus协议逆向和电子数据取证方面的思路整理清楚从协议原理到流量抓取再到内存排查尽量串成一条能落地的工作流。不管你是做等保测评、工控应急响应还是单纯想理解工控协议背后的安全逻辑这篇笔记都有参考价值。1. Modbus协议核心知识梳理1.1 协议家族与三个常用变体Modbus本质上是一套应用层报文规范定义的是“主站怎么问、从站怎么答”。实际传输时根据不同物理介质和封装方式分化出三种最常见的变体Modbus RTU、Modbus ASCII、Modbus TCP。Modbus RTU基于串口RS-232/RS-485报文是二进制编码效率高是工业现场最常见的形态。一帧报文包含从站地址、功能码、数据、CRC校验。Modbus ASCII同样基于串口但把每个字节编码成两个ASCII字符肉眼可读性好但效率低。通常出现在老旧设备或某些需要人机交互的调试场景。Modbus TCP基于以太网使用TCP/IP传输端口默认502。它在RTU基础上增加了MBAP报文头去掉了地址和CRC因为TCP自身保证可靠传输直接用功能码和数据进行读写。取证时最常碰到的是Modbus TCP和Modbus RTU。前者抓包容易、数据量大、结构清晰后者需要串口监听往往还要物理接触设备或总线。1.2 报文结构与关键字段解析搞清楚报文结构是取证的第一步。我们分别看TCP和RTU的关键字段。Modbus TCP报文结构Modbus TCP MBAP头 PDU功能码 数据。MBAP头共7字节事务处理标识符2字节用于匹配请求和响应。每次请求递增响应时必须原样返回。协议标识符2字节0表示Modbus协议。长度2字节后面所有字节的长度。单元标识符1字节相当于RTU里的从站地址用于标识网关后面的设备。PDU部分功能码1字节决定操作类型比如03读保持寄存器、06写单个寄存器、16写多个寄存器。数据长度不定寄存器地址、数量、字节数、寄存器值等。举一个典型报文请求00 01 00 00 00 06 01 03 00 00 00 0A意思是事务ID1协议ID0长度6单元ID1功能码03起始地址0x0000读10个寄存器。响应则会把第13个字节置为读取的字节数后面跟着各寄存器的高低位数据。Modbus RTU报文结构RTU帧结构从站地址1字节 功能码1字节 数据N字节 CRC校验2字节低字节在前。这里没有长度字段靠帧间空闲时间一般3.5个字符时间分隔帧。所以抓RTU串口数据时时间戳尤为重要不能简单拼接要按间隔切分。1.3 功能码与攻击行为映射功能码是判断操作意图最直接的依据。取证时我会把功能码分成四类看待分类功能码典型操作恶意场景映射位读写01、05、15读线圈、写单线圈、写多线圈突然写入大量线圈状态可能用于启停电机或开合阀门寄存器读写03、04、06、16读保持/输入寄存器、写单/多寄存器篡改过程值设定比如修改温度设定点、速度给定量文件/诊断07、08、11、17读异常状态、诊断、读事件计数、报告从站ID攻击者侦察设备类型和状态信息封装接口43MEI设备信息读取读取设备型号、固件版本用于针对漏洞实际攻击中最经典的组合是“扫描 读寄存器 写寄存器”。攻击者先用03功能码批量读取寄存器了解工艺参数然后对关键寄存器用06或16功能码做篡改。取证时要盯住时间上有成群03请求之后突然出现06/16写操作的流量。2. 取证前的准备工具选型与环境搭建2.1 流量抓取工具链工控流量取证Wireshark是绝对主力。它内置了Modbus TCP和Modbus RTU的解析器能直接把报文拆成字段省去手工算字节的痛苦。我常用的组合是tcpdump在Linux主机上长期抓流量的轻量工具输出pcap文件。Wireshark离线分析pcap配合显示过滤器快速定位Modbus流量。tsharkWireshark的命令行版本方便批量提取字段比如把功能码、寄存器地址直接导出成CSV。抓包位置也有讲究。如果是取证现场优先在交换机的镜像端口抓镜像流量或者直接在工控主机的网卡上抓包。不要随便在工业环网的设备上启动抓包可能会影响实时通信。2.2 协议模拟与测试工具理解协议最好的方法是自己发报文。Modbus Poll和Modbus Slave是一对经典工具Modbus Poll主站模拟器安装后配置从站IP/串口参数、功能码、寄存器地址范围。它可以周期性发送读请求也可以手动写单个寄存器。关键点工具本身有授权限制但很多场景用demo模式也能工作。取证时用它来验证某个设备是否响应特定功能码能快速判断设备在线状态和寄存器可访问性。常用配置项Connection选择TCP/IP或RTU串口。Slave ID默认1。Function03读保持寄存器、04读输入寄存器等。Modbus Point Type保持/输入寄存器。Address和Quantity从哪个地址开始读读多少个。Poll Definition的Delay轮询间隔单位ms。取证时为了不干扰现场间隔设大一些比如1000ms。Modbus Slave从站模拟器用来模拟PLC或传感器响应。取证中最常见的用途是搭建一个蜜罐或仿真环境看到底会收到什么样的写指令。也能用来验证你抓到的流量是否正常——把响应数据和你自己模拟的设备寄存器做对比可以反推原始值。这两个工具都有试用版平时练习完全够用。网上流传的所谓“注册码”其实没必要去折腾重装系统或换MAC后就失效不如直接申请正版试用避免引入恶意软件风险。2.3 内存镜像获取工具当取证对象是一台Windows工控上位机时内存取证能拿到很多静态文件拿不到的东西加密前的串口数据、TCP会话状态、Modbus Poll进程内部的寄存器缓存。常用工具FTK Imager可以制作物理内存镜像操作简单支持导出进程内存。MemDump轻量级内存导出工具能在被取证机器上快速执行。Volatility 3内存分析框架可以列出进程、网络连接、DLL列表还能用插件扫描特定结构体。对于Modbus相关进程我们需要关注的进程名通常是Modbus Poll、Modbus Slave、组态软件如WinCC、Kepware等。内存取证要注意时效性。镜像必须在关机前抓取而且要尽量使用写保护设备比如用专用U盘启动或通过远程采集防止覆盖现场数据。3. 流量取证实操从PCAP到线索链3.1 定位Modbus流量拿到一个大pcap不要直接翻。先用统计功能过滤在Wireshark里用显示过滤器modbus直接把所有含Modbus TCP的报文过滤出来。如果还有RTU通常没有标准解析层需要先通过“Decode As”把端口或串口数据映射为Modbus TCP协议或者通过原始字节特征手动定位。更精确一点的过滤只看请求modbus tcp.len 0 tcp.dstport 502只看响应modbus tcp.len 0 tcp.srcport 502区分功能码modbus.func_code 3或modbus.func_code 16另外可以用tshark快速导出所有Modbus请求tshark -r capture.pcap -Y modbus tcp.dstport502 -T fields -e frame.time_epoch -e ip.src -e ip.dst -e modbus.func_code -e modbus.reference_num -e modbus.word_cnt -E separator, modbus_req.csv导出后再用Excel透视表很快就能看到谁在问谁问了哪些地址。3.2 提取功能码与寄存器操作序列功能码序列是还原攻击手法的关键。我会分三步处理第一步提取请求-响应对。用Wireshark的“Packet Bytes”里的响应事务ID和请求事务ID做关联。通常从站IP会响应主站请求事务ID相同。第二步把寄存器读写行为按时间排序形成时间线。重点关注三类事件同一时间窗口内大量读请求覆盖了很广的寄存器范围且这些请求来自非工控主机IP——这通常是扫描。对单个或多个连续寄存器执行写操作功能码06或16且写入数值与正常工况差异明显——比如一个本来应该在0~100之间波动的寄存器突然被写成65535。写操作之后紧跟设备重启或心跳丢失——说明操作可能触发了设备异常。第三步计算寄存器地址和数值的物理含义。Modbus的寄存器地址往往和点表有关比如4x0001表示开始保持寄存器地址0。如果拿到了现场的点表就可以直接把寄存器号映射到物理量精度更高。示例抓到一个写请求地址0x000B数据0xFFFF。点表上0x000B对应“急停状态寄存器”正常值为00xFFFF代表急停触发。这个写操作就是攻击的直接证据。3.3 识别异常操作与恶意行为流量里不是所有写操作都是恶意必须结合上下文判断。我整理了一套快速判断维度判断维度正常操作特征可疑特征通信源固定上位机IP或HMI IP陌生IP、跨网段扫描、多个IP轮询频率周期性轮询或手动操作高频请求、大量重复请求、无轮询规律的突发功能码分布以03/04为主大量01/05/15/06/16尤其集中在短时间内寄存器范围窗口相对固定遍历大量地址区间或精准命中关键寄存器数值特征在合理工艺范围内0xFFFF、0x0000、极大极小值、浮点反转另外注意一下Modbus诊断功能码08它可以用来执行Modbus协议层面的回环测试。攻击者会先用08子功能码探测设备在线状态和通信质量再展开更深入的攻击。抓包时如果看到大量08请求从陌生IP发出基本可以判定为侦察行为。4. 内存取证与主机痕迹排查4.1 内存镜像中的Modbus会话恢复流量取证能覆盖网络侧但网络侧往往只看到加密或封装后的数据或者根本抓不到串口流量。这时候内存镜像的价值就体现出来了。以Windows上位机为例内存里通常会保存TCP socket缓冲区中未消费的Modbus报文。Modbus Poll进程堆中缓存的寄存器值。组态软件如Kepware、WinCC的OPC连接状态和标签数据。最近执行的Modbus命令在进程内存中的残留。使用Volatility 3分析时我最常用以下插件组合vol3 -f mem.raw windows.pslist vol3 -f mem.raw windows.netscan vol3 -f mem.raw windows.dlllist --pid ModbusPoll_PID vol3 -f mem.raw windows.cmdline vol3 -f mem.raw windows.memmap --pid ModbusPoll_PID --dumpnetscan能看到TCP连接表里还残留的502端口连接状态包括本地IP、远端IP、连接状态。这能直接证明某台主机在某个时间点与某台PLC建立了Modbus会话。dump下来的进程内存用strings命令搜索Modbus相关的可读信息。比如strings -el m.poll.exe.dmp | grep -i modbus\|register\|coil\|holding如果是UTF-16编码的字符串需要用strings -el选项。还可以直接搜索十六进制特征串比如03功能码读请求的报文字节。不过现代操作系统内存碎片化严重建议先把进程内存导出然后在导出文件中搜索。小技巧如果内存镜像里有Modbus Slave模拟器它的寄存器缓冲区和网络收发缓冲区大概率直接存在连续内存里。找到进程数据段后能直接dump出当时它缓存的一整块寄存器表。4.2 主机侧Modbus客户端痕迹分析主机侧痕迹主要看三块最近打开的文件PLC点表、组态工程文件路径可能在%USERPROFILE%\Recent或组态软件的“最近文件”里。软件安装和运行日志Modbus Poll的配置文件一般写入注册表或者应用目录下的INI文件里面记录了从站IP、功能码、寄存器地址范围、轮询间隔等。这些参数在取证时非常有用。通信日志很多上位机软件会留有内部日志比如WinCC的报警记录或Kepware的通道诊断日志。这些日志通常包含通信失败、设备掉线、异常写入记录。我个人习惯按这个顺序搜集提取Prefetch文件Windows%SystemRoot%\Prefetch里能看到Modbus工具最近执行的次数和时间。提取Amcache/最近打开文件夹记录使用MFTECmd或KAPE解析能还原出用户把点表文件拷到哪个目录。检查应用配置文件Modbus Poll默认会在安装目录或AppData\Roaming下生成配置解析时直接看里面保存的从站地址和功能码就相当于拿到了攻击目标清单。4.3 时间线关联与还原攻击路径流量、内存、主机痕迹都是“点”时间线串联才能变成“线”。我的做法是定义三个维度的时间线网络维度由pcap提取的每一次Modbus请求/响应时间。系统维度主机上的登录事件Event ID 4624/4625、进程启动时间、文件访问时间。控制维度PLC侧报警记录、工艺参数变化记录如果PLC有日志功能。在这三个时间线里寻找“交叉点”。比如10:00:15MemDump捕捉到Modbus Poll进程正在运行配置了对寄存器0x000B的写操作。10:00:20网络抓包显示从该主机IP发往PLC的写请求功能码06寄存器0x000B数据0xFFFF。10:01:05PLC侧记录急停信号触发。这三个时间点紧密关联就形成了完整的证据链。实战中还会有一种情况攻击者用了合法的组态软件比如用了工程师的笔记本这时主机侧的用户行为痕迹比流量更重要。通过解析Chrome历史记录、最近访问的共享路径、MSTSC连接记录往往能还原出攻击者当初是怎么进入该主机、从哪里下载了工具、访问了哪个共享目录里的点表。5. 常见问题与排查技巧实录5.1 取证中的字节序陷阱Modbus的寄存器数据有两种字节序大端高位在前和小端低位在前很多协议解析工具默认按大端显示但不同PLC厂商可能默认是小端。比如寄存器地址0x1000数据字节为01 02 03 04在Wireshark里显示的是0x01020304但如果目标是西门子PLC实际可能被解释为0x04030201。排查技巧对照抓包中的原始字节和组态软件的点表定义。如果发现数值明显不合理比如一个温度寄存器读出来是1667457891这种巨大整数先考虑字节序是否反了。取证报告里必须注明字节序规则否则证据数据可能被质疑。另一个坑是“寄存器地址偏移”。有些设备是零基址有些是一基址比如点表写4x0001实际Modbus报文里的地址是0x0000。计算时统一减去偏移量否则最终提取的物理量会差一个寄存器。5.2 工具配置易错点集合Modbus Poll连接不上从站的排查从站IP和端口是否配置正确默认端口502。协议族是TCP还是RTU over TCP有的网关设备要求使用RTU over TCP封装不能直接用Modbus TCP。Slave ID是否对应很多网关的单元标识符不是1尤其当网关下挂多个从站时要填对应的单元ID。功能码和地址范围是否越界有些PLC只支持有限范围读一次是没问题但连续轮询可能会让从站出错。Wireshark里Modbus报文解析不出来常见原因是端口不是502需要手动指定Decode As为Modbus TCP。如果是RTU over TCPWireshark不会自动解析需要选择“Modbus RTU over TCP”或者用analyse里的“Decode As”强制转换为Modbus TCP。CRC错误导致RTU帧不完整通常是抓包时串口配置的奇偶校验和停止位不对抓包工具和实际设置不一致。内存取证时strings搜不到Modbus关键字可以先检查dump是否完整然后尝试搜索进程的堆标记或网络缓冲区地址。另一个原因是软件把字符串做了UTF-16编码记得用-el参数。5.3 关键证据保全细节取证不只是技术更是规范和细节。这里分享几个我踩过坑后的经验证据时间必须统一。现场设备时间和取证主机时间可能差很多抓包前先记录各设备的时间和时区之后所有分析结果都要换算到UTC或同一基准否则时间线对不上。镜像之前先哈希。给原始pcap和内存镜像先算MD5/SHA256保证后续分析不改变原始证据。抓包别开混杂模式以外的过滤。有些工程师为了让抓包“干净”会先过滤掉广播包结果把关键的手动写操作也滤掉了。建议现场优先抓全集分析再做过滤。对PLC做在线取证时千万不要执行写操作。即使是为了测试也不要对生产设备发写指令否则可能造成工艺事故。只读诊断和读取寄存器相对安全但也要评估负载。所有分析工具最好用便携版不要给被检机器安装任何东西避免破坏现场。如果条件允许尽量对工控网络做旁路镜像平时就保留一个长期运行的抓包系统。这样事发后拿到的不仅是一个可疑时间段的pcap而是一整段时间的基线数据对比起来更有说服力。写在最后我在实际取证项目中最深的体会是Modbus这种老协议之所以到今天还能主导工控通信是因为它足够简单可靠。但它的简单也意味着几乎没有安全防护没有认证、没有加密、没有权限分级。做取证的人恰恰要利用这份“简单”把报文的每一个字节都当作线索把功能的每一种调用都看作动作把进程的每一次内存变化都视为记录。掌握了协议就等于掌握了现场的“通用语言”剩下的匹配和重组拼的就是细心和耐心。最后分享一个小技巧如果遇到一次攻击行为跨越了网络和主机的复杂场景不要急着找结论先把所有关键事件按时间排序做成CSV再用Excel透视表按源IP和功能码分组往往攻击模式会自己浮现出来。取证这条路上工具只是辅助真正的“破案”靠的是不停地假设、验证和跟真实系统的对照。希望这些笔记能对打算入门或正在做工控取证的你有所帮助。以后碰到Modbus相关的现场记得先深呼吸从协议开始拆解很多难题都会迎刃而解。