使用外部工具、subprocess.Popen和线程进行多重端口扫描

1 投票
3 回答
809 浏览
提问于 2025-04-16 08:37

我正在使用一个端口扫描器来扫描我的子网。这个扫描器有个问题,它一次只能扫描一个主机的一个端口。而且,如果主机无法访问,扫描器会在1秒后超时。由于这个扫描器是一个外部程序,所以我需要通过subprocess.Popen()来运行它。为了加快速度,我使用了线程,这样我可以在等待之前的扫描结果时同时发送多个请求。不过,当我尝试对一个完整的/24子网进行扫描时,使用大量线程就出现了问题。有些实际上是开放的端口却显示为关闭。我怀疑输出结果可能出现了混乱。需要注意的是,如果我只扫描少数主机或者一次只扫描一台主机,这种情况就不会发生。

以下是我尝试创建一个线程池的代码,这个线程池会接收一个IP地址,并对指定的端口进行“顺序”扫描。一旦所有指定的端口都扫描完了,它就会从列表中取下一个IP。

        while True:
            if not thread_queue.empty():
                try:
                    hst = ip_iter.next()
                except StopIteration:
                    break
                m=thread_queue.get()
                l=ThreadWork(self,hst,m)
                l.start()
        while open_threads != 0:
            pass      

这段代码设置了线程队列。

        thread_list = [x for x in range(num_threads)]
        for t in thread_list:
            thread_queue.put(str(t))
        ip_iter=iter(self.final_target)

在ThreadWork函数中,我会跟踪当前的开放线程(因为thread_queue.empty这个方法不太可靠,所以我不得不使用这种粗暴的方法)。

class ThreadWork(threading.Thread):
    def __init__(self,i,hst,thread_no):
        global open_threads
        threading.Thread.__init__(self)
        self.host = hst
        self.ptr = i
        self.t = thread_no
        lock.acquire()
        open_threads = open_threads + 1
        lock.release() 

    def run(self):
        global thread_queue
        global open_threads
        global lock
        user_log.info("Executing sinfp for IP Address : %s"%self.host)
        self.ptr.result.append(SinFpRes(self.host,self.ptr.init_ports,self.ptr.all_ports,self.ptr.options,self.ptr.cf))
        lock.acquire()
        open_threads = open_threads - 1
        lock.release()
        thread_queue.put(self.t)

调用SinFpRes会为一个IP创建一个结果对象,并开始对该IP的端口进行顺序扫描。每个端口的实际扫描过程如下所示。

        com_string = '/usr/local/sinfp/bin/sinfp.pl '+self.options+' -ai '+str(self.ip)+' -p '+str(p)
        args = shlex.split(com_string) 
        self.result=subprocess.Popen(args,stdout=subprocess.PIPE).communicate()[0]
        self.parse(p)

解析函数会利用存储在self.result中的结果来保存该端口的输出。所有端口的汇总结果就是该IP的扫描结果。

使用10个线程运行这段代码时,输出结果是准确的(与nmap的输出相比)。当使用15个线程时,有时会漏掉一个开放的端口。使用20个线程时,漏掉的开放端口更多。使用50个线程时,漏掉的端口就很多了。

附言:作为一个初学者,这段代码有点复杂。对那些追求完美的人表示歉意。

再附言:即使是使用线程的端口扫描,对于一个完整的C类子网,扫描20个端口也需要15分钟。我在想是否应该把这段代码移到其他语言中,只用Python来解析结果。有没有人能推荐我一个语言?注意:我正在考虑S.Lott提到的Shell选项,但在将结果写入文件之前需要手动处理。

3 个回答

0

用Perl代替Python吧。这个程序(SinFP)是用Perl写的,你可以修改代码来满足你的需求。

1

你为什么不试试呢?

(回答:不,他们会有自己的管道)

1

使用命令行

 for h in host1 host2 host3
 do
     scan $h >$h.scan &
 done
 cat *.scan >all.scan

这样做会同时扫描所有的主机,每个主机在一个单独的进程中运行。没有使用线程。

每次扫描都会生成一个 .scan 文件。然后你可以把所有的 .scan 文件合并成一个大的 all.scan 文件,方便后续处理或者其他你想做的事情。

撰写回答