使用外部工具、subprocess.Popen和线程进行多重端口扫描
我正在使用一个端口扫描器来扫描我的子网。这个扫描器有个问题,它一次只能扫描一个主机的一个端口。而且,如果主机无法访问,扫描器会在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 个回答
用Perl代替Python吧。这个程序(SinFP)是用Perl写的,你可以修改代码来满足你的需求。
你为什么不试试呢?
(回答:不,他们会有自己的管道)
使用命令行
for h in host1 host2 host3
do
scan $h >$h.scan &
done
cat *.scan >all.scan
这样做会同时扫描所有的主机,每个主机在一个单独的进程中运行。没有使用线程。
每次扫描都会生成一个 .scan
文件。然后你可以把所有的 .scan
文件合并成一个大的 all.scan
文件,方便后续处理或者其他你想做的事情。