实例探究Python以并发方式编写高性能端口扫描器的方法
來源:http://www.jb51.net/article/86615.htm
關(guān)于端口掃描器
端口掃描工具(Port Scanner)指用于探測服務(wù)器或主機(jī)開放端口情況的工具。常被計(jì)算機(jī)管理員用于確認(rèn)安全策略,同時被攻擊者用于識別目標(biāo)主機(jī)上的可運(yùn)作的網(wǎng)絡(luò)服務(wù)。
端口掃描定義是客戶端向一定范圍的服務(wù)器端口發(fā)送對應(yīng)請求,以此確認(rèn)可使用的端口。雖然其本身并不是惡意的網(wǎng)絡(luò)活動,但也是網(wǎng)絡(luò)攻擊者探測目標(biāo)主機(jī)服務(wù),以利用該服務(wù)的已知漏洞的重要手段。端口掃描的主要用途仍然只是確認(rèn)遠(yuǎn)程機(jī)器某個服務(wù)的可用性。
掃描多個主機(jī)以獲取特定的某個端口被稱為端口清掃(Portsweep),以此獲取特定的服務(wù)。例如,基于SQL服務(wù)的計(jì)算機(jī)蠕蟲就會清掃大量主機(jī)的同一端口以在 1433 端口上建立TCP連接。
Python實(shí)現(xiàn)
端口掃描器原理很簡單,無非就是操作socket,能connect就認(rèn)定這個端口開放著。
import socket def scan(port): s = socket.socket() if s.connect_ex(('localhost', port)) == 0: print port, 'open's.close() if __name__ == '__main__': map(scan,range(1,65536)) 這樣一個最簡單的端口掃描器出來了。等等喂,半天都沒反應(yīng),那是因?yàn)閟ocket是阻塞的,每次連接要等很久才超時。
我們自己給它加上的超時。 s.settimeout(0.1)
再跑一遍,感覺快多了。
多線程版本
import socket import threading def scan(port): s = socket.socket() s.settimeout(0.1) if s.connect_ex(('localhost', port)) == 0: print port, 'open's.close() if __name__ == '__main__': threads = [threading.Thread(target=scan, args=(i,)) for i in xrange(1,65536)] map(lambda x:x.start(),threads) 運(yùn)行一下,哇,好快,快到拋出錯誤了。thread.error: can't start new thread。
想一下,這個進(jìn)程開啟了65535個線程,有兩種可能,一種是超過最大線程數(shù)了,一種是超過最大socket句柄數(shù)了。在linux可以通過ulimit來修改。
如果不修改最大限制,怎么用多線程不報(bào)錯呢?
加個queue,變成生產(chǎn)者-消費(fèi)者模式,開固定線程。
多線程+隊(duì)列版本
import socket import threading from Queue import Queue def scan(port): s = socket.socket() s.settimeout(0.1) if s.connect_ex(('localhost', port)) == 0: print port, 'open's.close() def worker(): while not q.empty(): port = q.get() try: scan(port) finally: q.task_done() if __name__ == '__main__': q = Queue() map(q.put,xrange(1,65535)) threads = [threading.Thread(target=worker) for i in xrange(500)] map(lambda x:x.start(),threads) q.join()這里開500個線程,不停的從隊(duì)列取任務(wù)來做。
multiprocessing+隊(duì)列版本
總不能開65535個進(jìn)程吧?還是用生產(chǎn)者消費(fèi)者模式
注意這里把隊(duì)列作為一個參數(shù)傳入到worker中去,因?yàn)槭莗rocess safe的queue,不然會報(bào)錯。
還有用的是JoinableQueue(),顧名思義就是可以join()的。
gevent的spawn版本
from gevent import monkey; monkey.patch_all(); import gevent import socket ... if __name__ == '__main__': threads = [gevent.spawn(scan, i) for i in xrange(1,65536)] gevent.joinall(threads)注意monkey patch必須在被patch的東西之前import,不然會Exception KeyError.比如不能先import threading,再monkey patch.
gevent的Pool版本
from gevent import monkey; monkey.patch_all(); import socket from gevent.pool import Pool ... if __name__ == '__main__': pool = Pool(500) pool.map(scan,xrange(1,65536)) pool.join() concurrent.futures版本import socket from Queue import Queue from concurrent.futures import ThreadPoolExecutor ... if __name__ == '__main__': q = Queue() map(q.put,xrange(1,65536)) with ThreadPoolExecutor(max_workers=500) as executor: for i in range(500): executor.submit(worker,q)
總結(jié)
以上是生活随笔為你收集整理的实例探究Python以并发方式编写高性能端口扫描器的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓系统内 的 安卓虚拟机
- 下一篇: 初识网络