Modbus协议分析实战:从Wireshark抓包到CTF解题
1. 项目概述为什么Modbus协议分析是CTF与工控安全的必修课如果你接触过工业控制系统ICS安全或者网络安全竞赛CTF那么“Modbus”这个名字你一定不陌生。它不像HTTP、TCP/IP那样家喻户晓但在工厂车间、发电站、楼宇自动化这些地方Modbus协议就像空气一样无处不在默默地指挥着PLC可编程逻辑控制器读取传感器数据、控制阀门开关。然而正是这种“沉默的基石”角色让它成为了网络安全中一个既经典又充满挑战的分析对象。很多初入工控安全领域的朋友或者在做CTF中Misc杂项、Forensics取证类题目时一看到捕获的Modbus数据包就感到无从下手密密麻麻的十六进制字节流完全不知道从哪里开始解读。这正是我写这篇实战指南的初衷。我不打算给你罗列枯燥的协议规范文档而是带你亲手走一遍从“抓包”到“解题”的完整流程。我们会使用最经典的网络分析工具Wireshark像侦探一样一层层剥开Modbus数据包的外壳理解每一个字段的含义。更重要的是我会结合真实的CTF题目场景分享如何从看似杂乱的数据中快速定位关键信息找到隐藏的“flag”。无论你是想入门工控协议安全分析还是希望在CTF比赛中多掌握一项得分技能这篇内容都将提供可直接“抄作业”的步骤和思路。你会发现一旦掌握了方法Modbus协议分析其实就像解一道有固定公式的数学题清晰而有趣。2. Modbus协议核心原理快速解析在动手抓包之前我们必须先理解Modbus协议到底在“说”什么。否则面对数据包我们看到的只是一堆无意义的数字。Modbus本质上是一种非常简单的“主从式”问答协议。想象一下课堂提问老师主站点名叫“张三”从站地址然后发出一个具体问题功能码比如“请告诉我你的年龄寄存器地址和数量”。张三听到后就会回答“我18岁”返回的寄存器数据。整个对话结构固定没有复杂的寒暄。2.1 协议运行模式与数据单元Modbus主要有三种运行在以太网上的变种Modbus TCP、Modbus over TCP有时不规范地等同前者、以及Modbus RTU over TCP。在CTF和网络分析中我们遇到的基本都是Modbus TCP因为它直接基于TCP/IP易于在标准网络环境中捕获和分析。一个完整的Modbus TCP应用数据单元ADU由两部分组成MBAP头Modbus Application Protocol Header7个字节专为TCP封装添加用于事务处理、协议标识等。PDUProtocol Data Unit这是Modbus协议的核心由“功能码”和“数据”两部分构成。理解这个结构至关重要因为Wireshark的解析和我们的分析都是基于此展开的。MBAP头让Modbus能在TCP连接上复用对话而PDU则承载了真正的操作指令。2.2 关键字段详解功能码与地址PDU里的“功能码”是协议的灵魂它决定了这是一次什么类型的操作。对于分析而言最常遇到的是这几个0x01 (Read Coils)/0x02 (Read Discrete Inputs)读取开关量1位状态如开关通/断。0x03 (Read Holding Registers)/0x04 (Read Input Registers)读取寄存器16位数据如温度、压力值。0x03是最最常见的用于读取保存在从站设备里的数据。0x05 (Write Single Coil)/0x06 (Write Single Register)写入单个开关量或寄存器。0x10 (Write Multiple Registers)写入多个寄存器在需要修改一段数据时出现。地址域则告诉从站具体要操作哪个数据点。这里有一个极易混淆的坑协议文档中的地址通常是“从0开始的偏移量”。但很多设备厂商特别是PLC编程软件在界面上展示的地址是“从1开始的编号”或者是带了前缀的如“4xxxx”代表输入寄存器。在分析数据包时Wireshark展示的通常是协议原始地址从0开始。如果你在题目中看到类似“读取40001寄存器”的描述需要意识到其协议地址可能是0。这个转换是解题的关键一步我后面会用一个具体例子说明。3. Wireshark抓取与解析Modbus流量实战理论说得再多不如动手抓一个包看看。这里我分享从环境搭建到过滤分析的完整流程。3.1 环境搭建与流量捕获纯粹的Modbus设备不容易获取但对于学习和CTF练习我们完全可以用软件模拟。一个经典的组合是Modbus Slave模拟从站扮演一个PLC里面定义了线圈、寄存器的值。Modbus Poll模拟主站扮演上位机SCADA/HMI向从站发起读写请求。Wireshark在本机网络接口上捕获两者之间的TCP通信。你可以在同一台电脑上运行这三个软件。启动Modbus Slave创建一个TCP从站定义几个寄存器的值比如在地址0存放1234地址1存放5678。然后启动Modbus Poll创建连接指向本机127.0.0.1和从站端口默认502配置一次读取多个寄存器的请求。此时打开Wireshark选择正确的网卡如果是本机通信选“Adapter for loopback traffic capture”或类似的环回接口开始捕获。在Modbus Poll中执行一次“连接”和“读取”你就能在Wireshark中看到清晰的Modbus TCP数据包了。注意有些CTF题目会直接提供一个.pcapng或.pcap抓包文件这就是你的“犯罪现场”记录。我们不需要自己抓但要学会在这个“现场”中寻找线索。3.2 Wireshark中的Modbus数据包解剖捕获到数据包后在Wireshark主界面找到Modbus TCP协议的数据包通常使用TCP端口502。选中一个中间的面板会显示分层的协议解析。关键看这两层Transmission Control Protocol这是TCP层关注源端口和目的端口502。Modbus/TCP这就是Wireshark解析后的Modbus协议层是我们分析的核心。展开Modbus/TCP协议树你会看到清晰的结构Transaction identifier事务ID用于请求响应配对。Protocol identifier协议IDModbus TCP固定为0。Length后续字节长度。Unit identifier从站地址在TCP中常用来标识后端设备。Function code功能码例如3: Read Holding Registers。Reference number起始地址协议地址从0开始。Word count要读取的寄存器数量。如果是响应包下面还会有Byte count和Register value以十六进制形式显示读取到的数据。一个至关重要的技巧Wireshark的Register value字段默认显示的是十六进制。但CTF题目中隐藏的信息可能是ASCII字符、十进制数字、或者需要转换的格式。你需要熟练使用Wireshark的“数据包字节”面板最下面那个结合协议树中的地址和长度手动提取和转换原始字节。3.3 高效过滤与追踪数据流面对成百上千个数据包如何快速找到关键的那几个Wireshark过滤器是你的神器。modbus显示所有Modbus协议包。modbus.func_code 3只看读取保持寄存器的请求。modbus.regnum16_uint16 0查找操作起始地址为0的Modbus包。tcp.port 502最基本的过滤出Modbus端口流量。更高级的是“追踪TCP流”在包上右键 - 追踪 - TCP流。Wireshark会将本次会话的所有请求和响应按顺序排列并分别用不同颜色标注这对于理解一次完整的读写交互过程非常直观。在流窗口中你还可以选择“仅显示此流”让视图更干净。4. 从协议分析到CTF解题核心技巧与例题拆解掌握了基础分析能力后我们进入最激动人心的环节解题。CTF中Modbus相关的题目通常不会直接问“这个包的功能码是什么”而是把flag或关键信息隐藏在协议通信的数据里。我总结了几种常见的出题套路和破解技巧。4.1 套路一寄存器数据即Flag这是最直接的方式。flag的ASCII字符被直接写入到模拟PLC的保持寄存器中。每个寄存器存2个字节一个字符的ASCII码可能占一个或两个字节取决于编码。解题步骤在Wireshark中过滤modbus寻找Function code: 3 (Read Holding Registers)的响应包。查看响应包中的Register value字段。这些十六进制值可能就是flag的ASCII码。将十六进制值转换为ASCII字符。注意寄存器的顺序大端序和组合方式。例如响应数据可能是 Register value: 666c6167-7b74-6869-732d-6973-2d61-2d74-6573-747d这看起来像是一组组16位数据。你需要将其拼接起来转换成字节再解码为字符串。实际中Wireshark可能直接显示为字节流如 66 6c 61 67 7b 74 68 69 73 2e 2e 2e这就是ASCII “flag{this...”。实操心得不要完全依赖Wireshark协议树里显示的文本有时它的解析可能不完整或格式不对。一定要点开“数据包字节”面板找到Modbus PDU数据部分的原始字节通常是MBAP头之后的部分自己复制出来进行转换。使用Python脚本或在线工具批量转换十六进制到ASCII会更高效。4.2 套路二地址偏移与隐秘写入出题人可能不会把flag放在一次读取中而是分段写入多个不连续的寄存器地址或者利用“写寄存器”功能码06或10将数据先写入再通过其他方式提示你。解题步骤不仅关注读03/04也要关注写06/10请求包。写请求的Reference number和Data字段可能包含信息。注意题目描述中的地址提示。如“flag存储在40010至40020寄存器”你需要将其转换为协议地址。记住40001对应的协议地址是0。所以40010对应协议地址940020对应19。你在过滤时可以尝试modbus.regnum16_uint16 9。可能存在对同一地址的多次写入每次写入一个字符最终拼成flag。你需要按照数据包的先后顺序注意Transaction identifier或TCP序列号将多次写入的数据拼接起来。注意事项Modbus TCP的Unit Identifier字段有时也会被利用来传递信息或者作为某种标识。检查所有非常规字段的值。4.3 套路三异常响应与错误码中的信息Modbus异常响应功能码最高位置1如0x83代表读保持寄存器异常本身可能不是重点但异常响应包中的“异常码”字段或者伴随异常发生的其他正常数据包可能暗藏玄机。有时出题人会设计一种“只有发生特定错误时才会在后续某个数据包中泄露flag”的场景。解题步骤过滤所有功能码大于0x80的Modbus包查看异常类型。分析异常请求前后的数据流看看是否有特殊的数据交互模式。结合数据流的上下文而不是孤立地看一个包。4.4 综合例题解析假设我们拿到一个CTF的抓包文件modbus_ctf.pcapng题目描述为“控制员不小心将巡检口令存入了PLC的保持寄存器中你能从通讯数据中找回它吗”我们的解题流程初步侦查用Wireshark打开文件输入过滤器modbus。快速浏览发现主要是Function code: 3的请求和响应。定位关键响应因为flag可能在寄存器数据中我们重点关注响应包。找一个较大的响应包或者查看TCP流概览。提取数据追踪一个看起来数据量较大的TCP流。在流中清晰地看到一次读取请求Func: 3, Ref: 0, Count: 20。对应的响应中Byte Count: 40后面跟着40个字节的Register Value。数据转换将这40个字节的十六进制值复制出来。假设是 48 65 6c 6c 6f 4d 6f 64 62 75 73 53 65 63 72 65 74 50 61 73 73 77 6f 72 64 31 32 33解码将其转换为ASCII字符串。可以使用Pythonbytes.fromhex(“48656c6c6f...”).decode(‘utf-8’)得到结果HelloModbusSecretPassword123。提交这可能就是“巡检口令”。但CTF的flag通常有固定格式如flag{...}。检查整个字符串或题目描述可能最终flag就是flag{HelloModbusSecretPassword123}或者需要对这个口令进行某种哈希如MD5后再封装。这就需要结合题目其他提示了。避坑技巧在真实比赛或复杂题目中数据可能被编码如Base64、Hex、加密或压缩后存入寄存器。看到寄存器数据是看似乱码的可打印ASCII时要第一时间尝试Base64解码如果是整齐的十六进制对尝试Hex解码。养成“先尝试最常见编码”的条件反射。5. 高级分析与故障排查场景除了CTF在实际的工控安全评估或故障排查中Modbus协议分析也是核心技能。5.1 识别异常与攻击行为通过Wireshark分析可以识别一些潜在的恶意行为或配置错误功能码滥用大量使用05/06/15/16功能码进行写操作可能是攻击者在尝试篡改工艺参数。非法地址访问请求访问的寄存器地址远远超出从站设备定义的范围会导致异常响应。连续的非法访问可能是扫描探测行为。过高的通信频率主站以极快的频率轮询数据可能构成拒绝服务攻击影响PLC的正常控制循环。未授权的主站通过分析源IP地址可以发现网络中是否存在未经验证的主站正在尝试与PLC通信。在Wireshark中你可以使用统计功能Statistics - Conversations - IPv4/TCP查看哪些IP对在频繁通信结合协议过滤快速定位可疑会话。5.2 调试通信故障当上位机HMI无法读取PLC数据时抓包分析是定位问题的黄金标准。检查连接首先确认TCP三次握手是否成功完成。是否有到502端口的SYN包是否有SYN-ACK回复检查请求主站发出的Modbus请求包格式是否正确功能码、地址、长度是否符合从站的支持范围MBAP头中的Unit Identifier是否与从站配置匹配很多故障是因为这里填错。检查响应从站是否回复了如果回复了异常码如0x83根据异常码如0x02非法地址去排查配置。检查数据响应数据是否正确是否与PLC编程软件中监控到的值一致如果不一致可能是字节序大端/小端理解有误或者地址映射方式不对。一个真实案例我曾遇到一个案例HMI显示某个温度值始终为0。抓包发现HMI请求读取地址为0的4个寄存器PLC也正常返回了4个寄存器的数据。但HMI解析时错误地将这4个寄存器8字节当作一个32位浮点数IEEE 754格式来解析而实际上PLC存储的是两个独立的16位整数。通过抓包看到原始数据后问题一目了然最终修改HMI的数据解析配置后解决。6. 必备工具链与脚本化技巧工欲善其事必先利其器。除了Wireshark还有一些工具和技巧能极大提升效率。6.1 辅助工具推荐Modbus Poll / Modbus Slave如前所述学习和测试的黄金搭档。可以用来复现抓包文件中的请求验证自己的分析。scapyPython的强大网络包操作库。当你需要编程解析大量的Modbus pcap文件或者自动化提取flag时scapy是首选。你可以写一个Python脚本自动遍历pcap中的每个包识别Modbus响应提取寄存器数据并转换。from scapy.all import * from scapy.layers.modbus import * packets rdpcap(modbus_ctf.pcapng) flag_data b for pkt in packets: if pkt.haslayer(ModbusADUResponse) and pkt[ModbusADUResponse].funcCode 3: # 读保持寄存器响应 # 提取数据部分需要根据实际层数调整 data pkt[ModbusADUResponse].registerVal # 假设data是Raw层需要转换为字节 if isinstance(data, bytes): flag_data data print(flag_data.decode(utf-8, errorsignore))CyberChef一个万能的网络瑞士军刀网页工具。当你手动分析时可以把Wireshark中复制的十六进制串丢进去轻松进行Hex Decode、Base64 Decode、XOR等各种操作快速试错。6.2 脚本化处理复杂数据对于数据分散在多个请求/响应中或者需要复杂转换如每两个字节交换顺序的情况手动操作极易出错。编写简单的Python脚本是专业做法。思路通常是使用pysharkWireshark的Python封装或scapy加载pcap文件。按照事务ID或TCP流将请求和响应配对。根据功能码和地址按顺序提取数据字段。对提取出的字节流进行所需的解码、解密或重组操作。输出最终结果。这种能力在应对复杂CTF题目和真实安全审计时能节省大量时间并保证准确性。我个人在实际操作中的体会是Modbus协议分析就像学习一门新的“方言”。起初那些十六进制数字令人望而生畏但一旦你理解了它简单的“主从问答”语法和固定的“单词表”功能码剩下的就是耐心和细致的观察。CTF题目往往会把线索放在最显眼的地方——频繁访问的地址、异常大的数据量、或者重复出现的特定字节模式。多练习多动手从简单的题目开始逐渐建立你的分析直觉。最后别忘了在Wireshark里设置一个自己喜欢的配色方案毕竟盯着屏幕找线索的过程也可以是一种享受。

相关新闻