如何在Python中制作端口扫描器?代码实现教程

2021年11月16日16:29:07 发表评论 1,216 次浏览

Python如何制作端口扫描器?本文带你了解如何使用套接字在 Python 中编写端口扫描器,从简单的端口扫描器开始,然后深入研究可靠使用的端口扫描器的线程版本,包括Python制作端口扫描器示例

端口扫描是一种扫描方法,用于确定网络设备上的哪些端口是开放的,无论是服务器、路由器还是普通机器。端口扫描器只是一个脚本或程序,旨在探测主机的开放端口。

如何在Python中制作端口扫描器?在本教程中,你将能够使用套接字库在 Python 中制作自己的端口扫描器。这个简单的端口扫描器背后的基本思想是尝试通过端口列表连接到特定主机(网站、服务器或任何连接到 Internet/网络的设备),如果成功建立连接,则意味着端口开了。

例如,当你加载此网页时,你已在端口80上连接到此网站,类似地,此脚本将尝试连接到主机但在多个端口上。这类工具对黑客和渗透测试人员很有用,所以不要在没有权限测试的主机上使用这个工具!

另请阅读: 如何在 Python 中暴力破解 ZIP 文件密码。

或者,你需要安装colorama模块以进行彩色打印:

pip3 install colorama

简单的端口扫描器

Python如何制作端口扫描器?在本节中,我们将编写一个简单的端口扫描器,只是为了让你的手变脏,让我们从导入socket模块开始:

import socket # for connecting
from colorama import init, Fore

# some colors
init()
GREEN = Fore.GREEN
RESET = Fore.RESET
GRAY = Fore.LIGHTBLACK_EX

注意:socket模块已经安装在你的机器上,它是Python标准库中的内置模块,所以你不需要安装任何东西。

socket 模块为我们提供了 socket 操作、网络相关任务的函数等。它们在 Internet 上被广泛使用,因为它们支持任何网络的任何连接。任何网络通信都通过套接字进行,更多细节在官方 Python 文档中

我们将在此处使用colorama仅用于在端口打开时以绿色打印,在端口关闭时以灰色打印。

Python制作端口扫描器示例 - 让我们定义负责确定端口是否打开的函数:

def is_port_open(host, port):
    """
    determine whether `host` has the `port` open
    """
    # creates a new socket
    s = socket.socket()
    try:
        # tries to connect to host using that port
        s.connect((host, port))
        # make timeout if you want it a little faster ( less accuracy )
        # s.settimeout(0.2)
    except:
        # cannot connect, port is closed
        # return false
        return False
    else:
        # the connection was established, port is open!
        return True

s.connect((host, port))函数尝试将套接字连接到远程地址(host,port),当它无法连接到该主机时会引发异常,这就是为什么我们将该行代码包装到try-except块中,因此每当引发异常时,就是向我们表明该端口实际上已关闭,否则它是打开的。

现在让我们使用上面的函数并迭代一系列端口:

# get the host from the user
host = input("Enter the host:")
# iterate over ports, from 1 to 1024
for port in range(1, 1025):
    if is_port_open(host, port):
        print(f"{GREEN}[+] {host}:{port} is open      {RESET}")
    else:
        print(f"{GRAY}[!] {host}:{port} is closed    {RESET}", end="\r")

上面的代码将扫描从 1 一直到 1024 的端口,如果需要,你可以将范围更改为 65535,但这需要更长的时间才能完成。

当你尝试运行它时,你会立即注意到脚本非常慢,好吧,如果我们将超时设置为200毫秒左右(使用settimeout(0.2)方法),我们可以避免这种情况。但是,这实际上会降低侦察的准确性,尤其是当你的延迟非常高时。因此,我们需要一种更好的方法来加速这一过程。

 请阅读:如何在 Python 中使用 Shodan API。

快速端口扫描器

Python如何制作端口扫描器?现在让我们将简单的端口扫描器提升到更高的水平。在本节中,我们将编写一个能够同时扫描 200 个或更多端口的线程端口扫描器。

如何在Python中制作端口扫描器?下面的代码其实就是我们之前看到的那个函数,负责扫描单个端口。由于我们使用了threads,我们需要使用一个锁,这样一次只能打印一个线程,否则,输出会乱七八糟,我们什么也读不到, Python制作端口扫描器示例代码:

import argparse
import socket # for connecting
from colorama import init, Fore
from threading import Thread, Lock
from queue import Queue

# some colors
init()
GREEN = Fore.GREEN
RESET = Fore.RESET
GRAY = Fore.LIGHTBLACK_EX

# number of threads, feel free to tune this parameter as you wish
N_THREADS = 200
# thread queue
q = Queue()
print_lock = Lock()

def port_scan(port):
    """
    Scan a port on the global variable `host`
    """
    try:
        s = socket.socket()
        s.connect((host, port))
    except:
        with print_lock:
            print(f"{GRAY}{host:15}:{port:5} is closed  {RESET}", end='\r')
    else:
        with print_lock:
            print(f"{GREEN}{host:15}:{port:5} is open    {RESET}")
    finally:
        s.close()

所以这次函数没有返回任何东西,我们只想打印端口是否打开(尽管可以随意更改)。

我们使用Queue()内置队列模块中的类来帮助我们使用端口,以下两个函数用于使用端口号生成和填充队列并使用线程来使用它们:

def scan_thread():
    global q
    while True:
        # get the port number from the queue
        worker = q.get()
        # scan that port number
        port_scan(worker)
        # tells the queue that the scanning for that port 
        # is done
        q.task_done()


def main(host, ports):
    global q
    for t in range(N_THREADS):
        # for each thread, start it
        t = Thread(target=scan_thread)
        # when we set daemon to true, that thread will end when the main thread ends
        t.daemon = True
        # start the daemon thread
        t.start()
    for worker in ports:
        # for each port, put that port into the queue
        # to start scanning
        q.put(worker)
    # wait the threads ( port scanners ) to finish
    q.join()

scan_thread()function的工作是从队列中获取端口号并扫描它,然后将其添加到已完成的任务中,而main()function 负责用端口号填充队列并产生N_THREADS线程以使用它们。

请注意,q.get()将阻塞,直到队列中的单个项目可用。q.put()将单个项目放入队列并q.join()等待所有守护线程完成(清除队列)。

Python制作端口扫描器示例代码 - 最后,让我们创建一个简单的参数解析器,以便我们可以从命令行传递主机和端口号范围:

if __name__ == "__main__":
    # parse some parameters passed
    parser = argparse.ArgumentParser(description="Simple port scanner")
    parser.add_argument("host", help="Host to scan.")
    parser.add_argument("--ports", "-p", dest="port_range", default="1-65535", help="Port range to scan, default is 1-65535 (all ports)")
    args = parser.parse_args()
    host, port_range = args.host, args.port_range

    start_port, end_port = port_range.split("-")
    start_port, end_port = int(start_port), int(end_port)

    ports = [ p for p in range(start_port, end_port)]

    main(host, ports)

这是我尝试扫描路由器时的屏幕截图:

如何在Python中制作端口扫描器?代码实现教程

结论

惊人的!它在不到 2 秒的时间内完成了5000 个端口的扫描!你可以使用默认范围(1到65535),这需要几秒钟才能完成。

Python如何制作端口扫描器?如果你看到扫描仪在单个端口上冻结,则表明你需要减少线程数,如果你正在探测的服务器具有高 ping,则应减少N_THREADS 到100 、 50或什至更低,请尝试用这个参数做实验。

端口扫描在许多情况下被证明是有用的,授权的渗透测试人员可以使用此工具查看哪些端口是开放的并揭示潜在安全设备(如防火墙)的存在,以及测试网络安全性和设备的强度。

对于寻求弱点以访问目标机器的黑客来说,它也是一种流行的侦察工具。

大多数渗透测试人员经常使用nmap 来扫描端口,因为它不仅提供端口扫描,而且显示正在运行的服务和操作系统,以及更高级的技术。

请在此处查看两个脚本的完整版本。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: