freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

工控安全 | 西门子通信协议S7COMM(Part 2)
2018-11-12 10:00:02

*本文原创作者:LiukerTeam,本文属FreeBuf原创奖励计划文章,未经许可禁止转载

前言

随着网络强国、工业4.0,工控安全市场今年明显有相当大的改善,无论从政策还是客户需求,都在逐步扩大中。但是,搞工控安全研究的人员却寥寥无几,一方面,没有可以研究和学习的便利的环境;另一方面工控安全是个跨学课的技术,需要了解多方面的知识,有比较高的技术上的门槛。特别是工控系统中通信协议,在工控系统中通信协议存在众多标准,也存在众多私有协议,如果你有过使用组态软件的经历,你便会发现,在第一步连接设备时除连接设备的方式有以太网/串行等方式外,各家基本上都存在自己的私有通信协议。比如:西门子的是S7Comm协议。

上一篇文章《工控安全 | 西门子通信协议S7COMM(Part 1)》带来了西门子PLC系统构成S7协议结构TPKT协议COTP协议S7Comm协议五大块内容,本文紧接着上文中的S7Comm协议章节继续开展,没看过上一篇的小伙伴需要补补课哦,不然会不知所云~

5.2.4下载

下载是Step7发送块数据给PLC(图25)。在西门子设备上,程序代码和(大部分)程序数据存储在块中,这些块有自己的头和编码格式。

在西门子设备中有8种不同类型的功能块,具体的请参考6.7

这些块在上/下载请求中用特殊的ASCII文件名寻址。这个文件名的结构如下:

1 (1 byte): File identifier(ASCII),文件标识符。其有_ (Complete Module)、$ (Module header for up-loading)两种文件标识符;

2 (2 bytes): Block type,块类型。具体类型,请参考6.7

3 (5 bytes): Block number,块编号;

4 (1 byte): Destination filesystem(ASCII),目标的文件系统。其有三种文件系统:

P(Passive (copied, but not chained) module):被动文件系统

A (Active embedded module):主动文件系统

B (Active as well as passive module):既主既被文件系统

例如:文件名为_0A00001P(文件标识是_,块类型为DB,块的编号为00001,目标块的文件系统是P。),用于将DB 1复制到被动文件系统或从被动文件系统复制。

下载有3中不同的功能类型:

请求下载(Request download [0x1A])

下载块(Download block [0x1B])

下载结束(Download ended [0x1C])

在下载过程中,先是Step7向PLC发送一个请求下载的Job,PLC收到后则回复一个Ack_Data。在发送完所有字节后,Step7向PLC发送一个下载结束的Job来关闭下载会话。 时序图如下:

图25 下载时序图(图片来源:互联网)

图25 下载时序图(图片来源:互联网)

好了,开始介绍下载的结构啦!

如图26所示,即为一个完整的下载过程:

完整的下载过程

图26 一个完整的下载过程例子

5.2.4.1请求下载(Request download [0x1A])

先来介绍,当PDU类型为Job时,Request download [0x1A]没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 无意义,一般为0x00000000;

4 (1 byte): filename length,文件名长度;

5 (? bytes): filename, default is 9 byte,文件名,长度一般为9个字节;

1 (1 byte): File identifier(ASCII),文件标识符。其有_ (Complete Module)、$ (Module header for up-loading)两种文件标识符;

2 (2 bytes): Block type,块类型。具体类型,请参考6.7

3 (5 bytes): Block number,块编号;

4 (1 byte): Destination filesystem(ASCII),目标的文件系统。其有P(Passive (copied, but not chained) module)、A (Active embedded module)、B (Active as well as passive module)三种文件系统;

6 (1 byte): Length part 2 in bytes,参数的第二部分长度,也就是接下来的字段长度;

7 (1 byte): Unknown char(ASCII);

8 (6 bytes): Length load memory in bytes(ASCII);

9 (6 bytes): Length of MC7 code in bytes(ASCII)。

其实就是告诉PLC要下载块。举个例子:

图27 请求下载_0800001P的作业请求

图27 请求下载_0800001P的作业请求

如图27所示,文件标识是_ (Complete Module),块类型为OB,块的编号为00001,目标块的文件系统是P (Passive (copied, but not chained) module),所以文件名为_0800001P。

那PDU类型为Ack_Data时,Request download [0x1A]的Parameter中只有一个function。下图即为图27的响应:

图28 请求下载_0800001P的确认数据响应

图28 请求下载_0800001P的确认数据响应

OK,请求下载完成后,接下来就可以Download block了!

5.2.4.2下载块(Download block [0x1B])

上面说了,下载是Step7发送块数据给PLC。

当PDU类型为Job时,Download block [0x1B]也没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 无意义,一般为0x00000000;

4 (1 byte): filename length,文件名长度;

5 (? bytes): filename, default is 9 byte,文件名,长度一般为9个字节;

1 (1 byte): File identifier(ASCII),文件标识符。其有_ (Complete Module)、$ (Module header for up-loading)两种文件标识符;

2 (2 bytes): Block type,块类型。具体类型,请参考6.7

3 (5 bytes): Block number,块编号;

4 (1 byte): Destination filesystem(ASCII),目标的文件系统。其有P(Passive (copied, but not chained) module)、A (Active embedded module)、B (Active as well as passive module)三种文件系统;

是的,Download block [0x1B]的Parameter比Request download [0x1A]的Parameter的第一部分相同!

为了更好比较,举个例子:

图29 下载块_0800001P的作业请求

图29 下载块_0800001P的作业请求

上图是下载_0800001P的作业请求。

那PDU类型为Ack_Data时,Download block [0x1B]有Parameter和Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

而其Data的结构,如下:

1 (Unsigned integer, 2 bytes): Length,数据长度;

2 (Unsigned integer, 2 bytes): Unknown byte(s) in blockcontrol,未知字节;

3 (Label,data_length-4 bytes): Data,数据;

下图即为图29的响应:

图30 下载块_0800001P的响应

图30 下载块_0800001P的响应

5.2.4.3下载结束(Download ended [0x1C])

当PDU类型为Job时,Download ended [0x1C]也没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 无意义,一般为0x00000000;

4 (1 byte): filename length,文件名长度;

5 (? bytes): filename, default is 9 byte,文件名,长度一般为9个字节;

1 (1 byte): File identifier(ASCII),文件标识符。其有_ (Complete Module)、$ (Module header for up-loading)两种文件标识符;

2 (2 bytes): Block type,块类型。具体类型,请参考6.7

3 (5 bytes): Block number,块编号;

4 (1 byte): Destination filesystem(ASCII),目标的文件系统。其有P(Passive (copied, but not chained) module)、A (Active embedded module)、B (Active as well as passive module)三种文件系统;

是的,Download ended [0x1C]跟Download block [0x1B]的Parameter和Request download [0x1A]的Parameter的第一部分相同!

举个例子:

图31 结束下载_0800001P的作业请求

图31 结束下载_0800001P的作业请求

那PDU类型为Ack_Data时,Download ended [0x1C]的Parameter中只有一个function。下图即为图31的响应:

图32 结束下载_0800001P的响应

图32 结束下载_0800001P的响应

这样,整个下载过程就完成了!

下载到这就介绍完了,接着就介绍上传啦!

5.2.5上传

上传是PLC发送块数据给Step7(如图33)。

上传有3中不同的功能类型:

开始上传(Start upload [0x1D])

上传(Upload [0x1E])

上传结束(End upload [0x1F])

在上传过程中,先是Step7向PLC发送一个开始上传的Job,PLC收到后则回复一个Ack_Data,并告诉Step7块的长度、上传会话ID。然后PLC继续上传块数据到Step7,直到Step7收到所有字节。最后,Step7发送结束上传的作业请求来关闭上传会话。时序图如下:

图33 上传的时序图(图片来源:互联网)

图33 上传的时序图(图片来源:互联网)

好了,开始介绍上传的结构啦!

如图34所示,即为一个完整的下载过程:

图34 一个完整的上传过程例子

图34 一个完整的上传过程例子

5.2.5.1 开始上传(Start upload [0x1D])

先来介绍,当PDU类型为Job时,Start upload [0x1D]没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 上传的会话ID,此时为0x00000000;

4 (1 byte): filename length,文件名长度;

5 (? bytes): filename, default is 9 byte,文件名,长度一般为9个字节;

1 (1 byte): File identifier(ASCII),文件标识符。其有_ (Complete Module)、$ (Module header for up-loading)两种文件标识符;

2 (2 bytes): Block type,块类型。具体类型,请参考6.7 功能块

3 (5 bytes): Block number,块编号;

4 (1 byte): Destination filesystem(ASCII),目标的文件系统。其有P(Passive (copied, but not chained) module)、A (Active embedded module)、B (Active as well as passive module)三种文件系统;

其实就是告诉PLC你上传的位置。举个例子:

图35 开始上传的作业请求

图35 开始上传的作业请求

如图35所示,文件标识是_ (Complete Module),块类型为0B(SDB),块的编号为00000,目标块的文件系统是A (Active embedded module),所以文件名为_0B00000A。

那PDU类型为Ack_Data时,Start upload [0x1D]的Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 上传的会话ID,告诉Step7上传会话ID;

4 (Unsigned integer, 1 byte): Blocklengthstring Length;

5 (Character string): Blocklength,块的长度;

下图即为图35的响应:

图36开始上传的响应

图36开始上传的响应

图36中,其上传会话ID为0x00000007。

5.2.5.2 上传(Upload [0x1E])

上面说了,上传是PLC发送块数据给Step7。

当PDU类型为Job时,Upload [0x1E]也没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): for all unknown bytes in blockcontrol;

3 (4 bytes): 上传的会话ID,告诉Step7上传会话ID;

为了更好比较,举个例子:

图37 上传的作业请求

图37 上传的作业请求

那PDU类型为Ack_Data时,Upload [0x1E]有Parameter和Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

而其Data的结构,如下:

1 (Unsigned integer, 2 bytes): Length,数据长度;

2 (Unsigned integer, 2 bytes): Unknown byte(s) in blockcontrol,未知字节;

3 (Label,data_length-4 bytes): Data,数据;

下图即为图37的响应:

图38 上传的确认数据响应

图38 上传的确认数据响应

5.2.5.3 上传结束(End upload [0x1F])

上传结束的过程,即为所有数据上传完成后,Step7发送结束上传的作业请求,PLC收到后就关闭会话,然后返回一个响应。

当PDU类型为Job时,End upload [0x1F]也没有Data,其Parameter的结构,如下:

1 (1 byte): Function Status,功能码状态;

2 (2 bytes): Error code,错误代码:

详细的Error code,参考6.1.2 Error code in parameter part

3 (4 bytes): 上传的会话ID,告诉Step7上传会话ID;

举个例子:

图39 上传结束的作业请求

图39 上传结束的作业请求

那PDU类型为Ack_Data时,End upload [0x1F]的Parameter中只有一个function。

图40所示,即为图39的响应:

图40 上传结束的响应

图40 上传结束的响应

这样,整个上传过程就完成了!

5.2.6 程序调用服务(PI service [0x28])

程序调用是用于在PLC执行修改执行/内存状态的日常工作。这些命令可以用于启动或停止PLC控制程序、激活或删除程序块。

当PDU类型为Job时,PI service [0x28]没有Data,只有Parameter,那Parameter的结构,如下:

1 (7 bytes): Unknown;

2 (Unsigned integer, 2 bytes): Parameter block length;

3 (?bytes): Parameter block,参数;

4 (Unsigned integer, 1 byte):String length,PI service的字符串长度;

5 (Character string, ASCII):PI (program invocation) Service name,程序调用服务名,参考6.8 程序调用服务名(PI service names)

Parameter包含两个主要部分:

服务名称

参数:取决于方法类型,可以将它们看作是它的参数

服务名称及其相关参数的示例:

_INSE:激活设备上下载的块,参数是块的名称(比如:OB 1)。

_DELE:从设备的文件系统中删除一个块,该参数也是该块的名称。

P_PROGRAM:设置设备的运行状态(启动、停止、复位)。

_GARB:压缩PLC内存。

_MODU:将ram复制到ROM,参数包含文件系统标识符(A/E/P)。

如果服务调用的参数是块的话,那么Parameter block的结构如下:

1 (1 byte): Number of block;

2 (1 byte): Unknown,默认为0x00;

3 (? bytes): filename,文件名:

1 (2 bytes, ASCII): Block type,块类型。具体类型,请参考6.7 功能块

2 (5 bytes, ASCII): Block number,块编号;

3 (1 byte, ASCII): Destination filesystem(ASCII),目标的文件系统。其有P(Passive (copied, but not chained) module)、A (Active embedded module)、B (Active as well as passive module)三种文件系统;

举个例子,如图41所示:

图41 _INSE(激活PLC模块)的作业请求

图41 _INSE(激活PLC模块)的作业请求

上图可知服务名称是_INSE,参数是0B0004P(SDB4),那么它的作业请求是激活PLC中SDB 4,那么它的请求响应又是如何呢?如图42所示:

图42 _INSE(激活PLC模块)的响应

图42 _INSE(激活PLC模块)的响应

而另一种情况,如图43所示:

图43_MODU(复制RAM到ROM)的作业请求

图43_MODU(复制RAM到ROM)的作业请求

上图中,其Parameter block中只有Argument。

5.2.7 PLC STOP [0x29]

PLC STOP 基本上跟5.2.6 程序调用服务(PI service [0x28])一致,唯一的区别就是它没有Parameter block,而它的PI service为P_PROGRAM。搞不明白为啥单独占用一个功能码~~~

看个例子吧,如图44所示:

图44 PLC STOP的作业请求

图44 PLC STOP的作业请求

到此为此JOB和ACK_DATA类型下的功能码都介绍完了,接下来介绍S7commm协议的扩展。

5.3 协议拓展(Userdata)

上面介绍了S7Comm的JOB和ACK_DATA两个PDU类型,那接着将介绍PDU类型是UserData的内容,它用于编程/调试、读取SZL、安全功能、时间设置,循环读取等,可以说是S7Comm中最复杂的一部分。

大家不要慌哈,Are u ready?

Okay,当PDU类型为UserData时,其S7Comm结构,如图45所示:

图45 S7Comm的结构(UserData)

图45 S7Comm的结构(UserData)

图45中蓝色部分为S7Comm头部,橘色为Parameter部分,具体的Parameter结构如下:

1 (3 bytes):参数头(Parameter head);

2 (1 byte):参数长度(Parameter length),它的可能是8字节或12字节;

3 (1 byte):未知定义;

4 (1/2 byte,高位):参数类型(Type),常见的类型可参考《6.9 拓展协议的参数类型》

5 (1/2 byte,Low nibble):功能组(Function group),常见的功能组可参考《6.10 拓展协议的功能组》

6 (1 byte):子功能码(SubFunction);

7 (1 byte):序号。

接着就是一一介绍各个功能组。

5.3.1 转换工作模式(Mode-transition [0x0])

当功能组为转换工作模式(Mode-transition)时,请求报文中是没有Data部分的,而主要起作用的是子功能码(Subfunction),常见的子功能码有:

STOP(0x00):STOP模式;

Warm Restart(0x01):暖启动;

RUN(0x02):RUN模式;

Hot Restart(0x03):热启动;

HOLD(0x04):HOLD模式;

Cold Restart(0x06):冷启动;

RUN_R (H-System redundant)(0x09):H-System冗余运行;

LINK-UP(0x0B):LINK-UP模式;

UPDATE(0x0C):UPDATE模式。

关于暖启动、冷启动、热启动的区别可参考:S7-400 CPU 启动(暖启动),冷启动和热启动的区别是什么? - ID: 34053758 - Industry Support Siemens,至于冗余可参考: 何为冗余-找答案-工业支持中心-西门子(中国)有限公司(SLC)

来看个栗子消化一下吧,如图46所示:

图46 工作模式转换为暖启动

图46 工作模式转换为暖启动

如图46中绿色部分为参数类型(Type)和功能组(Function group),蓝色框内容是子功能码(SubFunction),值是0x01,即为暖启动。

5.3.2 程序员命令(Programmer commands [0x1])

程序员命令(Programmer commands)主要是工程师用于编程或调试,比如:监视/修改变量、读取修改诊断数据。所有的子功能码有:

请求诊断数据(Request diag data (Type 1)):0x01;

变量表(VarTab):0x02;

读取诊断数据(Read diag data):0x0c;

移除诊断数据(Remove diag data):0x0e;

清除(Erase):0x0f;

强制(Forces):0x10;

请求诊断数据(Request diag data (Type 2)):0x13;

这里的请求报文和响应报文都和图45有点不一样,具体如图47所示:

图47 功能码组为Programmer commands的报文结构

图47 功能码组为Programmer commands的报文结构

下面以变量表为例,变量表如图48所示:

图48 变量表

图48 变量表

如果对 DB100.DBW 2进行监视,那么他的请求报文如图49所示:

图49 监视变量表的请求报文

图49 监视变量表的请求报文

图49中的Header、Parameter在前面已经介绍了,重点介绍Data部分的结构,请求报文的结构如下:

1 (1 byte) : 返回码,具体的可参考6.6.1

2 (1 byte) :Transport sizes,指的数据类型,通常有bit、byte等,具体可参考6.4.2

3 (2 bytes) : 往后的数据长度,如图49为32个字节;

4 (1 byte) : Unknown;

5 (1 byte) : 报文类型(type of data),分为请求(0x14)、响应(0x04);

6 (2 bytes) : Item count和Item data的长度(Byte count);

7 (20bytes) : Unknown;

8 (2bytes) : Item个数;

9 (varibalebytes) : Item 1;

1 (1 byte) : 区域(Area);

2 (1 byte) : 长度(Length (repetition factor));

3 (2 bytes) : 模块号(DB number);

4 (2 bytes) : 偏移地址(Startaddress)。

...

n (varibalebytes) : Item n;

响应报文跟请求非常的像,但是还是有所不一样,响应报文结构如下:

1 (1 byte) : 返回码,具体的可参考6.6.1

2 (1 byte) :数据类型(Transport sizes),通常有bit、byte等,具体可参考6.4.2

3 (2 bytes) : 往后的数据长度,如图49为32个字节;

4 (1 byte) : Unknown;

5 (1 byte) : 报文类型(type of data),分为请求(0x14)、响应(0x04);

6 (2 bytes) : Item count和Item data的长度(Byte count);

7 (4bytes) : Unknown;

8 (2bytes) : Item个数;

9 (varibalebytes) : Item 1;

1 (1 byte) : 返回码,具体的可参考6.6.1

2 (1 byte) :数据类型(Transport sizes),通常有bit、byte等,具体可参考6.4.2

3 (2 bytes) : 往后的数据长度;

4 (varibale bytes) : Data。

...

n (varibalebytes) : Item n;

图50 监视变量表的响应报文

从图50中,得知DB100.DBW 2的值是61a8。

其它的子功能都比监视/修改变量表(VarTab)简单,在这就不一一介绍了,感兴趣的可以去研究研究。

未完待续~

*本文原创作者:LiukerTeam,本文属FreeBuf原创奖励计划文章,未经许可禁止转载

# 工控安全 # 西门子 # S7COMM
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
  • 0 文章数
  • 0 关注者
文章目录