端口扫描工具-python
# 端口扫描工具
TCP模式-socket
原理
目标主机的一个端口如果是监听状态(LISTENING或者LINSTEN),那么当我connect目标主机时就能成功,否则说明端口是关闭的。
优点: 编程简单,是需要一个API connect(),比较可靠,因为TCP是可靠协议,当丢包的时候,会重传SYN帧。
缺点: 正因为TCP的可靠性,所以当端口不存在的时候,源主机会不断尝试发SYN帧企图得到ack的应答,多次尝试后才会放弃,因此造成了扫描的时间较长。并且,connect的扫描方式可能较容易被目标主机发现。
主要是一个编程思路,代码很简单
1、定义portscan函数,创建socke对象,进行TCP端口扫描
2、启动多线程运行PortScan函数
3、记录并输出扫描结果与时间
主要还是利用了三次握手来判断目标端口是否开启:
创建端口扫描函数
def portscan(target,port):
# 定义portscan函数,进行TCP端口扫描
try:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建socket对象
client.connect((target,port)) #建立socket连接,判断端口是否开放
print("[*] %s:%d端口开放" % (target,port))
client.close()
except:
pass #捕获异常,避免socket连接建立失败造成程序退出
直接测试调用函数,简单完善一次,测试100个端口:
import socket
import time
def portscan(target,port):
# 定义portscan函数,进行TCP端口扫描
try:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建socket对象
client.connect((target,port)) #建立socket连接,判断端口是否开放
print("[*] %s:%d端口开放" % (target,port))
client.close()
except:
pass #捕获异常,避免socket连接建立失败造成程序退出
start_time = time.time()
for i in range(0,1001,1):
print('正在进行第{}个端口'.format(i))
portscan('127.0.0.1',i)
print('end')
end_time = time.time()
print("[*] All done in %.2f s" % (end_time - start_time))
可以看到非常的慢,看下用时,50个端口用了近100秒
加入多线程
for port in range(1,9999):
# 启动多线程运行PortScan函数
t = Thread(target=portscan,args=(target,port)) #创建线程对象
t.start() #开始线程
完整代码
扫描1-65535
import socket #创建TCP连接
from threading import Thread #多线程模块,进行多线程扫描
import time #时间模块,记录扫描所需时间
def main():
target = input("IP:")
start_time = time.time()
s_time = time.ctime()
print("[*] Start port scan at %s" % s_time)
for port in range(1,65536): #定义扫描的端口范围
# 2、启动多线程运行PortScan函数
t = Thread(target=portscan, args=(target, port)) # 创建线程对象
t.start() # 开始线程
end_time = time.time()
print("[*] All done in %.2f s" % (end_time - start_time))
def portscan(target,port):
# 定义portscan函数,进行TCP端口扫描
try:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建socket对象
client.connect((target,port)) #建立socket连接,判断端口是否开放
print("[*] %s:%d端口开放" % (target,port))
client.close()
except:
pass #捕获异常,避免socket连接建立失败造成程序退出
if __name__ == '__main__':
main()
可以看到多线程快了很多,只用了9秒钟
完善功能
import socket #创建TCP连接
from threading import Thread #多线程模块,进行多线程扫描
import time #时间模块,记录扫描所需时间
def main():
target = input("IP:")
start_time = time.time()
s_time = time.ctime()
port_tmp = int(input("选择全端口(数字1)还是指定端口(数字2):"))
if port_tmp==1:
print("[*] Start port scan at %s" % s_time)
for port in range(1,65536): #定义扫描的端口范围
# 2、启动多线程运行PortScan函数
t = Thread(target=portscan, args=(target, port)) # 创建线程对象
t.start() # 开始线程
elif port_tmp==2:
port_tmp2 = input("请输入端口列表,以逗号分割,列如80,8080,7001:")
prot_list=port_tmp2.split(',')
print(prot_list)
for port_i in prot_list:
t = Thread(target=portscan, args=(target, int(port_i))) # 创建线程对象
t.start() # 开始线程
end_time = time.time()
print("[*] All done in %.2f s" % (end_time - start_time))
def portscan(target,port):
# 定义portscan函数,进行TCP端口扫描
try:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建socket对象
client.connect((target,port)) #建立socket连接,判断端口是否开放
print("[*] %s:%d端口开放" % (target,port))
client.close()
except:
pass #捕获异常,避免socket连接建立失败造成程序退出
if __name__ == '__main__':
main()
SYN扫描
原理
TCP SYN 扫描也就是半开扫描(半开式扫描),这种扫描方式与全连接扫描类似,但客户端不会和服务端建立完整的连接。
扫描过程为:客户端会发送一个带有 SYN 标识和端口号的 TCP 数据包给服务器,如果服务器这个端口是开放的,则会接受这个连接并返回一个带有 SYN 和 ACK 标识的数据包给客户端,随后客户端会返回带有 RST 标识的数据包,而不是像全连接扫描一样返回一个带有 ACK和 RST 标识的数据包,这样就不会与服务端建立完整的连接了。如果目标端口处于关闭状态,则服务端会返回一个 RST 标识的数据包。
代码(多线程)
import threading,time
import sys
from scapy.all import *
from scapy.layers.inet import IP, TCP, ICMP
def get_ip():
'''从命令行参数中获取 IP'''
try:
parameter = sys.argv
ip = parameter[1]
print('The IP you test is : ', end = '')
font.print_YELLOW(ip)
except Exception as e:
print(e)
return ip
def port_scan(port):
'''扫描端口'''
try:
packet = IP(dst=ip)/TCP(dport=port,flags='S') # 构造一个 flags 的值为 S 的报文
send = sr1(packet,timeout=2,verbose=0)
if send.haslayer('TCP'):
if send['TCP'].flags == 'SA': # 判断目标主机是否返回 SYN+ACK
send_1 = sr1(IP(dst=ip)/TCP(dport=port,flags='R'),timeout=2,verbose=0) # 只向目标主机发送 RST
font.print_GREEN('[+] %d is open' % port)
elif send['TCP'].flags == 'RA':
pass
except:
pass
def main():
packet_ping = IP(dst=ip)/ICMP() # 在扫描端口之前先用 ICMP 协议探测一下主机是否存活
ping = sr1(packet_ping,timeout=2,verbose=0)
if ping is not None:
for p in range(1,65535): # 默认扫描1-1000的端口,可以手动修改这里的端口范围
t = threading.Thread(target=port_scan,args=(p,))
t.start()
elif ping is None:
font.print_RED('该主机处于关闭状态或本机被该主机过滤,无法对其使用 ping 探测')
if __name__ == '__main__':
ip = get_ip()
start_time = time.time()
main()
end_time = time.time()
print('[time cost] : ' + str(end_time-start_time) + ' 秒')