黑帽子Python——基础的网络编程工具——取代netcat

学习自Python黑帽子(第二版)——第二章



前言

最终没有运行成功😭,应该是可以成功监听端口的,但是以客户端方式运行脚本的时候,在命令行中无论是输入EOF还是按ctrl+D都没有反应没有书中的效果。Google和百度了,找到了源码中的错误,但是改了之后依然输入EOF没有反应。Debug了好几天,放弃了,毕竟只是储备量不足实在不知道哪里出了问题。

netcat源码

import argparse
#提供了一种方便的方法来解析命令行参数,使您的工具更加灵活,用户友好。
import socket
#提供了套接字编程所需的基础功能,如创建、连接、发送和接收数据。
import shlex
#提供了一种简单的方式来解析和处理命令行参数字符串。
import subprocess
#提供了一种执行子进程的方法,以便您的工具可以执行系统命令和程序。
import sys
#提供了与Python解释器交互的功能,包括访问命令行参数和标准输入/输出。
import textwrap
#提供了一种方便的方法来格式化文本,使其在终端上更易读。
import threading
#提供了一种创建和管理线程的方法,以便您的工具可以同时处理多个连接。

def execute(cmd):
    cmd = cmd.strip()
    if not cmd:
        return
    output = subprocess.check_output(shlex.split(cmd),stdeer=subprocess.STDOUT)
    return output.decode()
'''
该函数首先通过strip()方法去除命令字符串两端的空格和换行符,然后判断该命令是否为空字符串。
如果是,则返回None。如果不是空字符串,则使用subprocess.check_output()方法执行该命令。
这个方法会调用操作系统的shell执行该命令,并返回该命令的输出结果。shlex.split()方法
可以将命令字符串分割成一个包含各个参数的列表。函数还将stdeer参数设置为subprocess.STDOUT,
以便将命令的标准错误输出流也重定向到标准输出流中,从而避免错误输出被忽略。最后,使用
decode()方法将命令输出的字节串转换为Unicode字符串,并将其作为函数返回值返回。
'''

class NetCat:
    def __init__(self,args,buffer=None):
        self.args = args
        self.buffer = buffer
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)

    def run(self):
        if self.args.listen:
            self.listen()
        else:
            self.send()
    '''
    这是 NetCat 类的定义。该类封装了使用 socket 模块进行网络通信的逻辑。
    __init__ 方法是类的构造函数,用于初始化对象的状态。在这个函数中,它接收两个参数:args 和 buffer,
    并将它们保存为对象的属性,分别为 self.args 和 self.buffer。另外,它创建了一个 socket 对象,
    使用 AF_INET 和 SOCK_STREAM 参数分别表示要使用 IPv4 协议和 TCP 协议。并且通过 setsockopt 方法
    设置了 SO_REUSEADDR 选项,这是为了在同一台计算机上频繁绑定端口而避免出现错误。
    '''

    def send(self):
        self.socket.connect((self.args.target,self.args.port))
        if self.buffer:
            self.socket.send(self.buffer)
        
        try:
            while True:
                recv_len = 1
                response = ''
                while recv_len:
                    data = self.socket.recv(4096)
                    recv_len = len(data)
                    response += data.decode()
                    if recv_len < 1:#原文错误本来是“<4096”看文章说这里错了应该改成这样,但是还是不行。
                        break
                if response:
                    print(response)
                    buffer = input('>')
                    buffer += '\n'
                    self.socket.send(buffer.encode())
        except KeyboardInterrupt:
            print('User terminated.')
            self.socket.close()
            sys.exit()
    '''
    先连接到target和port,如果这时缓冲区里有数据的话,就先把这些数据发过去。接下来,创建一个try/catch块,
    这样就能直接用Ctrl+C组合键手动关闭连接。然后,创建一个大循环,一轮一轮地接收target返回的数据❸。
    在大循环里,我们建了一个小循环,用来读取socket本轮返回的数据。如果socket里的数据目前已经读到头,就退出小循环❹。
    接下来检查刚才有没有实际读出什么东西来,如果读出了什么,就输出到屏幕上,并暂停,等待用户输入新的内容,
    再把新的内容发给target❺,接着开始下一轮大循环。
    这个大循环将一直持续下去,直到你按下Ctrl+C组合键触发KeyboardInterrupt中断循环❻,这时也会关闭socket对象。
    '''

    def listen(self):
        self.socket.bind((self.args.target,self.args.port))
        self.socket.listen(5)
        while True:
            client_socket, _ = self.socket.accept()
            client_thread = threading.Thread(
                target=self.handle, args=(client_socket,)
            )
            client_thread.start()

    def handle(self,client_socket):
        if self.args.execute:
            output = execute(self.args.execute)
            client_socket.send(output.encode())

        elif self.args.upload:
            file_buffer = 'b'
            while True:
                data = client_socket.recv(4096)
                if data:
                    file_buffer += data
                else:
                    break

            with open(self.args.upload,'wb') as f:
                f.write(file_buffer)
            message = f'Saved file {self.args.upload}'
            client_socket.send(message.encode())

        elif self.args.command:
            cmd_buffer = b''
            while True:
                try:
                    client_socket.send(b'BHP: #>')
                    while '\n' not in cmd_buffer.decode():
                        cmd_buffer += client_socket.recv(64)
                    response = execute(cmd_buffer.decode())
                    if response:
                        client_socket.send(response.encode())
                    cmd_buffer = b''
                except Exception as e:
                    print (f'server killed {e}')
                    self.socket.close()
                    sys.exit()

if __name__== '__main__':
    parser = argparse.ArgumentParser(
        description = 'BHP Net Tool',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent('''Exmple:
            netcat.py -t 192.168.1.108 -p 5555 -l -c # command shell
            netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.txt # upload to file
            netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat/ect/passwd\" # execute command
            echo 'ABC' | ./netcat.py -t 192.168.1.108 -p 135 # echo text to server port 135
            netcat.py -t 192.168.1.108 -p 5555 # connect to server
        '''))    
    '''
    使用argparse.ArgumentParser()函数创建一个argparse解析器对象,并指定程序的描述信息和
    示例信息。formatter_class参数指定输出帮助信息时的格式,这里
    使用argparse.RawDescriptionHelpFormatter类来保留示例信息的格式。epilog参数则指定
    帮助信息的结尾部分,用于展示示例命令的用法。
    '''

    parser.add_argument('-c','--command',action='store_true',help='command shell')
    parser.add_argument('-e','--execute',help='execute specified command')
    parser.add_argument('-l','--listen',action='store_true',help='listen')
    parser.add_argument('-p','--port',type=int,default=5555,help='specified port')
    parser.add_argument('-t','--target',default='192.168.1.203',help='specified IP')
    parser.add_argument('-u','--upload',help='upload file')
    args = parser.parse_args()
    '''
    使用parser.add_argument()方法向解析器中添加参数。
    该方法可以接受多个参数,用于指定参数名称、参数值类型、默认值、帮助信息等。
    程序收到-c参数,就会打开一个交互式的命令行shell;收到-e参数,就会执行一条命令;收到-l参数,就会
    创建一个监听器;-p参数用来指定要通信的端口;-t参数用来指定要通信的目标IP地址;-u参数用来指定要上传的文件。
    因为发送方和接收方都会运行这个程序,所以传进来的参数会决定这个程序接下来是要发送数据还是要
    进行监听。使用了-c、-e和-u这三个参数,就意味着要使用-l参数,因为这些行为都只能由接收方来完成。
    而发送方只需要向接收方发起连接,所以它只需要用-t和-p两个参数来指定接收方。
    '''

    if args.listen:
        buffer = ''
    else:
        buffer = sys.stdin.read()

    nc = NetCat(args,buffer.encode())
    nc.run()
    '''
    使用parser.parse_args()方法解析命令行参数,并将结果存储在一个名为args的命名空间对象中。
    然后,根据-l参数的值,确定了程序是否要进行监听,我们就在缓冲区里填上空白数据,
    把空白缓冲区传给NetCat对象。反之,我们就把stdin里的数据通过缓冲区传进去。最后调用NetCat类的run函数来启动它。
    '''

后记

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值