抓取和分析西门子S7COMM协议
近期在初步入门工控安全,进行了第一次协议分析,本文将介绍如何对 S7COMM
协议进行从零到一的分析。
因为是纯小白,加上几乎没人指引,又由于 S7COMM
是西门子的私有协议,本文的编写全靠个人的分析与网上的资料参考,无法保证编写的内容全部准确。
如何抓取到 S7COMM 协议
要想对一个协议进行分析,必须先能抓取到该协议。这里我目前尝试了两种方法,第一种是使用 Snap7
模拟器,优点是快捷,方便;第二种是搭建一整套仿真环境,优点是很充分的模拟出真实环境,如果还需要进行除协议外的其他研究,可能需要搭建出一套仿真环境。如果有设备的话,那就更加方便和真实了(不过我没有,所以这种情况就不做记录了)
使用 Snap7
模拟器抓取S7协议
关于下载和基本使用的话,这篇 文章 介绍的很清晰,下面直接介绍用 Wireshark
如何在 Snap7
模拟器上抓取到 S7COMM
流量包
这里直接写成 0.0.0.0
,然后点 Start
如下
然后这里选择 127.0.0.1
,点击 Connect
如下
Wireshark
中选择这个 Adapter for loopback traffic capture
以向 PLC
发出 wirte
请求为例,我们在客户端做如下设置,然后点击 write
请求发出后,我们用 Wireshark
就能抓取到这个数据包了,这里用 cotp
来过滤一下。这里只介绍协议的抓取,后面再做协议分析
在仿真环境抓取S7协议
具体的搭建环境过程请参考这篇 文章 ,仿真软件的话我用的是 S7-PLCSIM Advanced v3.0
,这里只介绍环境搭建后如何用 Wireshark
抓取到 S7COMM
流量包(如果安装不熟练的话,建议安装在虚拟机里面,笔记本能吃得消的话,内存最好配置成 16G
,不然会很卡 )
首先从控制面板里找到更改适配器这里,然后点击以太网的属性(如下)
然后点击下面的版本协议
然后把 IP
配置成 192.168.0.100
(也不是非要跟我这个一样)
打开 S7-PLCSIM Advanced v3.0
仿真工具,然后配置就跟图上标注的一样即可,最后点击 Start
此时正常效果应该是如下,有个黄色的灯亮了
接下来配置博途,点这个创建新项目,项目名称随便取即可,最后点创建
点击打开项目视图
这里来添加新设备,随便选中一个 1500
型号的 CPU
正常的话,应该会出现如下界面,点击红框中的这个地方,来配置PLC
的 IP
这里的 IP
一定要和仿真工具里面配置的 IP
一样(如下)
下面这里需要选中 允许来自远程对象的 PUT/GET通信访问
下面创建一个 DB
块
然后点击这个 DB
块,写入信息如下图所示(至于变量类型和个数都无所谓,这里只是做一个演示)
右键 DB
块,点击属性,然后就能看到下面的配置。把 优化的块访问 选项给取消掉
项目这里,也就是下图的 抓取流量测试 的位置,右键选择属性,在保护这一栏勾选 块编译时支持仿真 选项
接下来进行编译和下载,依次点击下面的 编译 和 下载到设备 ,选中和图中一样的接口类型和接口(似乎默认就是这样),最后点击开始搜索
如果前面的配置都正确的话,应该会出现下图的状态并且 下载 按钮也会亮起来(如果没亮的话,肯定是前面配置错了,大概率是 PLC
的 IP
和仿真工具中的 IP
或者以太网卡的 IP
没有配置好),点击 下载 按钮
正常的话,应该是要先点一下 装载,然后选择 启动模块 ,点击 完成(如果跟下面一样,直接到这个界面的话,就不需要再管装载了,点击完成就行)
至此全部完成的话,那么应该和下图一样,从之前的黄灯变成绿灯。
并且也是能够直接 ping
通 PLC
的(如下)
最后我使用的是 python
中的 snap7
模块,对 PLC
发出读写请求。从而用 Wireshark
抓取通信时的流量,用 S7COMM
来过滤一下,可以发现成功的抓取到了 S7COMM
协议
这里我写的脚本代码如下,如果没不太理解函数作用的话,可以阅读一下 官方文档
import snap7 |
因为是初学者,有一些问题在上网查过资料后没有找到想要的答案,在下面记录一下(希望有明白的朋友能告知一下)
- 配置的
PLC
是S7-1500
,为什么发出读写请求抓到的是S7COMM
协议,而不是S7 COMM PLUS
协议- 除了用
python
脚本直接对PLC
发出请求之外,还有哪些方式能抓取到与PLC
通信的流量(我发现在TIA
中的上传和下载也有通信的流量)python-snap7
给出了plc_stop()
函数,该函数可以直接向PLC
发出关机请求,我使用了之后发现没有用,是哪里进行了阻止呢?或者是仿真环境无法实现的原因?
S7COMM协议分析
ISO-OSI参考模型 | S7以太网协议模型 |
---|---|
7-应用层 | 6-S7 Communication |
6-表示层 | S7 communication(COTP) |
5-会话层 | S7 communication(TPKT) |
4-传输层 | 3-TCP(102端口) |
3-网络层 | IP |
2-数据链路层 | 1-工业以太网 |
1-物理层 |
这里我用 Snap7
模拟器来抓取一个 S7COMM
的流量进行分析,先介绍一下 Snap7
客户端中的 Area
DB Number
Start
Amount
WordLen
这五部分(如下图)
Area
表示数据区域类型,常见区域如下Hex Value 描述 0x03 System info of 200 family 200系列系统信息 0x05 System flags of 200 family 200系列系统标志 0x06 Analog inputs of 200 family 200系列模拟量输入 0x07 Analog outputs of 200 family 200系列模拟量输出 0x80 Direct peripheral access (P) 直接访问外设 0x81 Inputs (I) 输入(I) 0x82 Outputs (Q) 输出(Q) 0x83 Flags (M) 内部标志(M) 0x84 Data blocks (DB) 数据块(DB) 0x85 Instance data blocks (DI) 背景数据块(DI) 0x86 Local data (L) 局部变量(L) 0x87 Unknown yet (V) 全局变量(V) 0x1c S7 counters (C) S7计数器(C) 0x1d S7 timers (T) S7定时器(T) 0x1e IEC counters (200 family) IEC计数器(200系列) 0x1f IEC timers (200 family) IEC定时器(200系列) DB Number
代表数据块编号,如果访问的不是数据库则该位置为0
Start
表示从指定数据块的起始位置写入数据,Start
为4
,那么则会从编号为4
(第五个字节处)开始写入或读出数据Amount
表示写入或读出的数据量(这单位不一定是字节,取决于WordLen
字段的类型)WordLen
表示单位长度,可以选择Byte
Word
DWord
等等
接下来点击 Write
,此时抓取一个 S7COMM
的报文
S7 Communication |
S7COMM
的完整数据如上,下面针对 Header
Parameter
Data
三部分的各个字段分别做出解释
Header
S7 Communication |
Protocol Id
为协议 ID
,属于常量为 0x32
ROSCTR
表示 PDU
(协议数据单元)的类型,有以下值
值 | 类型 | 解释 |
---|---|---|
0x01 | Job | 主站发送的作业请求(例如读/写存储器,读/写块,启/停设备,通信设置) |
0x02 | Ack | 没有数据字段的简单确认 |
0x03 | Ack_Data | 带有数据字段的应答,一般来响应 Job 请求 |
0x07 | Userdata | 扩展协议,其参数字段包含请求/响应ID,一般用于编程/调试、读取SZL |
Redundancy Identification (Reserved)
: 冗余数据,通常为 0x0000
Protocol Data Unit Reference
: 确保通信中每个 PDU
都有唯一的标识,发送方会为每个发送的 PDU
分配一个独立的协议数据单元参考值。接收方在接收到PDU时会检查该字段,以确认该 PDU
是否是之前接收过的重复 PDU
,或者是一个新的 PDU
Job
报文和 Ack_Data
报文的protocol data unit reference
值是一样的,个人猜测是为了确保 Ack_Data
是对应于特定的 Job
报文相应
Parameter length
:参数 Parameter
字段的长度
Data length
:数据 Data
字段的长度
Error class
:仅存在于 Ack-Data 报文中,可能的错误常量查看协议常量
Error code
:仅存在于 Ack-Data 报文中,可能的错误常量查看协议常量
Parameter
Parameter: (Write Var) |
上面 Header
部分提到了 Parameter
字段的长度为 14
,通过下图观察,得以验证
Function
:功能码,当 PDU
类型为 Job
或者 Ack_Data
类型时常见的功能码如下:
Hex | Value | 含义 |
---|---|---|
0x00 | CPU services | CPU服务 |
0xf0 | Setup communication | 建立通信 |
0x04 | Read Var | 读取值 |
0x05 | Write Var | 写入值 |
0x1a | Request download | 请求下载 |
0x1b | Download block | 下载块 |
0x1c | Download ended | 下载结束 |
0x1d | Start upload | 开始上传 |
0x1e | Upload | 上传 |
0x1f | End upload | 上传结束 |
0x28 | PI-Service | 程序调用服务 |
0x29 | PLC Stop | 关闭PLC |
Item count
:表示一个请求中包含的数据项 Item
的数量
Item
:数据项,Item [1]
表示第一个数据项。它描述了要操作的数据的详细信息,包括数据类型、长度和位置等。
Item
结构又包含如下字段:
Variable specification
:变量规范字段,一般为 0x12
Length of following address specification
:地址规范长度,主要是以此往后的地址长度(通过下面这个图看的可能比较直观)
Syntax Id
:为 IDS
的地址规范格式类型,主要用来确定寻址模式。常见值如下
Hex | 值 | 描述 |
---|---|---|
0x10 | S7ANY | Address data S7-Any pointer-like DB1.DBX10.2 |
0x13 | PBC-R_ID | R_ID for PBC |
0x15 | ALARM_LOCKFREE | Alarm lock/free dataset |
0x16 | ALARM_IND | Alarm indication dataset |
0x19 | ALARM_ACK | Alarm acknowledge message dataset |
0x1a | ALARM_QUERYREQ | Alarm query request dataset |
0x1c | NOTIFY_IND | Notify indication dataset |
0xa2 | DRIVEESANY | seen on Drive ES Starter with routing over S7 |
0xb2 | 1200SYM | Symbolic address mode of S7-1200 |
0xb0 | DBREAD | Kind of DB block read, seen only at an S7-400 |
0x82 | NCK | Sinumerik NCK HMI access |
Transport size
:指的是数据传输的大小或类型,它用于定义在进行数据交换时所使用的数据单元大小。
Hex | 值 |
---|---|
2 | BYTE |
3 | CHAR |
4 | WORD |
5 | INT |
6 | DWORD |
7 | DINT |
8 | REAL |
Length
指的是写/读 数据的长度,就是模拟器中的 Amount
字段
DB number
: DB块编号,如果访问的不是DB区域,此处为0x0000
Area
:代表要操作的区域类型,如下表
Hex | Value | 描述 |
---|---|---|
0x03 | System info of 200 family | 200系列系统信息 |
0x05 | System flags of 200 family | 200系列系统标志 |
0x06 | Analog inputs of 200 family | 200系列模拟量输入 |
0x07 | Analog outputs of 200 family | 200系列模拟量输出 |
0x80 | Direct peripheral access (P) | 直接访问外设 |
0x81 | Inputs (I) | 输入(I) |
0x82 | Outputs (Q) | 输出(Q) |
0x83 | Flags (M) | 内部标志(M) |
0x84 | Data blocks (DB) | 数据块(DB) |
0x85 | Instance data blocks (DI) | 背景数据块(DI) |
0x86 | Local data (L) | 局部变量(L) |
0x87 | Unknown yet (V) | 全局变量(V) |
0x1c | S7 counters (C) | S7计数器(C) |
0x1d | S7 timers (T) | S7定时器(T) |
0x1e | IEC counters (200 family) | IEC计数器(200系列) |
0x1f | IEC timers (200 family) | IEC定时器(200系列) |
Data
Data
Item [1]: (Reserved)
Return code: Reserved (0x00)
Transport size: BYTE/WORD/DWORD (0x04)
Length: 40
Data: 010366778899000011000000000000000000000000000000000000ff0000000000000000…
因为 Wirte
操作要写入值,所以它比 Read
操作多一个 Data
部分。
return code
:返回码,用来标识 job
操作后的结果(这里的 Return code
是 0x00
,因为这里截取的是请求报文,它对应的响应报文中的 return code
字段是 0xff
)
Hex | 值 | 描述 |
---|---|---|
0x00 | Reserved | 未定义,预留 |
0x01 | Hardware error | 硬件错误 |
0x03 | Accessing the object not allowed | 对象不允许访问 |
0x05 | Invalid address | 无效地址,所需的地址超出此PLC的极限 |
0x06 | Data type not supported | 数据类型不支持 |
0x07 | Data type inconsistent | 日期类型不一致 |
0x0a | Object does not exist | 对象不存在 |
0xff | Success | 成功 |
Transport size
:数据传输的大小或类型,上面已经介绍过
Length
:这里的 Length
指的是只有 Data
(下面这个字段)的长度,并不是这个 Data
部分的长度
Data
:需要写入的数据
参考文章
https://xz.aliyun.com/t/6603?accounttraceid=336501792de944e4807240804083f4f3zheo#toc-2
https://blog.csdn.net/oliver223/article/details/118107094
https://blog.csdn.net/weixin_43158056/article/details/104298156