使用Python

时间:2018-03-06 04:20:55

标签: python tcp ip port python-multithreading

我正在编写一个程序,需要扫描主机中的所有65535个端口,搜索那些打开的端口。这是我到目前为止所做的,并且它有效,但每次执行脚本时都会产生不同的结果,为什么会发生这种情况?

def check_open_port(host, port):
    s = socket.socket()
    s.settimeout(0.1)
    # the SO_REUSEADDR flag tells the kernel to reuse a local 
    # socket in TIME_WAIT state, without waiting for its natural
    # timeout to expire.
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    try:
        code = s.connect_ex((host, port))
        s.close()

        if code == 0:
            return True
        else:
            return False
    except socket.error:
        return False


def get_open_ports(host, max_port=65535):
    open_ports = []

    def worker(port):
        if check_open_port(host, port):
            open_ports.append(port)


    pool = ThreadPoolExecutor(max_workers=10000)
    [pool.submit(worker, port) for port in range(1, max_port + 1)]
    pool.shutdown(wait=True)

    return open_ports

例如,在端口22,80和443打开的主机中,有时我得到此响应:

[22, 80]

有时我得到:

[22, 80, 443]

甚至:

[22]

具有更多开放端口的主机产生更多组合。

我玩过max_workerssettimeout()值,但我无法让它运作良好。它唯一有效的时间是不使用线程,但显然需要花费很长时间才能完成,我需要使用它们。

我错过了什么吗?还有其他方法可以实现吗?

3 个答案:

答案 0 :(得分:7)

这里有2个问题:

  1. 我错过了什么
  2. 还有其他方法可以实现吗?
  3. 我错过了什么

    我认为值得在此检查错误代码:

    if code == 0: 
        return True
    else:
        return False
    

    鉴于您正在尝试运行池! 10K 线程可能会出现所有类型的错误 - 即达到某个系统/您的用户限制(请查看ulimit -a),您可以将此类错误视为封闭端口而不另行通知。它可能会解释您遇到的不稳定结果。

    在我的MacBook上BTW结果是一致的(在VPS主机上检查我的实时服务器)

    我也会选择较少数量的线程 - 10K 是一种过度杀伤力。例如,以下是python docs中建议的默认值:

      

    在版本3.5中更改:如果max_workers为None或未给出,则会   默认为机器上的处理器数量,乘以5,   假设ThreadPoolExecutor经常用于重叠I / O.   CPU工作量和工人数应高于数量   ProcessPoolExecutor的工作人员

    还有其他方法可以实现吗?

    首先,没有必要使用threads/processes - 非阻塞套接字+事件multiplexorsepoll已存在多年,因此您可以在没有的情况下离开额外的线程/处理过的任何内容。

    连接/关闭的方法也不是最理想的,因为您只需要检查端口是否打开 - 这里不需要全连接TCP连接。

    在最简单的情况下,您只需发送 SYN 段并检查服务器将响应的内容。

    这是一篇good文章,其中包含使用scapy

    的十几种方法
      

    Scapy是一个功能强大的交互式数据包操作程序。它是   能够伪造或解码大量协议的数据包,发送   他们在线上,捕获它们,匹配请求和回复等等   更多。它可以轻松处理大多数经典任务,如扫描,   跟踪路由,探测,单元测试,攻击或网络发现(它   可以取代hping,85%的nmap,arpspoof,arp-sk,arping,tcpdump,   tethereal,p0f等。)。

    以下是其中一种方法说明(“ TCP连接扫描”):

      

    客户端使用SYN标志和端口发送第一次握手   连接到TCP数据包中的服务器。如果服务器响应a   RST而不是SYN-ACK,然后该特定端口关闭   服务器

    还有一种方法(“ TCP隐身扫描”):

      

    此技术类似于TCP连接扫描。客户端发送一个   设置了SYN标志的TCP数据包和要连接的端口号。如果   端口打开,服务器响应SYN和ACK标志   在TCP数据包中。但这次客户端发送一个RST标志   TCP数据包而不是RST + ACK,这是TCP连接中的情况   扫描。该技术用于避免端口扫描检测   防火墙

    当然如果只是想玩套接字/线程,你的方法即使没有pcap / scapy也会没问题

答案 1 :(得分:5)

我在jupyter笔记本上尝试了你的代码,我总是得到相同的端口集:

get_open_ports('127.0.0.1')

输出:

[133, 200, 144...60700]

是否有可能在特定时间打开不同数量的端口以供查询主机?

为验证一小组端口,我将max_port缩减为10000,每次我仍然获得相同的端口集:

def get_open_ports(host, max_port=10000):
open_ports = []

def worker(port):
    if check_open_port(host, port):
        open_ports.append(port)

with ThreadPoolExecutor(max_workers=10000) as executor:
    [executor.submit(worker, port) for port in range(1, max_port + 1)]
    executor.shutdown(wait=True)
return open_ports

get_open_ports('127.0.0.1')

输出:[150, 900, 1035, 7789]

注意:出于安全考虑,我已经更改了端口号。

修改

def get_open_ports(host, max_port=65535):
    open_ports = []

    def worker(port):
        if check_open_port(host, port):
            open_ports.append(port)

# We can use a with statement to ensure threads are cleaned up promptly
    with ThreadPoolExecutor(max_workers=100) as executor:
        print('main:starting')
        wait_for=[executor.submit(worker,port) for port in range(1, max_port + 1)]
        for f in as_completed(wait_for):
            print('main: result: {}'.format(f.result())) #check result on each thread execution

#         executor.shutdown(wait=True)  #not required when using the 'with' statement
    return len(open_ports)

test = get_open_ports('45.60.112.163') #hostname for www.indracompany.com

#max_workers not defined & max_port=10000
# len(test)     #test1: 148
# len(test)     #test 2: 79

#max_workers = 10000 & max_port=65535
# len(test)      #test1: 1
# len(test)      #test2:1
# len(test)      #test3:1

#max_workers = 20000 & max_port=65535

# len(test)  #test1: 14
# len(test)  #test2:1
# len(test)  #test3: 1
# len(test)  #test4:1

#max_workers not defined & max_port=65535 #quite time-consuming
# len(test)   #test1: 63

编辑2:更可靠的解决方案

根据@Tarun的建议,Python的python-nmap库可以更好地扫描主机。

以下解决方案给出了准确的结果,但是,随着端口发现范围的增加,我观察到了性能上的重大折衷。也许,线程可以合并到代码中以提高性能。我还导入了时间库以最终获得程序执行时间。在测试性能时,这可用于比较目的。

# The python-nmap library helps to programmatically manipulate scanned results of nmap to automate port scanning tasks. 
# To use this library you must have the Nmap software installed. This can be installed from https://nmap.org/download.html.
# Network Mapper (Nmap) is a free and open-source tool used for network discovery and security auditing. 
# It runs on all major computer operating systems, and official binary packages are available for Linux, Windows, and Mac OS X.
# For Windows 7 and later, you must also upgrade 'NCap' from https://nmap.org/npcap/ 
# For Windows, make sure nmap.exe is added to PATH.
# When you're ready, pip install python-nmap

import time
import nmap
nm = nmap.PortScanner() #initialize PortScanner object
host = '45.60.112.163'  #specify host
nm.scan(host, '1-100') #run the scan, specify host and range of ports to scan

#Optional steps for verification:

#Output: nmap -oX - -p 1-100 -sV 45.60.112.163
print(nm.command_line()) #command_line command to execute on nmap command prompt

#Output: {'tcp': {'method': 'syn', 'services': '1-100'}}
print(nm.scaninfo())   #nmap scan information

#Now we can scan all hosts
#From Official documentation at https://xael.org/pages/python-nmap-en.html
start_time = time.time()   #To get program execution time
for host in nm.all_hosts(): 
    print('----------------------------------------------------')
    print('Host : %s (%s)' % (host, nm[host].hostname()))
    print('State : %s' % nm[host].state())
    for proto in nm[host].all_protocols():
        print('----------')
        print('Protocol : %s' % proto)
        lport = nm[host][proto].keys()
        for key in sorted(lport):
            for port in lport:
                print ('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
print('Execution time: %s seconds' % (time.time() - start_time))

    #Output:
    ----------------------------------------------------
    Host : 45.60.112.163 ()
    State : up
    ----------
    Protocol : tcp
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    port : 25       state : open
    port : 51       state : open
    port : 53       state : open
    port : 80       state : open
    port : 81       state : open
    port : 85       state : open
    port : 91       state : open
    Execution time: 0.015624761581420898 seconds

要将输出转换为csv,请使用:

print(nm.csv())

通过此调查,Nmap现已安装在我的计算机上。为了好玩,我还使用下面的命令在命令提示符上运行扫描。此扫描范围为1-1000'花了超过15分钟(我没有参加整个会议!)。

enter image description here

答案 2 :(得分:1)

我在使用python中的套接字时遇到过这个端口扫描程序......

import socket
import threading
from queue import *

print_lock = threading.Lock()
target = input("Enter websit or IP Adress to scan: ")
minPort = int(input("Enter minimum Port to scan (1 is the smallest): "))
maxPort = int(input("Enter maximum Port to scan: "))
threadNo = int(input("Enter No. of threads to use(500 is a good all around number): "))

def portscan(port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        con = s.connect((target, port))
        with print_lock:
            print("Port", port, "is open!")
        con.close()
    except:
        pass

def threader():
    while True:
        worker = q.get()
        portscan(worker)
        q.task_done()

q = Queue()

for x in range(threadNo):
    t = threading.Thread(target=threader)
    t.daemon = True
    t.start()

for worker in range (minPort, maxPort):
     q.put(worker)

q.join()

它运作良好,可以轻松调整:)