在建立虛擬化實驗環境並熟悉 SCADA 系統的基本操作後,本章將焦點轉向構成工業自動化神經網絡的通訊協定。工業協定是設備間溝通的語言,其運作機制與資料結構直接影響系統的穩定性與安全性。本篇將從一個宏觀視角,系統性地梳理 Modbus、Ethernet/IP、DNP3 等主流協定的特性與應用差異,為理解複雜的工業網路環境奠定基礎。隨後,我們將深入剖析 Modbus TCP 的核心原理,包含其封包結構、暫存器模型與功能碼的具體作用,展示這些協定如何精確地執行讀寫操作,並為後續利用這些協定進行系統控制與安全分析的實踐環節,提供必要的理論支撐。
第八章:工業協議202
玄貓認為,深入理解工業協議是掌握工業控制系統安全的核心。本章將帶您進入工業協議的深層次探討,了解它們如何運作,以及如何利用這些協議來控制工業網路中的系統。
結構:
本章將涵蓋以下主要議題:
- 工業協議:介紹工業領域中常見的各種協議。
- Modbus速成課程:深入探討Modbus協議的原理和應用。
- 透過Ethernet/IP控制燈號:實踐性地展示如何使用Ethernet/IP協議控制實際設備。
技術要求:
為了完成本章的實踐操作,您將需要以下環境:
- 一台已安裝並運行
pymodbus套件的PLC虛擬機。 - 一台已安裝並運行
cpppo套件的PLC虛擬機。 - 一台已安裝並運行
mbtget工具的SCADA虛擬機。
前言:
我們已經完成了本書的一半以上內容,並涵蓋了許多重要知識。我們安裝了ESXi伺服器和多個虛擬機,並設定了PLC以與這些虛擬機連接。我們還將I/O連接到PLC並構建了一個燈塔。在安裝Ignition SCADA並將其連接到實驗室的PLC之後,我們使用各種工具掃描了我們的安裝,發現了開發人員可能在基於Web的SCADA系統上留下的開放埠和路徑。
在接下來的章節中,我們將深入研究管理工業設備的協議,以及如何利用這些協議來控制工業網路中的系統。我們將利用在第一章中創建的虛擬機來生成協議特定的流量,然後使用Wireshark和TShark工具深入分析協議,就像我們在第六章「封包深度分析」中所做的那樣。玄貓希望您在閱讀本書時能注意到,每一章都建立在前一章的基礎之上,不斷強化您已獲得的技能,並引入新的知識點,這些知識點將在後續章節中進一步闡述。
工業協議深度解析:從Modbus到Ethernet/IP
玄貓認為,理解工業協議的多樣性與特性,是深入工業控制系統安全領域的基礎。本節將探討多種關鍵工業協議,特別聚焦於Modbus和Ethernet/IP,並為後續的實踐操作奠定理論基礎。
工業協議概覽:
在工業領域,協議的選擇往往因地理位置、行業特性和供應商偏好而異。雖然我們的Koyo CLICK PLC支援Modbus和Ethernet/IP,但玄貓認為有必要擴展視野,快速瀏覽一些您可能遇到的重要工業協議。
1. Modbus:
- 特性:許多控制應用程式首先在Modbus環境中開發,然後再轉換為其他協議進行評估。這表明Modbus在工業通訊中的基礎地位。
- 應用:它是最普遍的工業協議之一,通常用於連接各種工業設備。
- 埠號:最常用的埠號是502。
2. Ethernet/IP:
- 特性:這是一個全球性的協議,常見於Rockwell設備,也因其在Ethernet/IP堆疊中的韌性而被廣泛使用。
- 底層協議:通用工業協議(Common Industrial Protocol, CIP)透過Ethernet/IP傳輸。
- 埠號:最常用的埠號是44818。
3. DNP3 (Distributed Network Protocol 3):
- 特性:SCADA系統使用此協議連接電力和水務領域的製程設備。它是一個開放標準,已獲得國際認可,但在北美市場應用最廣泛。
- 埠號:最常用的埠號是20000。
4. Siemens S7 Protocol:
- 特性:由西門子開發,最初是一個閉源協議(基於ISO 8073 Class 0),用於連接西門子設備。它曾是全球控制自動化市場的領導者,特別是在歐洲。
- 安全事件:因Stuxnet攻擊中針對伊朗核計畫的設備和協議而聞名。
- 安全改進:S7+的開發旨在解決重放攻擊等安全威脅。
- 埠號:最常用的埠號是102。
5. MELSEC (Mitsubishi Electric System Controller):
- 特性:由三菱電機開發,在日本各行業中廣泛使用。
- 埠號:最常用的埠號是5007和5006。
其他值得注意的協議:
- BSAP (Bristol Standard Asynchronous Protocol):在石油和天然氣行業中應用。
- GE SRTP (GE Service Request Transport Protocol):幾乎所有通用電氣設備都使用。
- BACnet (Building Automation and Control Network):廣泛用於建築管理系統,控制暖通空調等。值得一提的是,2013年Target數據洩露事件中,BACnet被用作入侵點。
- CANBus (Controller Area Network Bus):起源於1980年代,已成為交通運輸領域的事實標準,包括汽車、船舶、飛機和農業設備等,是自動駕駛汽車的基礎協議。
未來趨勢:
隨著物聯網(IoT)和工業物聯網(IIoT)在工業領域的普及,Message Queuing Telemetry Transport (MQTT)、ZigBee、Advanced Message Queuing Protocol (AMQP)等協議也將日益重要。
此圖示:工業協議分類與特性
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
rectangle "工業協議" as industrial_protocols {
folder "通用協議" as general_protocols {
component "Modbus" as modbus
component "Ethernet/IP (CIP)" as ethernet_ip
component "DNP3" as dnp3
}
folder "供應商特定協議" as vendor_protocols {
component "Siemens S7" as siemens_s7
component "MELSEC" as melsec
component "BSAP" as bsap
component "GE SRTP" as ge_srtp
}
folder "特定應用協議" as app_protocols {
component "BACnet" as bacnet
component "CANBus" as canbus
}
folder "IIoT協議" as iiot_protocols {
component "MQTT" as mqtt
component "ZigBee" as zigbee
component "AMQP" as amqp
}
}
modbus -- "埠號: 502"
ethernet_ip -- "埠號: 44818"
dnp3 -- "埠號: 20000"
siemens_s7 -- "埠號: 102"
melsec -- "埠號: 5006, 5007"
modbus .down.> general_protocols
ethernet_ip .down.> general_protocols
dnp3 .down.> general_protocols
siemens_s7 .down.> vendor_protocols
melsec .down.> vendor_protocols
bsap .down.> vendor_protocols
ge_srtp .down.> vendor_protocols
bacnet .down.> app_protocols
canbus .down.> app_protocols
mqtt .down.> iiot_protocols
zigbee .down.> iiot_protocols
amqp .down.> iiot_protocols
note bottom of modbus
最常用, 開放標準
end note
note bottom of ethernet_ip
Rockwell設備常見, CIP底層
end note
note bottom of dnp3
電力水務SCADA, 北美流行
end note
note bottom of siemens_s7
西門子專有, Stuxnet攻擊
end note
note bottom of bacnet
樓宇管理, Target數據洩露
end note
@enduml看圖說話:
此圖示展示了工業協議的分類與主要特性。它將工業協議分為通用協議(如Modbus、Ethernet/IP、DNP3)、供應商特定協議(如Siemens S7、MELSEC、BSAP、GE SRTP)、特定應用協議(如BACnet、CANBus)以及IIoT協議(如MQTT、ZigBee、AMQP)。圖中標示了各協議的典型埠號,並透過註釋簡要說明了其關鍵特性和應用場景。例如,Modbus因其開放標準和廣泛應用而成為最常用的協議之一;Ethernet/IP則常見於Rockwell設備,並以CIP作為底層;Siemens S7協議則因Stuxnet攻擊而聞名。此圖清晰地呈現了工業協議的多元面貌,有助於玄貓理解不同協議在工業網路中的角色和潛在安全風險。
Modbus速成課程:
Modbus協議起源於1970年代,最初是一個串列協議,用於在工業製程中透過共享匯流排連接設備。自發布以來,Modbus經歷了多次修改和演變,這主要歸因於其開放和靈活的標準。由於它是連接工業設備最廣泛使用的協議,因此有大量的文獻和資源討論它。玄貓將重點關注Modbus TCP及其不同的命令和功能。
Modbus TCP深度解析與實作:從理論到伺服器建構
玄貓認為,Modbus協議作為工業領域的基石,其深入理解對於工業控制系統的安全至關重要。本節將詳細解析Modbus TCP的運作機制、資料結構,並引導您建構一個Modbus伺服器進行實作。
Modbus TCP的演進與運作:
Modbus TCP是Modbus協議的現代化版本,它將Modbus RTU的封包封裝在TCP封包中,使其能夠透過IP網路傳輸,這相較於早期的RS-232或RS-485串列通訊方式是一個巨大的進步。Modbus TCP採用客戶端-伺服器架構,允許客戶端與多個伺服器進行互動,交換操作和控制數據。
Modbus資料結構:暫存器與位元大小
Modbus協議使用不同的暫存器來處理操作和控制的輸入與輸出。這些暫存器根據其功能和資料型態,具有特定的位元大小。
此圖示:Modbus暫存器類型與特性
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
rectangle "Modbus 暫存器類型" as registers {
rectangle "離散輸入 (Discrete Inputs)" as di
rectangle "線圈 (Coils)" as co
rectangle "輸入暫存器 (Input Registers)" as ir
rectangle "保持暫存器 (Holding Registers)" as hr
}
di -- "1 位元 (只讀)"
co -- "1 位元 (讀寫)"
ir -- "16 位元 (只讀)"
hr -- "16 位元 (讀寫)"
note bottom of di
用於讀取物理開關狀態等
end note
note bottom of co
用於控制繼電器、燈號等
end note
note bottom of ir
用於讀取感測器數據等
end note
note bottom of hr
用於設定參數、讀取數值等
end note
@enduml看圖說話:
此圖示清晰地展示了Modbus協議中的四種主要暫存器類型及其特性。**離散輸入(Discrete Inputs)和線圈(Coils)**都是1位元的資料,其中離散輸入是只讀的,常用於讀取物理開關的狀態;線圈則是讀寫的,常用於控制繼電器或燈號的開關。**輸入暫存器(Input Registers)和保持暫存器(Holding Registers)**都是16位元的資料,其中輸入暫存器是只讀的,常用於讀取感測器數據;保持暫存器則是讀寫的,常用於設定參數或讀取數值。這些暫存器的不同特性決定了它們在工業控制系統中扮演的角色,對於理解Modbus通訊的數據交換機制至關重要。
在第三章「安裝與實驗室」中,我們在梯形邏輯中使用了**觸點(contacts)和線圈(coils)**來控制燈號的開關。這些線圈和離散輸入的位元大小都是1位元,如上表所示。當我們透過GUI直接開啟或關閉燈號時,工程軟體會發送一個包含數據、功能碼和一個或多個暫存器的封包。功能碼(Function Code)指定了PLC的預期行為以及如何處理後續的暫存器。例如,在我們控制燈號的簡單範例中,我們使用功能碼5(寫入單個線圈)向線圈1發送一個1位元的封包,其值為1,以開啟燈號。
Modbus協議的標準功能碼:
Modbus協議定義了一系列標準功能碼,用於執行不同的操作。以下是一些常見的功能碼:
- 1 (0x01):讀取線圈狀態 (Read Coils)
- 2 (0x02):讀取離散輸入狀態 (Read Discrete Inputs)
- 3 (0x03):讀取保持暫存器 (Read Holding Registers)
- 4 (0x04):讀取輸入暫存器 (Read Input Registers)
- 5 (0x05):寫入單個線圈 (Write Single Coil)
- 6 (0x06):寫入單個保持暫存器 (Write Single Register)
- 15 (0x0F):寫入多個線圈 (Write Multiple Coils)
- 16 (0x10):寫入多個保持暫存器 (Write Multiple Registers)
建構Modbus伺服器:
玄貓認為,親自動手實作是最好的學習方式。回想第一章「使用」中,我們在PLC和SCADA虛擬機上安裝了pymodbus和mbtget兩個應用程式。接下來,我們將設定一個伺服器和一個客戶端,編寫一些基本的通訊程式,然後使用Wireshark監聽網路,檢查我們傳輸的流量。
1. 建立Modbus伺服器程式碼:
將以下Python原始碼複製並貼上到您的PLC虛擬機中,並命名為server.py:
#!/usr/bin/env python
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import (ModbusRtuFramer, ModbusAsciiFramer, ModbusBinaryFramer)
from pymodbus.server.sync import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.version import version
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_async_server():
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = version.short()
StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020))
if __name__ == "__main__":
run_async_server()
2. 運行Modbus伺服器:
在PLC虛擬機的終端機中,執行以下命令來啟動伺服器:
python3 server.py
如果一切順利,您將看到伺服器啟動並顯示相關的日誌訊息。
此圖示:Modbus TCP伺服器建構流程
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "攻擊者 (玄貓)" as attacker
rectangle "PLC 虛擬機" as plc_vm {
component "Python 環境" as python_env
file "server.py" as server_file
component "Pymodbus 庫" as pymodbus_lib
component "Modbus TCP 伺服器" as modbus_server
}
attacker --> server_file : 建立 server.py 程式碼
attacker --> plc_vm : 執行 `python3 server.py`
server_file --> python_env : 載入
python_env --> pymodbus_lib : 引用 Pymodbus 庫
pymodbus_lib --> modbus_server : 初始化 Modbus 伺服器
modbus_server --> modbus_server : 設定 ModbusSlaveContext (DI, CO, IR, HR)
modbus_server --> modbus_server : 設定 ModbusServerContext
modbus_server --> modbus_server : 設定 ModbusDeviceIdentification
modbus_server --> modbus_server : 監聽 0.0.0.0:5020
note right of modbus_server
伺服器已啟動,等待客戶端連接
end note
@enduml看圖說話:
此圖示展示了Modbus TCP伺服器的建構流程。攻擊者(玄貓)在PLC虛擬機上,首先建立了一個名為server.py的Python程式碼檔案。隨後,玄貓執行python3 server.py命令來啟動伺服器。這個程式碼在Python環境中載入,並引用了Pymodbus庫。Pymodbus庫負責初始化Modbus TCP伺服器,並設定了ModbusSlaveContext來定義離散輸入(DI)、線圈(CO)、輸入暫存器(IR)和保持暫存器(HR)的初始值和範圍。同時,也設定了ModbusServerContext和ModbusDeviceIdentification,最後,伺服器開始監聽0.0.0.0:5020埠,等待客戶端的連接。這個流程清晰地描繪了如何透過Pymodbus庫在Python中快速建構一個Modbus TCP伺服器。
伺服器啟動後,我們將連接到SCADA虛擬機,並使用mbtget命令作為客戶端來查詢虛擬PLC上的暫存器。
評估此發展路徑的長期效益後,本章內容標誌著工控安全專家從理論認知到實踐能力的關鍵躍遷。許多從業人員滿足於被動的封包分析,但真正的專業深度源於主動交互。透過親手建構Modbus伺服器,讀者完成了從「知曉規則」到「駕馭協議」的質變,這種將抽象知識轉化為具體程式碼的過程,其價值遠超過單純記憶協議特性。未來,以程式化方式與工業協議互動,將成為區分高階人才與否的關鍵能力,也是開發客製化攻防策略的基礎。玄貓認為,這條從理論到實作的修煉路徑,是為個人職涯建立深厚技術護城河不可或缺的基石,值得深度投資。