REA驱动的某川PLC私有协议-逆向分析实战
原创
ic3blac4
ic3blac4
ChaMd5安全团队
2026年10月14日 08:00
辽宁
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
诚招web、re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱 [email protected](带上简历和想加入的小组)
1 前言
PLC是工业控制系统的\"大脑\",而上位机编程软件,是攻击者攻击控制器的第一要素。要评估风险,第一步就是把它与控制器之间的通信面摸清楚——尤其是那种没有文档、没有 SDK、没有编程语言绑定的私有协议。
本文的研究对象,是国产某川新发布的中大型PLC的上位机软件IFA与配套仿真控制器之间的私有通信协议。它承载着工程下载、状态查询、运行控制等敏感操作。
笔者此前在CODESYS生态做过较长研究:自2021年起分析CODESYS V2相关产品并发现数十个漏洞,提交厂商后经修复发布;此后又系统梳理 CODESYS V3通信协议的授权机制与漏洞挖掘方法。这些工作沉淀出一条经验:工业协议里的\"私有\",往往只是包装。
而这一次多了一个新变量——REA(Reverse Engineer Anything)。2026年10月,它登上GitHub Trending榜首,公开报道记录其单日新增约3万star,版本号一周内由v4.0连发至v6.0。但它的公开演示大多集中在可自由运行、可反复试错的软目标(Electron 应用、老游戏逻辑重建)。
于是问题被推到台前:把这类工具用到\"不能随意试错、符号常被剥离、且带商业保护\"的工业上位机上,它还能干活吗?
本文给出的答案是:能,而且干得比预想的多。 从\"不知道这是什么协议\"到\"能把它停掉再拉起来\",这套协议的逆向工作几乎全部由 REA 完成;人唯一不可替代的动作,是在组态软件里按了一下\"停止\"、再按了一下\"启动\"。
2 REA是什么
REA 是一层基于 MCP(Model Context Protocol) 的逆向工程编排层。它本身不做反汇编,而是把Ghidra、IDA、Hopper、JADX、Binwalk 等分析引擎统一封装为133个可编排工具,覆盖原生PE/ELF、.NET 托管程序集、Electron/JavaScript、Android APK、固件镜像与各类归档包。
对本文而言,它的两条纪律最关键:
- 定性优先——先回答\"这是什么东西\",而不是一头扎进字节;
- 证据分级——每条结论区分observation(观察)/ inference(推断)/ unknown(未知),保留证据与不确定性边界。
后者与笔者一贯坚持的\"诚实分级\"(CONFIRMED / INFERRED / OPEN)天然一致。文中每个结论也都按此标注。
3. REA干了哪些
与其罗列\"工具能做什么\",不如把研究拆成环节,逐条核对谁在做。如下表所示。
| 研究环节 | REA 做了什么 | 人类做了什么 |
|---|---|---|
| 协议栈身份识别 | 检索符号,秒级命中 Cmp\* 组件名与 .cpp 源码路径 | — |
| 报文格式还原 | 反编译 PDU 解析逻辑,读出四层嵌套与逐字段偏移 | — |
| 载荷编码识别 | 顺符号链定点反编译,确认 IBTag = LEB128 变长编码 | — |
| 会话流程还原 | 静态还原帧构造顺序,与真实抓包逐帧对照 | 提供抓包环境 |
| 命令字表提取 | 扫34个 sendRequest 调用点,全量导出命令字 | — |
| 校验字段推导 | 定位CRC32调用位置与输入范围,导出待验证假设 | 脚本暴力拟合验证 |
| 启停方向裁决 | 从命令字表抽出候选命令对 cmd=0x11 / 0x10 | 按一下\"停止\"、再按一下\"启动\" |
| 客户端实现 | 依结论产出纯 Python 启停客户端,实测生效 | — |
一句话结论:读懂这套私有协议,REA承担了全部逆向工作;人唯一不可替代的贡献,是提供真实流量、并按几下启停按钮给出真值。
第一步:协议栈身份识别
传统做法是\"对着字节猜格式\"。笔者的做法是先解决一个更便宜的问题:这到底是什么协议栈?
用REA打开上位机的原生PE后,检索通信相关符号,直接命中如下特征串:
CmpGwClientImpl.cpp
CmpGateway / CmpChannelClient / CmpRouter / CmpBlkDrvTcp
<厂商>CommDriver::<协议>ChannelConnect
以及一个版本字符串:1.2.heads/V2R1C00B18
这些不是\"推测出来的格式\",而是反编译后直接读到的符号名与源码路径——连原始工程的 .cpp 文件名都没有抹除。
结论已经清晰:该PL 的通信栈并非自研私有协议,而是基于CODESYS通信栈构建,厂商在其上做了一层适配封装。证据等级:CONFIRMED(静态观察,符号名与源码路径直接命中)。
这一步带来两个直接收益:
其一,攻击面从\"未知\"变成\"已知\"。 CODESYS通信栈是被公开研究多年的对象,其服务组划分、认证机制与已知漏洞类型都有公开资料。确认同源后,分析可以站在既有研究之上。
其二,既有认知可以直接迁移。 笔者对CODESYS V3 通信流程的掌握,在后续章节的会话流程还原中几乎逐条对上。
从安全设计角度看,这里也暴露一个常见误区:许多厂商把\"没公开文档\"等同于\"安全\"。 若底层复用的是公开协议栈,那么\"私有\"只是包装——真正的安全边界并没有因为\"不公开\"而扩大一丝。
第二步:协议格式
REA 反编译PDU解析逻辑后,一个完整数据包(剥离 TCP 头)呈现出清晰的四层嵌套。如下图为以一条状态查询帧为例的完整报文格式与字节偏移。
报文格式四层嵌套
- 第一层 Frame:u32 LE magic = 0xE8170100(线路字节 00 01 17 E8)+ 整帧总长;
- 第二层 Envelope:外层固定头 + 路由标签(请求方 / 响应方的端口 + IP)+ ftype / ctrl + 会话模式号 mode(由服务器分配)+ seq / ack + inner\_len + nonce;
- 第三层 InnerPDU:16字节定长头,svc位于inner+6、cmd位于 inner+8、handle位于 inner+10;
- 第四层 IBTag:[tag\_id][len][data]依次排列的键值流。
其中有两个关键点,都是靠REA定点反编译坐实的:
① svc / cmd 的偏移不能凭直觉。 svc在 inner+6、cmd 在inner+8,不是 +8 与 +10。偏移写错一个,服务号与命令号会整体错位,方向判断与命令语义全部混乱。笔者的做法是用已知语义的帧(如登录帧)反推偏移。
② 载荷是LEB128变长编码。顺符号链下钻到 IBTagReaderMoveNext的解码循环,and 0x7F / shl 7 / or 正是 LEB128(varint)的标准实现指纹。据此确认tag\_id与len均为变长编码。若按定长解析,服务器会直接返回\"坏参数\"——这正是许多逆向\"看起来对、真机不认\"的典型原因。
信封尾部的 4 字节 nonce 字段,第一眼像是完整性校验。经暴力拟合确认nonce = CRC32(inner),并且用对照实验验证——修改内层字段、重算CRC32后回填,服务器照收。结论明确:它只防传输错误,不防篡改。 顺带一条方法论提醒:该字段名为 \"nonce\",容易被误认为承担抗重放职责;把字段名当作功能说明,是协议逆向中最常见的误判来源之一。
第三步:完整通信流程
工业协议中\"先建立会话、再进行业务操作\"是通用设计。在分析一套新协议时,第一步不是急着发业务命令,而是把真实客户端的会话起始帧逐帧列全。整理后得到下图所示的五个阶段。
私有协议完整通信流程
- 链路协商:客户端发送 ftype=0xC3 协商帧,服务器回复 ftype=0x83,其中携带服务器分配的会话模式号mode;
- 会话注册与登录:svc=1 cmd=1 建立初始上下文 → cmd=9 交换运行参数 → cmd=2 登录,服务器在响应尾部返回会话句柄 handle;
- 参数协商循环:svc=1 cmd=1 + cmd=9 重复三轮;
- 应用附着:svc=2 cmd=0x59,附着到应用服务;
- 业务阶段:至此才可发送svc=2 的业务命令(状态查询、启停控制)。
把这条流程与笔者前作写过的CODESYS V3流程对照,两者在阶段划分与令牌传递方式上高度对应:
| 阶段 | CODESYS V3(笔者前作) | 本协议 |
|---|---|---|
| ① 通道建立 | CHOPEN → channel\_id | ftype=0xC3 / 0x83 协商 → mode |
| ② 会话创建 | 创建会话 → session\_id | svc=1 cmd=1 会话注册 |
| ③ 参数 / 挑战 | 获取 challenge | svc=1 cmd=9 参数协商 |
| ④ 身份登录 | 授权登录(用户名 + 密码) | svc=1 cmd=2 登录 → handle |
| ⑤ 应用登录 | 应用登录 → op\_key | svc=2 cmd=0x59 附着应用服务 |
| ⑥ 业务报文 | 业务服务组报文 svc=2 | 业务命令 |
这个\"五阶段\"并非本文的新发现,而是CODESYS通信栈的标准会话流程;它的价值在于印证——一套被包装成\"私有\"的协议,其会话模型与公开协议栈几乎逐条对应。前序章节底层是CODESYS V3协议栈的判断,在这里得到了流程级的印证。
顺带记一个坑:中途跳过第 3、4 步直接发 svc=2 业务命令,服务器会统一返回错误码28并踢掉会话。笔者曾排除多个方向——命令码写错、需要双会话、Tag编码不对、乃至需要加密升级(全量抓包中 0 条升级帧)。真因是最简单的那个:会话前置没有走完。 当出现\"参数都对、却整族被拒\"时,问题通常不在命令本身,而在前置流程。
第四步:启停攻击
拿到应用层访问权后,剩下一个必须靠真值裁决的问题:哪个命令是\"启动\"、哪个是\"停止\"?
REA已经把答案的一半摆到桌面上:扫过上位机里34个 sendRequest 调用点后,命令字表里有一对高度可疑的候选——载荷逐字节完全相同、仅命令码不同:
svc=2 cmd=0x11 payload = 81 01 88 00 11 84 80 00 f8 b4 ef af → 停止
svc=2 cmd=0x10 payload = 81 01 88 00 11 84 80 00 f8 b4 ef af → 启动
典型的 Start / Stop 命令对。证据等级:CONFIRMED(抓包观察,载荷一致仅命令码不同)。
此步骤需要人类介入告知REA操作,REA抓取报文即可做全面分析:
整理后见下表所示。
| 命令 | svc | cmd(十六进制 / 十进制) | 旁证 |
|---|---|---|---|
| 停止 | 2 | 0x11 / 17 | 点击后状态字节 01 → 02 |
| 启动 | 2 | 0x10 / 16 | 点击后状态字节 02 → 01 |
| 状态查询 | 2 | 0x14 / 20 | 应答 Tag 0x82 第 4 字节 |
这一步,就是人类在这项研究里不可替代贡献。 其余功夫——包括找出这一对候选命令——都由REA完成。
第五步:实测验证
按上述结论,笔者用纯Python标准库写了一个单文件客户端:构造帧 → 正确重算 nonce → 按序完成会话建立 → 发送业务命令。全程不引用厂商任何DLL / SDK。在授权仿真环境中实测,输出如下所示。
从协议身份识别到控制器启停控制的完整闭环,到此打通。 证据等级:CONFIRMED(动态验证,仿真环境实测)。
需要说明的是,本实现仅用于授权环境下的安全验证,旨在验证协议是否存在未授权控制风险,不作为攻击工具。
IFA\_PLC\_ATTACK
协议安全风险分析
综合静态分析与动态验证,该协议在设计上存在如下风险,整理后见下表所示。
| # | 风险 | 具体表现 |
| 1 | 会话建立无认证 | 无凭据、无证书,\"登录\"仅为固定字节握手动作,重放即可获得合法会话 |
| 2 | 握手可重放 | 协商帧载荷逐字节恒定,\"认证\"退化为\"知道模板\" |
| 3 | 完整性校验无效 | nonce = CRC32(inner),无密钥,改帧后重算即通过(已实测) |
| 4 | 全流量明文 | 未观察到任何加密升级帧;工程文件明文传输,可嗅探、可替换 |
| 5 | 控制命令无授权分级 | 只读查询与写操作(启停)同权限,一旦建立会话即\"一权通吃\" |
| 6 | 无速率 / 并发限制 | 未观察到对连接数或请求频率的任何限制 |
| 7 | 二进制指纹泄露 | 符号名、源码路径、版本串未剥离,直接暴露底层协议栈身份 |
在攻击路径上,这些风险并非孤立点,而是一条链:端口可达 → 建立会话(无需认证)→ 完成应用附着 → 发送启停命令。任何能访问业务端口的主体,都可以在不持有任何凭据的情况下,完成对控制器运行状态的控制。
需要特别指出的是:上述问题并非该厂商独有。由于协议底层复用CODESYS通信栈,同类问题在CODESYS生态的其他产品中同样需要关注;更广义地看,Modbus、S7Comm等经典工业协议也普遍建立在\"网络可达即可信\"的假设之上。这更多是一个行业性的历史遗留问题。
防护建议
厂商层面:
- 会话认证:在会话建立阶段引入随机挑战值(challenge)与凭据校验,避免\"固定模板即认证\"的可重放设计;
- 完整性校验:将无密钥的CRC32替换为带密钥的HMAC-SHA256;
- 通信加密:CODESYS通信栈本身具备加密通道能力,但默认未启用——建议出厂配置中强制开启;
- 授权分级:只读查询与控制类写操作权限分离,避免一权通吃;
- 二进制瘦身:发布前剥离符号名与源码路径,减少协议栈指纹泄露。
用户层面:
- 网络隔离:工控网络与办公网、互联网严格隔离,做好纵深防御;
- 最小可达:通过防火墙/访问控制仅放行必要主机访问业务端口;
- 远程访问加固:确需远程时采用安全VPN,并做好访问审计;
- 关注补丁:关注厂商安全通告,测试后升级受影响产品。
REA在工控安全研究中的价值
一句话:这次研究里,读懂私有协议的活儿几乎全是REA干的,人类只按了几个按钮。
它能用在哪些地方:
- 上位机 PE(组态软件、通信客户端、授权模块)是REA的舒适区——协议栈识别、报文格式还原、命令字提取、调用契约发现,基本都在数条编排内完成;
- 尤其值得注意的是黑盒抓包摸不到的\"调用契约\":隐藏参数、返回值为void、运行模式标志、结构体的精确偏移——这些只有静态反编译能给出,而REA把它们的获取成本压得很低。这也是\"能调通/调不通\"的分水岭;
- 它同时是\"定性加速器\":本次第一步就命中Cmp\* 符号,\"这是什么协议栈\"这个最贵的问题被一次检索解决,后续分析直接站在既有研究之上。
边界也要说清楚:
- REA不是反汇编器,能力取决于所接入的引擎(本次为 Windows Ghidra,仅支持 PE 目标);对非PE的OT固件无能为力,那属于ICS专用工具链的范畴;
- 它不降低认知门槛——本次识别之所以快,前提是笔者熟悉CODESYS组件命名;它压缩的是机械检索成本,而非判断力成本;
- 它替代不了\"观察真实流量 + 真值裁决\"——这正是本次按下那两次启停按钮的意义所在。
所以正确用法是:以REA建立假设,以真机抓包与人工裁决坐实结论。 而人类的价值,恰恰集中在后者那几次\"按键\"上,在于对业务的理解上。
结束
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论