實(shí)例探究Python以并發(fā)方式編寫(xiě)高性能端口掃描器的方法
關(guān)于端口掃描器
端口掃描工具(Port Scanner)指用于探測(cè)服務(wù)器或主機(jī)開(kāi)放端口情況的工具。常被計(jì)算機(jī)管理員用于確認(rèn)安全策略,同時(shí)被攻擊者用于識(shí)別目標(biāo)主機(jī)上的可運(yùn)作的網(wǎng)絡(luò)服務(wù)。
端口掃描定義是客戶端向一定范圍的服務(wù)器端口發(fā)送對(duì)應(yīng)請(qǐng)求,以此確認(rèn)可使用的端口。雖然其本身并不是惡意的網(wǎng)絡(luò)活動(dòng),但也是網(wǎng)絡(luò)攻擊者探測(cè)目標(biāo)主機(jī)服務(wù),以利用該服務(wù)的已知漏洞的重要手段。端口掃描的主要用途仍然只是確認(rèn)遠(yuǎn)程機(jī)器某個(gè)服務(wù)的可用性。
掃描多個(gè)主機(jī)以獲取特定的某個(gè)端口被稱為端口清掃(Portsweep),以此獲取特定的服務(wù)。例如,基于SQL服務(wù)的計(jì)算機(jī)蠕蟲(chóng)就會(huì)清掃大量主機(jī)的同一端口以在 1433 端口上建立TCP連接。
Python實(shí)現(xiàn)
端口掃描器原理很簡(jiǎn)單,無(wú)非就是操作socket,能connect就認(rèn)定這個(gè)端口開(kāi)放著。
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))
這樣一個(gè)最簡(jiǎn)單的端口掃描器出來(lái)了。
等等喂,半天都沒(méi)反應(yīng),那是因?yàn)閟ocket是阻塞的,每次連接要等很久才超時(shí)。
我們自己給它加上的超時(shí)。
s.settimeout(0.1)
再跑一遍,感覺(jué)快多了。
多線程版本
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)行一下,哇,好快,快到拋出錯(cuò)誤了。thread.error: can't start new thread。
想一下,這個(gè)進(jìn)程開(kāi)啟了65535個(gè)線程,有兩種可能,一種是超過(guò)最大線程數(shù)了,一種是超過(guò)最大socket句柄數(shù)了。在linux可以通過(guò)ulimit來(lái)修改。
如果不修改最大限制,怎么用多線程不報(bào)錯(cuò)呢?
加個(gè)queue,變成生產(chǎn)者-消費(fèi)者模式,開(kā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()
這里開(kāi)500個(gè)線程,不停的從隊(duì)列取任務(wù)來(lái)做。
multiprocessing+隊(duì)列版本
總不能開(kāi)65535個(gè)進(jìn)程吧?還是用生產(chǎn)者消費(fèi)者模式
import multiprocessing
def scan(port):
s = socket.socket()
s.settimeout(0.1)
if s.connect_ex(('localhost', port)) == 0:
print port, 'open'
s.close()
def worker(q):
while not q.empty():
port = q.get()
try:
scan(port)
finally:
q.task_done()
if __name__ == '__main__':
q = multiprocessing.JoinableQueue()
map(q.put,xrange(1,65535))
jobs = [multiprocessing.Process(target=worker, args=(q,)) for i in xrange(100)]
map(lambda x:x.start(),jobs)
注意這里把隊(duì)列作為一個(gè)參數(shù)傳入到worker中去,因?yàn)槭莗rocess safe的queue,不然會(huì)報(bào)錯(cuò)。
還有用的是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,不然會(huì)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)
相關(guān)文章
python dict.get()和dict[''key'']的區(qū)別詳解
下面小編就為大家?guī)?lái)一篇python dict.get()和dict['key']的區(qū)別詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06
利用Python3實(shí)現(xiàn)統(tǒng)計(jì)大量單詞中各字母出現(xiàn)的次數(shù)和頻率的方法
這篇文章主要介紹了利用Python3實(shí)現(xiàn)統(tǒng)計(jì)大量單詞中各字母出現(xiàn)的次數(shù)和頻率,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Python實(shí)現(xiàn)蒙特卡洛算法小實(shí)驗(yàn)過(guò)程詳解
這篇文章主要介紹了Python實(shí)現(xiàn)基于蒙特卡洛算法過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
Pyqt5實(shí)戰(zhàn)小案例之界面與邏輯分離的小計(jì)算器程序
網(wǎng)上很多PyQt5信號(hào)槽與界面分離的例子,但是真正開(kāi)發(fā)起來(lái)很不方便,下面這篇文章主要給大家介紹了關(guān)于Pyqt5實(shí)戰(zhàn)小案例之界面與邏輯分離的小計(jì)算器程序,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
淺談python 導(dǎo)入模塊和解決文件句柄找不到問(wèn)題
今天小編就為大家分享一篇淺談python 導(dǎo)入模塊和解決文件句柄找不到問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
python 的numpy庫(kù)中的mean()函數(shù)用法介紹
這篇文章主要介紹了python 的numpy庫(kù)中的mean()函數(shù)用法介紹,具有很好對(duì)參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
python 使用elasticsearch 實(shí)現(xiàn)翻頁(yè)的三種方式
這篇文章主要介紹了python 使用elasticsearch 實(shí)現(xiàn)翻頁(yè)的三種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Python設(shè)計(jì)模式之門(mén)面模式簡(jiǎn)單示例
這篇文章主要介紹了Python設(shè)計(jì)模式之門(mén)面模式,簡(jiǎn)單描述了門(mén)面模式的概念、原理,并結(jié)合實(shí)例形式給出了Python定義與使用門(mén)面模式的具體操作技巧,需要的朋友可以參考下2018-01-01
Python基于Serializer實(shí)現(xiàn)字段驗(yàn)證及序列化
這篇文章主要介紹了Python基于Serializer實(shí)現(xiàn)字段驗(yàn)證及序列化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11

