python如何編寫類似nmap的掃描工具
本文主要是利用scapy包編寫了一個簡易掃描工具,支持ARP、ICMP、TCP、UDP發(fā)現(xiàn)掃描,支持TCP SYN、UDP端口掃描,如下:
usage: python scan.py <-p ping掃描類型> <-s 端口發(fā)現(xiàn)類型> [-t target] [--port ports]
簡單掃描工具,可以進行存活掃描及端口掃描.
存活掃描包括:ARP掃描、ICMP掃描、TCP掃描、UDP掃描.
端口掃描包括:TCP SYN掃描、TCP ACK掃描、TCP FIN掃描.
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
target group:
用于設置IP、PORT參數(shù)
--target TARGET target為IP或IP段,如192.168.1.1,192.168.1.x,或1
92.168.1.1-254
--port PORT port為待掃描的端口,如21,80,...或21-80
ping group:
用于開啟存活掃描相關選項
-p 開啟存活掃描
--ARP 啟動ARP掃描
--ICMP 啟動ICMP掃描
--TCP 啟動TCP掃描
--UDP 啟動UDP掃描
port scan group:
用于開啟端口掃描相關選項
-s 開啟端口掃描
--SYN 開啟SYN掃描
--ACK 開啟ACK掃描
--FIN 開啟FIN掃描
--UPORT 開啟UDP端口掃描
utils group:
用于開啟掃描過程中的一些實用選項
--timeout TIMEOUT 設置發(fā)包超時時間,默認0.5秒
--retry RETRY 設置發(fā)包重試次數(shù),默認不重試
以上做為說明,祝好運!
一、發(fā)現(xiàn)掃描
1.首先進行ARP掃描
python scan.py -p --target 192.168.1.1-254 --ARP [+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx [+]IP: 192.168.1.111 => MAC: c6:36:55:xx:xx:xx [+]總共耗時9.84091806412秒.
通過retry參數(shù)增加發(fā)包嘗試次數(shù),如下:
python scan.py -p --target 192.168.1.1-254 --ARP --retry 2
[+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx
[+]IP: 192.168.1.111 => MAC: c6:36:55:xx:xx:xx
[+]IP: 192.168.1.102 => MAC: 58:1f:28:xx:xx:xx
[+]IP: 192.168.1.114 => MAC: 6c:8d:c1:xx:xx:xx
[+]IP: 192.168.1.103 => MAC: 84:38:38:xx:xx:xx
[+]總共耗時20.429942131秒.
2.使用ICMP掃描,若沒有指定任何掃描類型參數(shù),默認會啟用ICMP掃描,如下:
python scan.py -p --target 192.168.1.1-254 [+]沒有指定任何ping掃描方式,默認選擇ICMP掃描 [+]IP:主機192.168.1.1 echo-reply. [+]IP:主機192.168.1.111 echo-reply. [+]總共耗時10.7177450657秒.
通過timeout參數(shù),設置較長的超時,可以防止網(wǎng)絡狀況不好造成的丟包,如下:
python scan.py -p --target 192.168.1.1-254 --timeout 2 [+]沒有指定任何ping掃描方式,默認選擇ICMP掃描 [+]IP:主機192.168.1.1 echo-reply. [+]IP:主機192.168.1.111 echo-reply. [+]IP:主機192.168.1.114 echo-reply. [+]總共耗時10.7566649914秒.
3.使用TCP掃描
python scan.py -p --target 192.168.1.100-120 --TCP --timeout 1 [+]請稍等,時間較長! [!]掃描... 192.168.1.100 [!]掃描... 192.168.1.101 [!]掃描... 192.168.1.102 [!]掃描... 192.168.1.103 [!]掃描... 192.168.1.104 [!]掃描... 192.168.1.105 [!]掃描... 192.168.1.106 [!]掃描... 192.168.1.107 [!]掃描... 192.168.1.108 [!]掃描... 192.168.1.109 [!]掃描... 192.168.1.110 [!]掃描... 192.168.1.111 [!]掃描... 192.168.1.112 [!]掃描... 192.168.1.113 [!]掃描... 192.168.1.114 [!]掃描... 192.168.1.115 [!]掃描... 192.168.1.116 [!]掃描... 192.168.1.117 [!]掃描... 192.168.1.118 [!]掃描... 192.168.1.119 [!]掃描... 192.168.1.120 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.102 在線. [+]主機 192.168.1.103 在線. [+]主機 192.168.1.111 在線. [+]主機 192.168.1.114 在線. [+]總共耗時16.4359779358秒.
4.使用UDP掃描
python scan.py -p --target 192.168.1.100-120 --UDP --retry 3 [+]請稍等,時間較長! [!]掃描... 192.168.1.100 [!]掃描... 192.168.1.101 [!]掃描... 192.168.1.102 [!]掃描... 192.168.1.103 [!]掃描... 192.168.1.104 [!]掃描... 192.168.1.105 [!]掃描... 192.168.1.106 [!]掃描... 192.168.1.107 [!]掃描... 192.168.1.108 [!]掃描... 192.168.1.109 [!]掃描... 192.168.1.110 [!]掃描... 192.168.1.111 [!]掃描... 192.168.1.112 [!]掃描... 192.168.1.113 [!]掃描... 192.168.1.114 [!]掃描... 192.168.1.115 [!]掃描... 192.168.1.116 [!]掃描... 192.168.1.117 [!]掃描... 192.168.1.118 [!]掃描... 192.168.1.119 [!]掃描... 192.168.1.120 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.102 在線. [+]主機 192.168.1.103 在線. [+]主機 192.168.1.111 在線. [+]主機 192.168.1.114 在線. [+]總共耗時33.5198891163秒.
二、端口掃描
1、TCP SYN端口掃描,不設置端口參數(shù),則默認掃描1-1024端口
python scan.py --target 192.168.1.110-115 -s --SYN [+]沒有指定任何掃描端口,默認掃描1-1024 [!]掃描... 192.168.1.110 [!]掃描... 192.168.1.111 [!]掃描... 192.168.1.112 [!]掃描... 192.168.1.113 [!]掃描... 192.168.1.114 [!]掃描... 192.168.1.115 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.111 開放的TCP端口有:[80] [+]總共耗時165.125555992秒.
掃描指定端口:
python scan.py --target 192.168.1.1-254 -s --SYN --port 80 --timeout 1 [!]掃描... 192.168.1.1 [!]掃描... 192.168.1.2 [!]掃描... 192.168.1.3 [!]掃描... 192.168.1.4 ... [!]掃描... 192.168.1.253 [!]掃描... 192.168.1.254 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.111 開放的TCP端口有:[80] [+]主機 192.168.1.1 開放的TCP端口有:[80] [+]總共耗時9.72222185135秒.
2、掃描UDP端口
python scan.py --target 192.168.1.1 -s --UPORT --timeout 1 [+]沒有指定任何掃描端口,默認掃描1-1024 [!]掃描... 192.168.1.1 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.1 開放的UDP端口有:[520] [+]總共耗時27.4742250443秒.
也可同時進行發(fā)現(xiàn)掃描與端口掃描,如下:
python scan.py --target 192.168.1.1-254 -p --ARP -s --SYN --port 80 --timeout 1 --retry 2 [+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx [+]IP: 192.168.1.102 => MAC: 58:1f:28:xx:xx:xx [+]IP: 192.168.1.114 => MAC: 6c:8d:c1:xx:xx:xx [+]IP: 192.168.1.103 => MAC: 84:38:38:xx:xx:xx [+]IP: 192.168.1.101 => MAC: 5c:f7:e6:xx:xx:xx [!]掃描... 192.168.1.1 [!]掃描... 192.168.1.2 ... [!]掃描... 192.168.1.253 [!]掃描... 192.168.1.254 [+]正在處理掃描信息. ==================== [+]主機 192.168.1.1 開放的TCP端口有:[80] [+]主機 192.168.1.111 開放的TCP端口有:[80] [+]總共耗時45.2775988579秒.
OK,最后附上源碼:
import argparse
import re
import time
import threading
from scapy.all import *
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
class Discovery_Scan(object):
'''
說明:用于發(fā)現(xiàn)掃描
'''
def __init__(self,args,timeout=0.5,retry=0):
self.targets = parse_target(args)
self.timeout = timeout
self.retry = retry
def arp_scan(self,pdst):
#ARP發(fā)現(xiàn)掃描
ans = sr1(ARP(pdst=pdst),timeout=self.timeout,retry=self.retry,verbose=False)
if ans:
if ans[ARP].op == 2: #操作碼為2是is-at,是ARP響應
print '[+]IP: %s => MAC: %s' % (pdst,ans[ARP].hwsrc)
def icmp_scan(self,dst):
#ICMP發(fā)現(xiàn)掃描
ans = sr1(IP(dst=dst)/ICMP(),timeout=self.timeout,retry=self.retry,verbose=False)
if ans:
if ans[ICMP].type == 0: #ICMP type為0表示是ICMP echo-reply
print '[+]IP:主機%s echo-reply.' % dst
tcp_info = {}
def tcp_scan(self,dst,port):
#TCP SYN,發(fā)送TCP SYN包,有響應表示端口開放
ans,unans = sr(IP(dst=dst)/TCP(sport=RandShort(),dport=port,flags='S'),
timeout=self.timeout,retry=self.retry,verbose=False)
if ans.res:
if ans.res[0][0][IP].dst not in Discovery_Scan.tcp_info:
Discovery_Scan.tcp_info[ans.res[0][0][IP].dst] = True
udp_info = {}
def udp_scan(self,dst,port):
#UDP,發(fā)送UDP包,有響應表示端口開放
ans,uans = sr(IP(dst=dst)/UDP(sport=RandShort(),dport=port),
timeout=self.timeout,retry=self.retry,verbose=False)
if ans.res:
if ans.res[0][0][IP].dst not in Discovery_Scan.udp_info:
Discovery_Scan.udp_info[ans.res[0][0][IP].dst] = True
class Port_Scan(object):
'''
說明:用于進行端口掃描,判斷端口是否開放
'''
def __init__(self,args,timeout=0.5,retry=0):
self.targets = parse_target(args)
self.timeout = timeout
self.retry = retry
syn_port_dict = {}
def syn_port_scan(self,dst,port):
#TCP SYN端口掃描,若SYN包返回攜帶SYN、ACK(即TCP.flags=18)標志的包,則表明此端口打開。
ans,uans = sr(IP(dst=dst)/TCP(sport=RandShort(),dport=port,flags='S'),
timeout=self.timeout,retry=self.retry,verbose=False)
if ans:
first_respons_pkt = ans.res[0][1]
if first_respons_pkt[TCP] and first_respons_pkt[TCP].flags == 18:
if first_respons_pkt[IP].src not in Port_Scan.syn_port_dict:
Port_Scan.syn_port_dict[first_respons_pkt[IP].src] = [first_respons_pkt[TCP].sport]
else:
Port_Scan.syn_port_dict[first_respons_pkt[IP].src].append(first_respons_pkt[TCP].sport)
udp_port_dict = {}
def udp_port_scan(self,dst,port):
#UDP端口掃描,若UDP端口返回ICMP port-unreachable,則表示端口打開。(排除某些主機對任何UDP端口的探測都響應為ICMP port-unrechable)
ans,uans = sr(IP(dst=dst)/UDP(sport=RandShort(),dport=port),
timeout=self.timeout, retry=self.retry, verbose=False)
if ans.res and ans.res[0][1].haslayer(UDPerror):
first_respons_pkt = ans.res[0][1]
if first_respons_pkt[IP].src not in Port_Scan.udp_port_dict:
Port_Scan.udp_port_dict[first_respons_pkt[IP].src] = [first_respons_pkt[UDPerror].dport]
else:
Port_Scan.udp_port_dict[first_respons_pkt[IP].src].append(first_respons_pkt[UDPerror].dport)
def parse_opt():
'''
@說明:通過argparse模塊解析程序傳入的參數(shù)
@return:args
'''
usage = 'python %(prog)s <-p ping掃描類型> <-s 端口發(fā)現(xiàn)類型> [-t target] [--port ports]'
description = '簡單掃描工具,可以進行存活掃描及端口掃描.\n' \
'存活掃描包括:ARP掃描、ICMP掃描、TCP掃描、UDP掃描.\n' \
'端口掃描包括:TCP SYN掃描、TCP ACK掃描、TCP FIN掃描.'
epilog = '以上做為說明,祝好運!'
parser = argparse.ArgumentParser(usage=usage,description=description,epilog=epilog,version='v1.0')
target_group = parser.add_argument_group('target group',description='用于設置IP、PORT參數(shù)')
target_group.add_argument('--target',dest='target',action='store',
help='target為IP或IP段,如192.168.1.1,192.168.1.x,或192.168.1.1-254')
target_group.add_argument('--port',dest='port',action='store',
help='port為待掃描的端口,如21,80,...或21-80')
ping_group = parser.add_argument_group('ping group',description='用于開啟存活掃描相關選項')
ping_group.add_argument('-p',dest='ping',action='store_true',help='開啟存活掃描')
ping_group.add_argument('--ARP',dest='ARP',action='store_true',help='啟動ARP掃描')
ping_group.add_argument('--ICMP',dest='ICMP',action='store_true',help='啟動ICMP掃描')
ping_group.add_argument('--TCP',dest='TCP',action='store_true',help='啟動TCP掃描')
ping_group.add_argument('--UDP',dest='UDP',action='store_true',help='啟動UDP掃描')
port_scan_group = parser.add_argument_group('port scan group',description='用于開啟端口掃描相關選項')
port_scan_group.add_argument('-s',dest='scan',action='store_true',help='開啟端口掃描')
port_scan_group.add_argument('--SYN',dest='SYN',action='store_true',help='開啟SYN掃描')
port_scan_group.add_argument('--ACK',dest='ACK',action='store_true',help='開啟ACK掃描')
port_scan_group.add_argument('--FIN',dest='FIN',action='store_true',help='開啟FIN掃描')
port_scan_group.add_argument('--UPORT', dest='UPORT', action='store_true', help='開啟UDP端口掃描')
utils_group = parser.add_argument_group('utils group',description='用于開啟掃描過程中的一些實用選項')
utils_group.add_argument('--timeout',dest='timeout',action='store',type=float,help='設置發(fā)包超時時間,默認0.5秒')
utils_group.add_argument('--retry',dest='retry',action='store',type=int,help='設置發(fā)包重試次數(shù),默認不重試')
args = parser.parse_args()
if not args.ping and not args.scan:
print '[-]必須通過-p/-s選項開啟一種掃描'
print '\n'
parser.print_help()
exit(1)
elif not args.target:
print '[-]必須通過--target選項指定掃描的對象'
print '\n'
parser.print_help()
exit(1)
if args.ping:
if not args.ARP and not args.ICMP and not args.TCP and not args.UDP:
args.ICMP = True #若沒有指定任何ping掃描方式,則默認選擇ICMP掃描
print '[+]沒有指定任何ping掃描方式,默認選擇ICMP掃描'
if args.scan:
if not args.SYN and not args.ACK and not args.FIN and not args.UPORT:
args.SYN = True #若沒有指定任何端口掃描方式,則默認選擇SYN掃描
print '[+]沒有指定任何端口掃描方式,默認選擇SYN掃描'
if not args.port:
args.port = '1-1024' #若沒有指定任何掃描端口,則默認掃描1-1024
print '[+]沒有指定任何掃描端口,默認掃描1-1024'
return args
def parse_target(args):
'''
@說明:用于解析如'192.168.1.1,192.168.1.x,...或192.168.1.1-254'格式的IP為單獨的IP,用于解析如'21,80,...或21-80'格式的端口為單獨的端口
@param: args,一個namespace對象
@return: (ip_list,port_list)
'''
pattern1 = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
pattern2 = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}$'
pattern3 = r'\d{1,5}$'
pattern4 = r'\d{1,5}-\d{1,5}$'
ip_list,port_list = None,None
if args.target:
if re.search(pattern1,args.target):
ip_list = args.target.split(',')
elif re.match(pattern2,args.target):
_split = args.target.split('-')
first_ip = _split[0]
ip_split = first_ip.split('.')
ipdot4 = range(int(ip_split[3]), int(_split[1]) + 1)
ip_list = [ip_split[0] + '.' + ip_split[1] + '.' + ip_split[2] + '.' + str(p) for p in ipdot4]
else:
print '[-]target格式輸入有誤,請查看幫助!'
exit(1)
if args.port:
if re.match(pattern4,args.port):
_split = args.port.split('-')
port_list = range(int(_split[0]),int(_split[1])+1)
elif re.search(pattern3,args.port):
port_list = args.port.split(',')
else:
print '[-]port格式輸入有誤,請查看幫助!'
exit(1)
return ip_list,port_list
def main():
'''
@說明:掃描的主程序,首先根據(jù)條件創(chuàng)建Ping掃描或端口掃描對象,然后調(diào)用相關的掃描方法進行掃描。
'''
args = parse_opt()
if args.ping: #是否啟動Ping掃描
if not args.timeout and not args.retry:
obj_ping = Discovery_Scan(args)
elif args.timeout and not args.retry:
obj_ping = Discovery_Scan(args,timeout=args.timeout)
elif not args.timeout and args.retry:
obj_ping = Discovery_Scan(args,retry=args.retry)
else:
obj_ping = Discovery_Scan(args,args.timeout,args.retry)
ip_list = obj_ping.targets[0]
if ip_list:
#ARP掃描
if args.ARP:
for pdst in ip_list:
t = threading.Thread(target=obj_ping.arp_scan,args=(pdst,))
t.start()
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
#ICMP掃描
elif args.ICMP:
for dst in ip_list:
t = threading.Thread(target=obj_ping.icmp_scan,args=(dst,))
t.start()
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
#TCP掃描
elif args.TCP:
port_list = [80,443,21,22,23,25,53,135,139,137,445,1158,1433,1521,3306,3389,7001,8000,8080,9090]
print '[+]請稍等,時間較長!'
for dst in ip_list:
print '[!]掃描...',dst
for port in port_list:
t = threading.Thread(target=obj_ping.tcp_scan,args=(dst,port))
t.start()
print '[+]正在處理掃描信息.'
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
if not obj_ping.tcp_info:
print '\n'
print '=' * 20
print '[+]未發(fā)現(xiàn)在線主機.'
else:
print '\n'
print '=' * 20
for ip_a in sorted(obj_ping.tcp_info.keys()):
print '[+]主機 %s 在線.' % ip_a
#UDP掃描
elif args.UDP:
port_list = [7,9.13,15,37,53,67,68,69,135,137,138,139,445,520]
print '[+]請稍等,時間較長!'
for dst in ip_list:
print '[!]掃描...',dst
for port in port_list:
t = threading.Thread(target=obj_ping.udp_scan,args=(dst,port))
t.start()
print '[+]正在處理掃描信息.'
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
if not obj_ping.udp_info:
print '\n'
print '=' * 20
print '[+]未發(fā)現(xiàn)在線主機.'
else:
print '\n'
print '=' * 20
for ip_a in sorted(obj_ping.udp_info.keys()):
print '[+]主機 %s 在線.' % ip_a
if args.scan: #是否啟動端口掃描
if not args.timeout and not args.retry:
obj_port = Port_Scan(args)
elif args.timeout and not args.retry:
obj_port = Port_Scan(args,timeout=args.timeout)
elif not args.timeout and args.retry:
obj_port = Port_Scan(args,retry=args.retry)
else:
obj_port = Port_Scan(args,args.timeout,args.retry)
ip_list,port_list = obj_port.targets
if ip_list and port_list:
if args.SYN:
for dst in ip_list:
print '[!]掃描...',dst
for port in port_list:
t = threading.Thread(target=obj_port.syn_port_scan,args=(dst,int(port)))
t.start()
print '[+]正在處理掃描信息.'
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
if not obj_port.syn_port_dict:
print '\n'
print '=' * 20
print '[+]未發(fā)現(xiàn)開放TCP端口.'
else:
print '\n'
print '=' * 20
for k,v in obj_port.syn_port_dict.items():
print '[+]主機 %s 開放的TCP端口有:%s' % (k,str(v))
elif args.ACK:
pass #基本不能使用
elif args.FIN:
pass #基本不能使用
elif args.UPORT:
for dst in ip_list:
print '[!]掃描...',dst
for port in port_list:
t = threading.Thread(target=obj_port.udp_port_scan,args=(dst,int(port)))
t.start()
print '[+]正在處理掃描信息.'
while threading.activeCount() != 1: #避免線程還沒有運行完就提前輸出不全的結(jié)果
time.sleep(1)
if not obj_port.udp_port_dict:
print '\n'
print '=' * 20
print '[+]未發(fā)現(xiàn)開放UDP端口.'
else:
print '\n'
print '=' * 20
for k,v in obj_port.udp_port_dict.items():
print '[+]主機 %s 開放的UDP端口有:%s' % (k,str(v))
if __name__ == '__main__':
try:
start_time = time.time()
main()
stop_time = time.time()
print '[+]總共耗時'+str(stop_time-start_time)+'秒.'
except Exception,e:
print '[-]執(zhí)行出錯,具體錯誤見下面信息.'
print e
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Python用zip函數(shù)同時遍歷多個迭代器示例詳解
這篇文章主要給大家進行介紹了Python如何用zip函數(shù)同時遍歷多個迭代器,文中給出了示例以及原理和注意事項,相信會對大家的理解和學習很有幫助,有需要的朋友們下面來一起學習學習吧。2016-11-11
Python打包文件執(zhí)行報錯:ModuleNotFoundError: No module 
這篇文章給大家介紹了Python打包文件執(zhí)行報錯:ModuleNotFoundError: No module named ‘pymssql‘的解決方法,如果有遇到相同問題的朋友可以參考閱讀一下本文2023-10-10
Pytorch實現(xiàn)tensor序列化和并行化的示例詳解
這篇文章主要介紹了Pytorch實現(xiàn)tensor序列化和并行化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,感興趣的同學們下面隨著小編來一起學習學習吧2023-12-12
python 簡單照相機調(diào)用系統(tǒng)攝像頭實現(xiàn)方法 pygame
今天小編就為大家分享一篇python 簡單照相機調(diào)用系統(tǒng)攝像頭實現(xiàn)方法 pygame,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
django將網(wǎng)絡中的圖片,保存成model中的ImageField的實例
今天小編就為大家分享一篇django將網(wǎng)絡中的圖片,保存成model中的ImageField的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
Python報錯ImportError:?IProgress?not?found.?Please?update
在使用Jupyter Notebook或JupyterLab進行交互式編程時,我們可能會遇到各種導入錯誤,本文就來介紹一下Python報錯ImportError:?IProgress?not?found.?Please?update?jupyter?and?ipywidgets解決,感興趣的可以了解一下2024-06-06

