Professional Documents
Culture Documents
S7 Cmmunication说明文档
S7 Cmmunication说明文档
第 1 部分:一般结构
据我所知,S7 协议没有公开可用的文档,但是有几个值得注意的项目可以帮助
处理它。Davide Nardella 创建了一个很棒的开源通信库 Snap7,它实现了基本的
通信场景。该库附带了 S7 协议的基本结构的广泛文档。另一个伟大的项目是
Thomas W.的 S7 Wireshark 剖析器,它覆盖了协议的大部分,其源代码包含一个
冗长的协议常数列表。在我和西门子设备一起工作的那些年里,这些对我来说
是无价之宝。由于没有官方文件,在 S7 议定书中不存在官方术语。在本文的其
余部分中,我试图遵守上述项目中使用的术语。
1.西门子通讯方案
在进入更多的技术细节之前,我想简单介绍一下西门子通讯的基本原理。当我
谈到“S7 协议”时,我指的是以太网 S7 通信,它主要用于将 PLC 连接到(I)PC 站
(PG/PC-PLC 通信)。这不会与西门子设备使用的不同现场总线协议混淆,例如
MPI、Profibus、IE 和 Proinet(这是用于将 PLC 连接到 IO 模块的基于以太网的协
议,而不是设备的管理协议)。
大多数时候,西门子通信遵循传统的主-从或客户端-服务器模型,其中 PC(主/
客户端)向现场设备(从/服务器)发送 S7 请求。这些请求用于向设备查询或
发送数据或发出某些命令。当 PLC 可以是通信主机时,有一些例外,使用
FB14/FB15,设备可以向其他设备发起 GET 和 PUT 请求。
结构图如下。
S7 协议是面向功能/命令的,这意味着传输包括 S7 请求和适当的应答(很少有
例外)。在连接建立期间协商并行传输的数量和 PDU 的最大长度。
S7 PDU 由三个主要部分组成:
标题:包含长度信息、PDU 引用和消息类型常量
参数:基于 PDU 的消息和函数类型,内容和结构有很大的变化。
数据:如果有数据,例如内存值、块代码、固件数据等,它是一个可选的
字段。
2.1 标头
协议 ID:[1b]协议常数总是设置为 0x32
0x01 作业请求:主机发送的请求(例如读/写存储器、读/写块、启动/停止
设备、设置通信)
0x02 ACK:由没有数据字段的从属设备发送的简单确认(我从未见过它由
S300 /S400 设备发送)
0x07-Userdata:原始协议的扩展,参数字段包含请求/响应 id(用于编程/
调试、SZL 读取、安全函数、时间设置、循环读取)。
保留:[2b]总是设置为 0x000(但可能忽略)
PDU 引用:[2b]由主机生成,随着每次新的传输递增,用于链接响应他们的请
求,.-Endian(注意:这是 WinCC、Step7 和其他西门子程序的行为,它可能是
随机生成的,PLC 只是将它复制到应答)
参数长度:[2b]参数字段的长度,Big Endian
数据长度:[2b]数据字段的长度,Big Endian
第一部分详细介绍了一般通信场景和分组结构。本部分将进一步研究“职务请
求”和“Ack 数据消息”的用途和内部结构。这些消息类型是一起讨论的,因
为它们非常相似,而且通常每个 Job Request(作业请求)都会产生一个 Ack 数据
应答
S7 PDU 的结构和通用协议报头在前一部分进行了说明。但是,参数头是特定于
消息类型的,对于 Job 和 Ack Data 消息,它以一个函数代码开始。其余字段的
结构取决于此值。该功能代码决定了消息的目的,并作为进一步讨论的基础。
1.设置通信[0xF0]
Pcap:S300 安装通信
参数标题显示在以下图表中:
1.1 S7 认证与保护
Pcap:S300 认证
这可能是讨论 S7 身份验证和保护机制的好地方(即使它们与实际通信设置无
关)。在 CPU 的配置过程中,可以设置三种保护模式。
没有保护:正如预期的那样,不需要身份验证。
写入保护:对于某些数据写入和配置更改操作,需要进行身份验证。
读/写保护:就像前一个一样,但是某些读操作也需要认证。
在正常操作期间,需要读/写权限的客户端在通信设置之后,通过 SZL 读取
(SZL ID:0x0132SZL 索引:0x0004)查询实际和指定的保护级别。如果需要
身份验证,则在 userdata 消息中将密码发送到设备,这将降低有效保护级别。
在任何人都认为这至少提供了一点点安全性之前,让我澄清它不是。密码为六
字节,几乎在清除中发送(使用常数和移位的 XORD)。它是可重放的,可以被
强迫。该协议还不提供完整性或机密性保护,消息注入和修改是可能的。当涉
及到 S7 安全性时,一般的经验法则是,如果你能 ping 这个设备,你就可以拥
有它。
这里必须注意,S7-1200/1500 系列设备使用稍微不同的方法,保护级别的处理
稍有不同,并且发送的密码明显更长(实际上是密码的哈希),但是它仍然是
恒定的和可重放的。
2.读/写变量[0x04/0x05]
Pcaps:
S300 读数变量单
S300 读写变量(多变量读写简单寻址)
这里,当事情开始变得有点复杂时,我强烈建议在阅读本节时查看所提供的
pcap(wireshark2 带有默认启用的 S7disctor)。数据读写操作是通过指定变
量的存储器区域、它的地址(偏移)及其大小或类型来执行的。在进入协议细
节之前,我想简单介绍 S7 寻址模型。
与前面提到的变量一样,通过指定它们的地址来访问这些变量,这个地址由三
个主要属性组成。内存区域:
Merker:[M]任意标记变量或标志寄存器驻留在这里。
数据块:[DB]DB 区域是存储设备不同功能所需的数据的最常见位置,这些数
据块编号,这是地址的一部分。
输入:[i]数字和模拟输入模块的值,映射到内存中。
输出:[Q]同样的内存映射输出。
计数器:[C]由 PLC 程序使用的不同计数器的值。
定时器:[T]由 PLC 程序使用的不同定时器的值。
还有其他不太常见的内存区域(如本地数据[L]和外围访问[P]等)。
变量的类型决定了它的长度以及它应该如何解释。举几个例子:
位:[X]一位。
字:两个字节宽无符号整数。
32 位整数(DINT,Double Integer)四字节宽符号整数。
实数:四字节宽 IEEE 浮点数。
计数器:计数器类型由 PLC 程序计数器使用。
在这个简短的绕行之后,让我们回到协议的变量读/写的实现。S7 协议支持用
不同的寻址模式查询单个消息中的多变量读/写。主要有三种模式:
任何类型:这是默认的寻址模式,它用于查询任意变量。所有三个参数
(区域、地址、类型)都是针对每个被寻址的变量指定的。
DB 类型:这是专门设计用来解决 DB 区域变量的特殊模式,它比任何类型的
寻址都更紧凑。
符号寻址:S7-1200/1500 系列设备使用此模式,并且允许使用预先定义的
符号名称来寻址某些变量。此模式将不在这里详细介绍。
对于每个寻址模式,参数报头以相同的方式构造:
函数代码:[1b]常数 0x04,用于读取或 0x05 的写作业和回复。
项目计数:[1B]以下请求项结构的数目。
请求项:此结构用于处理实际变量,其长度和字段取决于所使用的寻址类
型。这些项仅存在于 Job 请求中,并且从相应的 Ack Data 发出,而不管寻
址模式是什么,也不管它是读还是写请求。
写入请求:包含与读取响应相似的数据项,参数头中的每个请求项都包含
一个数据项。类似地,这些包含要在从属设备上写入的变量值。
总之,请求项总是包含变量的描述,其中多个变量可以在作业请求中发送,而
数据项包含所描述变量的实际值。数据项结构必须以偶数字节开始,因此,如
果它们的长度是奇数,并且有下面的数据项,则它们被填充为零字节。
剩下要讨论的是请求/数据项结构的格式。如前所述,它们依赖于正在使用的寻
址模式,因此将基于此来引入寻址模式。
2.1 具有任意类型寻址的项结构
下图显示了请求和数据项结构:
请求项的字段:
规范类型:[1b]此字段确定项结构的主要类型,对于读/写消息,它总是具
有代表可变规范的值 0x12。
长度:[1B]这个项目其余部分的长度。
语法 ID:[1B]此字段决定了寻址模式和其余项结构的格式。对于任何类型的
寻址,它具有恒定值 0x10。
变量 Type:[1b]用于确定变量的类型和长度(通常使用 S7 类型,如
REAL、BIT、BYTE、WORD、DWORD、COUNTER、…)。
计数:[2B]可以选择一个具有单个项结构的类似变量的整数组。这些变量
必须具有相同的类型,并且必须在内存中是连续的,并且 count 字段决定
这个数组的大小。它被设置为一个用于单变量读或写。
DB 编号:[2B]数据库的地址,如果该区域未设置为 DB(见下一字段),则
忽略该地址。
地址:[3b]包含所选存储器区域中的寻址变量的偏移量。基本上,地址被
转换为位偏移,并在网络(大端字节)字节顺序上编码 3 字节。在实践中,
最重要的 5 位永远不会被使用,因为地址空间比那个小。作为例子,
dxx40.3 将是 0x000 0143,它是 40×8+3。
类似地,关联数据项的字段:
错误代码:[1b]操作的返回值,0xFF 信号成功。在写请求消息中,这个字
段总是设置为零。
变量类型和计数:[1B2B]与请求项相同。
数据:该字段包含已寻址变量的实际值,其大小为 LeN(变量)*计数。
2.2 具有 DB 类型寻址的项结构
请求项的字段:
规格类型:[1B]与任何类型的寻址相同。
长度:[1B]这个项目其余部分的长度。
语法 ID:[1B]确定寻址模式,对于 DB 类型具有恒定值 0xB0。
子项的数目:[1b]以下子项的数目。
Subitem:
大小:[1B]指定从所选地址读取或写入的字节数。
DB 编号:[2B]地址变量所在的 DB。
地址:[2b]将变量的字节偏移量转换为给定的 dB。
数据项的字段:
错误代码:[1b]操作的返回值,0xFF 信号成功。
变量类型:[1b]总是设置为 0x09(八位字节字符串)。
长度:[2b]剩余的子响应数据的长度。
Subresponse:
错误代码:[1b]与子项请求相关联的返回值。
数据:要读取或写入的实际数据,解释这需要相应的子项。
3.块下载/下载[0x1a1f]
Pcaps:
S300 下载 OB1
S300—SNAP7 上传
首先,在西门子术语中,下载是指主机向从机发送块数据,而上传是另一个方
向。在西门子设备上,程序代码和(大部分)程序数据存储在块中,这些块具
有它们自己的头部和编码格式,这里不再详细讨论。从协议的角度来看,它们
是需要传输的二进制块(对于感兴趣的读者,snap7 源提供有关块标头及其编
码的信息)。
有七种不同类型的块由西门子设备识别:
OB:组织块,存储主要程序。
(S) fc:(System)函数,无状态的函数(没有自己的内存),可以从其他程
序调用他们
(S)FB:(System)函数块,即有状态函数,它们通常具有关联的(S)DB。
这些块的目的在西门子文档中得到了很好的描述。
这些块是用一个特殊的 ASCII 文件名在 Up/Dead 请求中处理的。这个文件名
的结构如下:
文件标识符:(1 字符)据我所知,这总是有“值”的值。
块类型:(2 字符)确定块类型,参见具体值的康斯坦茨.txt。
块编号:[ 5 字符]以十进制格式给定的块的数目。
目标文件系统:(1 字符)这个字段既可以有被动文件系统的“a”值,
也可以有“p”值。复制到活动文件系统的块立即被链接起来,这意味着一旦 PLC
执行恢复,它们就生效。另一方面,需要首先激活复制到被动文件系统的块。
一个示例文件名是_0800001P,它用于将 OB 1 复制到或从被动文件系统复制。
关于块编码和内容保护的快速说明。有两项措施来保护节目的内容和数据,并
允许节目库的分布。第一个被称为诀窍保护,如果设置防止 STEP7 或 TIA 显示块
的实际内容。不幸的是,绕过这一点是微不足道的,因为它只是在块的头中设
置的两个位,并且可以很容易地被清除。另一个保护措施是块“加密”,实际
上它只是用线性变换(字节的 xoring 和常数的旋转)进行混淆,再绕过它应该
是很简单的。所以不要依赖这些“安全”机制来保护你的技术诀窍。否则,数
据块包含原始的、初始化的内存映像。程序块包含 MC7(机器代码 7)二进制
指令。
请求下载-0x1a
下载块-0x1b
下载结束-0x1c
开始上传-0x1D
上传块-0x1e
结束上传-0x1f
这些消息的结构非常简单,但是消息序列(特别是用于下载)需要稍加解释。
3.1 上传块
上传块顺序相当直观,如下所示:
在 Ack 数据启动上传消息中,从节点告诉块的长度,然后主程序继续发送 Job-
Upload 块消息,直到接收到所有字节为止。最后,它用作业结束上传消息关闭
上传序列。块的实际数据由 Ack 数据上传块消息中的从服务器发送。
Job-开始上传参数头:
函数代码:[1B]0x1d 用于开始上载。
功能状态:[1B]仅用于上传消息,如果要发送更多数据,则设置为
0x01。未知:[2B]总是 0x0000。
文件名长度:[1B]以下文件名的长度。文件名:标识上述块的文件名
Job-上载参数标题:
包含函数代码(0x1e)、函数状态、未知(0x0000)和会话 ID 字段。
Ack Data-上载参数和数据部分:
函数代码:[1B]0x1e 用于上载。
功能状态:[1B]如果要发送更多数据,设置为 0x01。
数据部分:
长度:[2B]块数据的长度。
未知:[2B]总是 0x00fb。
块数据:上传数据块的一部分。
Job 端上传参数头:
包含函数代码(0x1f)、函数状态、未知(0x0000)和会话 ID 字段。
Ack Data-末端上传参数头:
只包含函数代码(0x1f)
3.2 下载块
上传和下载之间的关键区别在于,在下载过程中,通信的方向发生了变化,从
服务器变成了主(不错)。在初始请求下载交换之后,从服务器发送作业消息,主
应答以 Ack 数据答复,这是“仅从答复”规则的唯一例外。在发送所有字节后,
主(原始)发送下载结束作业以关闭下载会话。请参阅下面的顺序图。
实际消息的结构与上传消息的结构非常相似,所以我只想介绍它们的不同之处。
要获得准确的语法描述,请在 Wireshark 中打开示例 pcapk。作业请求下载消息
包含两个额外的字段,下载块的块长度和块的有效载荷长度(没有块头的长度)。
这两个字段都是以 ASCII 字符串编码的十进制数字。响应 Ack 数据请求下载只包
含函数代码,另一个重要的区别是,虽然存在会话 ID 字段,但它没有被使用(仍
然是 0x00000000),而是在每个作业下载块中传输文件名。其余消息的结构与前
面讨论的相同。
PCAPS:
S 300-控制命令(将 RAM 复制到 ROM,压缩内存,启动 PLC)
S 300-复制-ram 到-rom
S 300-激活-
s 300-删除块(激活/删除块,启动 PLC)
PLC 控制消息用于在从设备上执行修改其执行/内存状态的不同例程。这些命令
用于启动或停止 PLC 控制程序的执行,激活或删除设备上的程序块,或将其配
置保存到持久内存中。这些消息的结构相当简单,不需要讨论确切的细节(请参
阅附件中的捕获)。作业-PLC 控制消息由两个主要部分组成:被调用方法的 ASCII
名称及其参数(也被编码为 ASCII 字符串)。方法名称的结构与块传输部分中引入
的文件名类似。参数取决于方法类型,它们可以被看作是它的一个参数。Ack
数据消息只包含 PLC 控制功能代码。
一些示例函数名及其相关参数:
_INSE:激活设备上下载的块,参数是块的名称(例如 OB1)。_DELE:从
设备的文件系统中移除一个块,该参数又是块的名称。
P_PROGRAM:设置设备的运行状态(启动,停止,MEM 重置)。它没有参
数发送来启动设备,但是停止 PLC 程序使用不同的函数代码(见下一节)。
_GARB 压缩内存。
5.PLC 停止[0x29]
PCAP
S300-停止程序