Python 實(shí)現(xiàn)端口掃描器

適合有一點(diǎn)Python編程基礎(chǔ)的學(xué)員學(xué)習(xí)

實(shí)現(xiàn)的原理

最簡(jiǎn)單的端口掃描工具使用TCP連接掃描的方式,即利用操作系統(tǒng)原生的網(wǎng)絡(luò)功能,且通常作為SYN掃描的替代選項(xiàng)。Nmap將這種模式稱(chēng)為連接掃描,因?yàn)槭褂昧祟?lèi)似Unix系統(tǒng)的connect()命令腾务。如果該端口是開(kāi)放的,操作系統(tǒng)就能完成TCP三次握手削饵,然后端口掃描工具會(huì)立即關(guān)閉剛建立的該連接岩瘦,防止拒絕服務(wù)攻擊。這種掃描模式的優(yōu)勢(shì)是用戶(hù)無(wú)需特殊權(quán)限窿撬。但使用操作系統(tǒng)原生網(wǎng)絡(luò)功能不能實(shí)現(xiàn)底層控制启昧,因此這種掃描方式并不流行。并且TCP掃描很容易被發(fā)現(xiàn)尤仍,尤其作為端口清掃的手段:這些服務(wù)會(huì)記錄發(fā)送者的IP地址箫津,入侵檢測(cè)系統(tǒng)可能觸發(fā)警報(bào)。

還有另外一種掃描方式是SYN掃描宰啦,端口掃描工具不使用操作系統(tǒng)原生網(wǎng)絡(luò)功能苏遥,而是自行生成、發(fā)送IP數(shù)據(jù)包赡模,并監(jiān)控其回應(yīng)田炭。這種掃描模式被稱(chēng)為“半開(kāi)放掃描”,因?yàn)樗鼜牟唤⑼暾腡CP連接漓柑。端口掃描工具生成一個(gè)SYN包教硫,如果目標(biāo)端口開(kāi)放叨吮,則會(huì)返回SYN-ACK包。掃描端回應(yīng)一個(gè)RST包瞬矩,然后在握手完成前關(guān)閉連接茶鉴。如果端口關(guān)閉了但未使用過(guò)濾,目標(biāo)端口應(yīng)該會(huì)持續(xù)返回RST包景用。這種粗略的網(wǎng)絡(luò)利用方式有幾個(gè)優(yōu)點(diǎn):給掃描工具全權(quán)控制數(shù)據(jù)包發(fā)送和等待回應(yīng)時(shí)長(zhǎng)的權(quán)力涵叮,允許更詳細(xì)的回應(yīng)分析。關(guān)于哪一種對(duì)目標(biāo)主機(jī)的掃描方式更不具備入侵性存在一些爭(zhēng)議伞插,但SYN掃描的優(yōu)勢(shì)是從不會(huì)建立完整的連接割粮。然而,RST包可能導(dǎo)致網(wǎng)絡(luò)堵塞媚污,尤其是一些簡(jiǎn)單如打印機(jī)之類(lèi)的網(wǎng)絡(luò)設(shè)備舀瓢。

實(shí)例中采用的是第一種掃描方式,直接利用操作系統(tǒng)的socket連接接口耗美,初步測(cè)試目標(biāo)服務(wù)器的端口是否可以連接京髓,如果可以則返回端口打開(kāi)狀態(tài)。

實(shí)現(xiàn)單線程掃描功能

主要實(shí)現(xiàn)這個(gè)簡(jiǎn)單的掃描器為單線程掃描商架,具體步驟如下:

獲取端口及目標(biāo)服務(wù)器

新建代碼如下:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from socket import *

# port_scan.py <host> <start_port>-<end_port>
host = sys.argv[1]
protstrs = sys.argv[2].splist('-')

start_port = int(portstrs[0])
end_port = int(portstrs[1])

target_ip = gethostbyname(host)
opened_ports = []

for port in range(start_port, end_port):
    sock = socket(AF_INET, SOCK_STREAM)
    sock.settimeout(10)
    result = sock.connect_ex((target_ip, port))
    if result == 0:
        opened_ports.append(port)

print("Opened ports:")

for i in opened_ports:
    print(i)

代碼解析:

獲取目標(biāo)ip地址:

target_ip = gethostbyname(host)

進(jìn)入循環(huán)連接:

opened_ports = []

for port in range(start_port, end_port):
    sock = socket(AF_INET, SOCK_STREAM)
    sock.settimeout(10)
    result = sock.connect_ex((target_ip, port))
    if result == 0:
        opened_ports.append(port)
        

打印opened_ports 列表

print i in opened_ports:
        print(i)

測(cè)試掃描10-200端口情況

>> python3 scanning_demo.py 127.0.0.1 10-200
Opened ports:
53
80

我們可以看到 53 與 80端口正處于開(kāi)啟的狀態(tài)朵锣,你可以使用127.0.0.1:80 查看開(kāi)啟了什么類(lèi)型的服務(wù)

多線程掃描

上面代碼實(shí)現(xiàn)了單線程掃描端口的測(cè)試,但是正常的程序在執(zhí)行中我們需要考慮執(zhí)行效率和提升性能甸私,所以需要實(shí)現(xiàn)多線程程序:

新建代碼如下:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import thread
from socket import *

def tcp_test(port):
        sock = socket(AF_INET, SOCK_STREAM)
        sock.settimeout(10)
        result = sock.connect_ex((target_ip, port))
        if result == 0:
                lock.acquire()
                print "Opened Port:", port
                lock.release()
if __name__=='__main__':
    # portscan.py <host> <start_port>-<end_port>
    post = sys.argv[1]
    portstrs = sys.argv[2].split('_')
    
    start_port = int(portstrs[0])
    end_port = int(portstrsp[1])
    
    target_ip = gethostbyname(host)
    
    lock = thread.allocate_lock()
    
    for port in range(start_port, end_port):
            thread.start_new_thread(tcp_test, (port,))

代碼解析

引入代碼包 thread ,這個(gè)是實(shí)現(xiàn)多線程必須要的:

import thread

實(shí)現(xiàn)TCP測(cè)試函數(shù)

需要注意print輸出時(shí)候需要加鎖飞傀,如果不加鎖可能會(huì)出現(xiàn)多個(gè)輸出混合在一起的錯(cuò)誤狀態(tài)皇型,而鎖需要在程序啟動(dòng)時(shí)創(chuàng)建,從而能讓新建的線程共享這個(gè)鎖

def tcp_test(port):
        sock = socket(AF_INET, SOCK_STREAM)
        sock.settimeout(10)
        result = sock.connect_ex((target_ip, port))
        if result == 0:
                lock.acquire()
                print "Opened Port:", port
                lock.release()

當(dāng)代碼執(zhí)行完之后要把鎖釋放掉(釋放lock)

輸入的處理及l(fā)ock的創(chuàng)建可以放在main函數(shù)中:

if __name__=='__main__':
    # portscan.py <host> <start_port>-<end_port>
    host = sys.argv[1]
    portstrs = sys.argv[2].split('-')

    start_port = int(portstrs[0])
    end_port = int(portstrs[1])

    target_ip = gethostbyname(host)

    lock = thread.allocate_lock()

然后修改for循環(huán):

for port in range(start_port, end_port):
        thread.start_new_thread(tcp_test, (port,))

thread.start_new_thread 用來(lái)創(chuàng)建一個(gè)線程砸烦,該函數(shù)的第一個(gè)參數(shù)是一個(gè)線程中執(zhí)行的函數(shù)弃鸦,第二個(gè)參數(shù)必須是個(gè)元組,作為函數(shù)的輸入幢痘,由于 tcp_test 函數(shù)只有一個(gè)參數(shù)唬格,所以我們使用(port,)這種形式表示這個(gè)參數(shù)為元組。

最后去掉上一節(jié)中的輸出代碼后我們的多線程改造就已經(jīng)完成了颜说。

測(cè)試結(jié)果如下:

>> python3 all_scanning_demo.py 127.0.0.1 80-200
Opened ports:80

python-nmap 包

學(xué)習(xí)Python端口掃描我們必須要接觸的一個(gè)非常強(qiáng)大的Python端口掃描包 pyton-nmap 這是一款很有名的安全工具购岗,開(kāi)源的。它可以在python程序中使用nmap端口掃描的Python包门粪,可以允許開(kāi)發(fā)者對(duì)nmap掃描結(jié)果進(jìn)行解析并實(shí)現(xiàn)自動(dòng)化掃描的任務(wù)喊积,并輸出報(bào)告。還有牛B的是可以支持異步操作玄妈,當(dāng)執(zhí)行掃描完成之后調(diào)用用戶(hù)自定義的回調(diào)函數(shù)乾吻。

install

執(zhí)行安裝命令

pip install pyton-nmap

Collecting python-nmap
  Downloading python-nmap-0.6.1.tar.gz (41kB)
    100% |████████████████████████████████| 51kB 65kB/s
Building wheels for collected packages: python-nmap
  Running setup.py bdist_wheel for python-nmap ... done
  Stored in directory: /Users/devon/Library/Caches/pip/wheels/d2/20/17/8eb9401fb0fa5ffbd0394c44d9d1c743036896c86029b0a613
Successfully built python-nmap
Installing collected packages: python-nmap
Successfully installed python-nmap-0.6.1

進(jìn)入到python shell 操作:

加載nmap包

import nmap

創(chuàng)建PortScanner對(duì)象

nm = nmap.PortScanner()

掃描 127.0.0.1的 80-200端口:

nm.scan('127.0.0.1','22-100')

查看使用的命令行和掃描信息:

nm.command_line()
Nm.scaninfo()

查看掃描的目標(biāo)主機(jī)信息:

nm.all_hosts()
nm['127.0.0.1'].hostname()
nm['127.0.0.1'].state()
nm['127.0.0.1'].all_protocols()
nm['127.0.0.1']['tcp'].keys()

擴(kuò)展

通過(guò)nmap我們可以實(shí)現(xiàn)比較復(fù)雜的一些掃描程序髓梅,你可以給予我們上面寫(xiě)的程序嘗試引入python-nmap包并將其拓展改版,實(shí)現(xiàn)一些有用的功能:

  • 1.增加GUI绎签,手動(dòng)添加掃描的端口范圍和主機(jī)
  • 2.生成csv格式的掃描報(bào)告
  • 3.后臺(tái)進(jìn)行掃描枯饿,完成后吧掃描報(bào)告已郵件的形式發(fā)送給管理員

常動(dòng)手,常思考 祝進(jìn)步诡必!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奢方,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子擒权,更是在濱河造成了極大的恐慌袱巨,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碳抄,死亡現(xiàn)場(chǎng)離奇詭異愉老,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)剖效,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)嫉入,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人璧尸,你說(shuō)我怎么就攤上這事咒林。” “怎么了爷光?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵垫竞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛀序,道長(zhǎng)欢瞪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任徐裸,我火速辦了婚禮遣鼓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘重贺。我一直安慰自己骑祟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布气笙。 她就那樣靜靜地躺著次企,像睡著了一般。 火紅的嫁衣襯著肌膚如雪健民。 梳的紋絲不亂的頭發(fā)上抒巢,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音秉犹,去河邊找鬼蛉谜。 笑死稚晚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的型诚。 我是一名探鬼主播客燕,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼狰贯!你這毒婦竟也來(lái)了也搓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涵紊,失蹤者是張志新(化名)和其女友劉穎傍妒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體摸柄,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颤练,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驱负。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗦玖。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跃脊,靈堂內(nèi)的尸體忽然破棺而出宇挫,到底是詐尸還是另有隱情,我是刑警寧澤酪术,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布器瘪,位于F島的核電站,受9級(jí)特大地震影響绘雁,放射性物質(zhì)發(fā)生泄漏娱局。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一咧七、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧任斋,春花似錦继阻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至澈蟆,卻和暖如春墨辛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趴俘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工睹簇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奏赘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓太惠,卻偏偏與公主長(zhǎng)得像磨淌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凿渊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容