• python3-编写netcat 工具


    前言

      该文章主要是描写如何使用Python3来编写网络监听工具。文章分为两个部分。首先,先第一部分来解说需要使用的代码功能与使用,为了让不太熟悉Python3语言的朋友能够更好的通过该文章学习。之后会带领各位来整体编写网络监听工具。

    天象独行

    2020-06-08

      第一部分:

      1;sys.argv[]

      我们想想看,常见使用一款工具的时候是不是打开是这样的格式:“工具名称 [选项] [输入内容]” 那么现在问题来了。我们输入的“[输入内容]”是如何进入到我们的代码当中呢?在我们的Python语言中使用函数sys.argv[]来获取命令行参数(从程序外部获取参数)。

      接下来我们来演示一下,该函数如何使用:

      我们来看看下面代码我们先了解以下他是一个什么类的数据:

    #导入sys库
    import sys
    #查看sys.argv是什么样的格式
    print(type(sys.argv))

      运行代码,我们发现它是一个列表:

    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$ python3.7 test.py sdf
    <class 'list'>

      现在我们知道了sys.argv是一个列表格式的数据。那么,我们大概能够理解。我们输入的参数将会以列表的形式保存在“sys.argv”。那么我们如何提取存入的参数呢?熟悉的朋友已经了解了。接下来,我们来通过代码来说明一下:

    #导入sys库
    import sys
    #输出输入参数
    print("输出第一个参数:%s" % sys.argv[0])
    print("输出第二个参数:%s" % sys.argv[1])
    print("输出第三个参数:%s" % sys.argv[2])
    print("输出第四个参数:%s" % sys.argv[3])

      运行代码我们发现sys.argv[0]表示的是文件本身,sys.argv[1] 开始是我们输入的第一位参数

    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$ python3.7 test.py 参数1 参数2 参数3
    输出第一个参数:test.py
    输出第二个参数:参数1
    输出第三个参数:参数2
    输出第四个参数:参数3
    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$ 

      

      2;getopt.getopt

      现在,我们已经了解了如何将外部的参数读取以列表的形式保存下来。我们来想想看在我们输入的参数当中有选项阿,那该怎么处理呢。我们可以慢慢提取出来。那这样是否过于繁琐呢。没有有一种方法能够帮我们处理这个问题呢。答案是有的。下面我们来看看getopt.getopt()方法。

      我们先了解一下getopt.getopt()方法的语法:

    getopt.getopt(args,options[,long_options])

      参数说明:

    • args: 表示需要解析的命令行列表
    • options:字符串格式定义。(如果选项有冒号则表示该选项后面有参数)
    • long_options:列表格式定义。(如果long_option 选项有“=” 则表示选项后面有参数)

      现在我们已经知道了该方法的使用,下面我们来用代码感受一下该方法的使用

    #导入sys库
    import sys
    import getopt
    
    #输出输入参数
    a = getopt.getopt(sys.argv[1:],'t:p:',["target=","port="])
    
    print(a)

      我们来尝试运行一下代码,我们发现输入的(选项和命令参数)是以元组的数据格式来保存的。

    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$ python3.7 test.py -t 192.168.1.1 -p 80
    ([('-t', '192.168.1.1'), ('-p', '80')], [])
    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$ python3.7 test.py --target 192.168.1.1 --port 80
    ([('--target', '192.168.1.1'), ('--port', '80')], [])
    (Studying) aaron@MiWiFi-R3-srv:~/PycharmProjects/Studying$

      

      3;socket

      我们都知道,我们需要编写的代码是需要通过网络来传输数据的,那么我们如何通过Python3来处理这个问题呢。这里我们需要使用socket模块。接下来,我们来重点关注一下该模块:

      在这之前,我们现了解一下“套接字(socket)”。所谓的“套接字”是一种计算机网络数据结构,任何类型的通讯开始之前是一定要创建套接字。套接字主要是分为两个部分:基于文件和面向网络。下面我们特别说明面向网络的套接字。我们知道在网络当中,有TCP连接和UDP链接。面对两种不同的方式,我们当然需要不同的套接字。

    • TCP/IP套接字
    tcpSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    • UDP/IP套接字
    udpSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

      套接字创建完成之后,我们可以利用套接字当中的方法来进行发送数据,监听,接收数据:详细方法如下图:

     

       4;subprocess.check_output()

      方法的作用是执行指定命令。

      接下来,我们来用实例来看一下效果:很明显,我们的命令“df -h”已经被执行。

    >>> import subprocess
    >>> subprocess.check_output("df -h",shell=True)
    b'xe6x96x87xe4xbbxb6xe7xb3xbbxe7xbbx9f        xe5xaexb9xe9x87x8f  xe5xb7xb2xe7x94xa8  xe5x8fxafxe7x94xa8 xe5xb7xb2xe7x94xa8% xe6x8cx82xe8xbdxbdxe7x82xb9
    udev            3.9G     0  3.9G    0% /dev
    tmpfs           794M  1.5M  793M    1% /run
    /dev/sda1       110G   58G   47G   56% /
    tmpfs           3.9G   75M  3.9G    2% /dev/shm
    tmpfs           5.0M     0  5.0M    0% /run/lock
    tmpfs           3.9G     0  3.9G    0% /sys/fs/cgroup
    tmpfs           794M  8.0K  794M    1% /run/user/1000
    '
    >>> 

        

      第二部分:

      好了,第一部分我们已经说明了编写该工具着重需要注意的点。那么接下来我们来正式进入编写程序的阶段

      1;我们先试想一下,我们的netcat 该有什么样的功能呢?文件上传,执行命令,shell,监听对吧。是不是还需要设定目标地址呢?还需要设定端口是把。好的,那接下来,我们再来想想。该如何去做是不是我们自己选择的。执行命令是不是我们来输入命令呢?好的。想到这儿,很明显了。我们需要用到第一部分sys.argv[] 和 getopt.getopt() 两个知识点呢?那么接下来我们看代码:

      1.1 ;首先我们定义一些变量,这些变量就是用来区分不同的选项的。不明白的可以稍候继续看下去:

    #定义全局变量
    listen = False
    command = False
    upload = False
    execute = ""
    target = ""
    upload_destination = ""
    port = 0

      1.2;这些变量好了,那么我们需要考虑的是如何将外部参数上传到内部。详细,我们看下面代码:

    opts,args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",["help","listen","execute","target","port","command","upload"])

      我们通过sys.argv将外部参数送进来并且通过getopt.getopt()来整理这些输入的参数。(注意,变量opts 用来存储定义的参数,变量args用来存储额外的参数)

      1.3;通过上面的代码,我们已经能够将外部参数已元组的形式保存在变量opts 当中了。那么,我们如何将数据保存下来并且使用呢。我们都知道不同的选项代表不同的功能。那么,变量opts当中存在什么选项就代表用户想要执行什么样的功能对不对?那么如何确定呢?可以通过for 循环来逐步提取出来进行对比。就可以发现opts变量中的选项了。好的,想法已经有了。下面我们落实到代码上面:

        for o,a in opts:
            if o in ("-h","--help"):
                usage()
            elif o in ("-l","--listen"):
                listen = True
            elif o in ("-e","--execute"):
                execute =a
            elif o in ("-c","--commandshell"):
                command = True
            elif o in ("-u","--upload"):
                upload_destination = a
            elif o in ("-t","--target"):
                target = a
            elif o in ("-p","--port"):
                port = int(a)
            else:
                assert False , "Unhandled Option"

      上面代码可以遍历变量当中opts中的值并且分别放入到变量“o”,“a” 。在循环中,通过if语句对比in语句当中的内容,对比成功则执行对应的操作。

      2;现在我们已经成功的完成了输入不同选项对应了不同的功能。那么下面我们开始补全这些功能。

      2.1;usage() 功能

      这里我们通过关键字def来定义一个功能函数,该功能函数主要是选项“-h”下的功能,用来提示如何使用该代码。

    def usage():
        print("BHP Net Tool")
        print("-----------------------------------------------------------------------------------------------")
        print("Usage: netcat_python_studying.py -t target_host -p port ")
        print("-l --listen    -listen on [host]:[port] for incoming connections")
        print("-e --execute=file_to_run    - execute the given file upon receiving a connection")
        print("-c --command    - initialize a command shell")
        print("-u --upload=destination    - upon receiving connection upload a file and write to [destination]")
        print("-----------------------------------------------------------------------------------------------")
        print("Examples: ")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -c")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -u=c:\target.exe")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -e="cat /etc/passwd"")
        print("echo 'sdfsdf' | ./netcat_python_studying.py -t 192.168.1.1 -p 123")
        sys.exit(0)

      2.2;run_command()

      代码功能当中需要执行命令,那么我们需要定义一个执行命令的函数,代码如下:

    def run_command(command):
        #换行
        command = command.rstrip()
    
        #运行命令并将输出返回
        try:
            output = subprocess.check_output(command,stderr=subprocess.STDOUT,shell=True)
        except:
            output = "Failed to execute command.
    "
    
        #将输出发送
        return output

      (代码解释:数据由参数command进入到函数内部,首先经过方法rstrip() 处理数据结尾的空格符号,之后有方法subprocess.check_output来执行命名,最后返回执行的结果output。)

      2.3;server_loop()

      监听功能是必要的,下面我们再来添加一下监听功能的函数,详细如下代码:

    def server_loop():
        global target
    
        #如果没有定义目标,那么我们监听所有接口
        if not len(target):
            target = "0.0.0.0"
    
        server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server.bind((target,port))
    
        server.listen(5)
    
        while True:
            client_socket,addr = server.accept()
            #分拆一个线程处理新的客户端
            client_thread = threading.Thread(target=client_sender,args=(client_socket))
            client_thread.start()

      (代码解析:这里我们通过全局变量target 来确定监听的地址,这里需要创建一个TCP/IP套接字。创建完成之后使用方法bind来绑定端口地址,启动监听功能。设定一个死循环,并且启动被动链接方法accept。一旦链接执行函数client_sender )

      注意:client_sender 函数自定义函数。主要功能是发送数据。

      2.4;client_sender()

      我们需要一个发送数据的功能,那么我们来自定义一个,详细如下面代码:

    def client_sender(buffer):
        client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
        try:
            #链接目标主机
            client.connect((target,port))
    
            if len(buffer):
                client.send(buffer)
    
            while True:
                #现在等待数据回传
                recv_len = 1
                response = ""
    
                while recv_len:
                    data = client.recv(4096)
                    recv_len = len(data)
                    response += data
    
                    if recv_len < 4096:
                        break
    
                print(response)
    
                #等待更多的输入
                buffer = input("")
                buffer += "
    "
    
                #发送出去
                client.send(buffer)
    
        except:
            print("[*] Exception! Exiting.")
            #关闭链接
            client.close()

      2.5;client_handler()

       现在我们来完善一下上传文件功能,执行命令,shell 功能。详细如下代码:

    def client_handler(client_socket):
        global upload
        global execute
        global command
    
        #检测上传文件
        if len(upload_destination):
            #读取所有的字符并写下目标
            file_buffer = ""
            #持续读取数据知道没有符合的数据
            while True:
                data = client_socket.recv(1024)
    
                if not data:
                    break
                else:
                    file_buffer += data
    
            ##现在我们接收这些数据并将他们写出来
            try:
                file_descriptor = open(upload_destination,"wb")
                file_descriptor.write(file_buffer)
                file_descriptor.close()
    
                #确认文件已经写出来
                client_socket.send("Successfully saved file to %s
    " % upload_destination)
            except:
                client_socket.send("Failed to save file to %s
    " % upload_destination)
    
    
        #检查命令执行
        if len(execute):
            #运行命令
            output = run_command(execute)
    
            client_socket.send(output)
    
        #如果需要一个命令行shell,那么我们进入另一个循环
        if command:
            while True:
                #跳出一个窗口
                client_socket.send("<BHP:#> ")
    
                #现在我们接收文件直到发现换行符(enter key)
                cmd_buffer = ""
                while "
    " not in cmd_buffer:
                    cmd_buffer += client_socket.recv(1024)
    
                #返还命令输出
                response = run_command(cmd_buffer)
    
                #返回响应数据
                client_socket.send(response)

      2.6;现在我们的积木已经都有了(我喜欢将代码程序比作积木,不同的积木有不同的形状,不同的功能),那么我们现在来组建一下。

      外部程序参数进入的时候,我们来判断一下,如果条件listen不满足, target port 都有,我们就可以实行发送数据功能。如果listen启动,那么我们开启监听的功能。现在思路有了。那么我们来写下这串代码:

        # 我们是进行监听还是仅从标准输入发送数据?
        if not listen and len(target) and port > 0:
            #从命令行读取内存数据
            #这里将阻塞,所以不在标准输入发送数据时发送CTRL-D
            buffer = sys.stdin.read()
    
            #发送数据
            client_sender(buffer)
    
        #我们开始监听并准备上传文件,执行命令
        #放置一个反弹shell
        #取决于上面的命令行选项
        if listen:
            server_loop()

      3;现在我们将代码完整的展现一下:

    import sys
    import socket
    import getopt
    import threading
    import subprocess
    
    #定义全局变量
    listen = False
    command = False
    upload = False
    execute = ""
    target = ""
    upload_destination = ""
    port = 0
    
    def usage():
        print("BHP Net Tool")
        print("-----------------------------------------------------------------------------------------------")
        print("Usage: netcat_python_studying.py -t target_host -p port ")
        print("-l --listen    -listen on [host]:[port] for incoming connections")
        print("-e --execute=file_to_run    - execute the given file upon receiving a connection")
        print("-c --command    - initialize a command shell")
        print("-u --upload=destination    - upon receiving connection upload a file and write to [destination]")
        print("-----------------------------------------------------------------------------------------------")
        print("Examples: ")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -c")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -u=c:\target.exe")
        print("netcat_python_studying.py -t 192.168.1.1 -p 5555 -l -e="cat /etc/passwd"")
        print("echo 'sdfsdf' | ./netcat_python_studying.py -t 192.168.1.1 -p 123")
        sys.exit(0)
    
    def main():
        global listen
        global port
        global execute
        global command
        global upload_destination
        global target
    
        if not len(sys.argv[1:]):
            usage()
    
        #读取命令选项
        try:
            opts,args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",["help","listen","execute","target","port","command","upload"])
        except getopt.GetoptError as err:
            print(str())
            usage()
    
        for o,a in opts:
            if o in ("-h","--help"):
                usage()
            elif o in ("-l","--listen"):
                listen = True
            elif o in ("-e","--execute"):
                execute =a
            elif o in ("-c","--commandshell"):
                command = True
            elif o in ("-u","--upload"):
                upload_destination = a
            elif o in ("-t","--target"):
                target = a
            elif o in ("-p","--port"):
                port = int(a)
            else:
                assert False , "Unhandled Option"
    
        # 我们是进行监听还是仅从标准输入发送数据?
        if not listen and len(target) and port > 0:
            #从命令行读取内存数据
            #这里将阻塞,所以不在标准输入发送数据时发送CTRL-D
            buffer = sys.stdin.read()
    
            #发送数据
            client_sender(buffer)
    
        #我们开始监听并准备上传文件,执行命令
        #放置一个反弹shell
        #取决于上面的命令行选项
        if listen:
            server_loop()
    
    def client_sender(buffer):
        client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
        try:
            #链接目标主机
            client.connect((target,port))
    
            if len(buffer):
                client.send(buffer)
    
            while True:
                #现在等待数据回传
                recv_len = 1
                response = ""
    
                while recv_len:
                    data = client.recv(4096)
                    recv_len = len(data)
                    response += data
    
                    if recv_len < 4096:
                        break
    
                print(response)
    
                #等待更多的输入
                buffer = input("")
                buffer += "
    "
    
                #发送出去
                client.send(buffer)
    
        except:
            print("[*] Exception! Exiting.")
            #关闭链接
            client.close()
    
    def server_loop():
        global target
    
        #如果没有定义目标,那么我们监听所有接口
        if not len(target):
            target = "0.0.0.0"
    
        server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server.bind((target,port))
    
        server.listen(5)
    
        while True:
            client_socket,addr = server.accept()
            #分拆一个线程处理新的客户端
            client_thread = threading.Thread(target=client_sender,args=(client_socket))
            client_thread.start()
    
    def run_command(command):
        #换行
        command = command.rstrip()
    
        #运行命令并将输出返回
        try:
            output = subprocess.check_output(command,stderr=subprocess.STDOUT,shell=True)
        except:
            output = "Failed to execute command.
    "
    
        #将输出发送
        return output
    
    def client_handler(client_socket):
        global upload
        global execute
        global command
    
        #检测上传文件
        if len(upload_destination):
            #读取所有的字符并写下目标
            file_buffer = ""
            #持续读取数据知道没有符合的数据
            while True:
                data = client_socket.recv(1024)
    
                if not data:
                    break
                else:
                    file_buffer += data
    
            ##现在我们接收这些数据并将他们写出来
            try:
                file_descriptor = open(upload_destination,"wb")
                file_descriptor.write(file_buffer)
                file_descriptor.close()
    
                #确认文件已经写出来
                client_socket.send("Successfully saved file to %s
    " % upload_destination)
            except:
                client_socket.send("Failed to save file to %s
    " % upload_destination)
    
    
        #检查命令执行
        if len(execute):
            #运行命令
            output = run_command(execute)
    
            client_socket.send(output)
    
        #如果需要一个命令行shell,那么我们进入另一个循环
        if command:
            while True:
                #跳出一个窗口
                client_socket.send("<BHP:#> ")
    
                #现在我们接收文件直到发现换行符(enter key)
                cmd_buffer = ""
                while "
    " not in cmd_buffer:
                    cmd_buffer += client_socket.recv(1024)
    
                #返还命令输出
                response = run_command(cmd_buffer)
    
                #返回响应数据
                client_socket.send(response)
    
    if __name__ == "__main__":
        main()

      

  • 相关阅读:
    2019/1/17 break语句小练习
    2019/1/17goto语句小试牛刀
    python 中* 和**的作用
    python 元组编码和解码问题
    python SMTP 发送邮件
    python 自定义异常
    python websocket client 使用
    excel、xls文件读写操作
    windows10局域网实现文件共享
    django入门
  • 原文地址:https://www.cnblogs.com/aaron456-rgv/p/13065801.html
Copyright © 2020-2023  润新知