简介
实现简易的端口扫描器:
使用Python的socket编程,去实现与指定ip端口进行TCP连接;
使用sys模块,实现命令交互;
使用多线程,加快扫描速度。
扫描原理
尝试与每一个指定的端口进行TCP“三次握手”通信,如果成功建立连接,则证明端口开放,否则认为端口关闭。三次握手过程如下:
实现代码
import socket
import threading
import queue
import sys
class DHPScan:
#无聊做了个logo
logo = '''
____ _ _ ____ ____
| _ \| | | | _ \ / __| ____ ___ _____
| | \ | |__| | |_\ | \__ / __|/ _ | _ |
| |_/ / __ | __/ \__ | |__| |_|| | | |
|____/|_| |_|_| |____/ \____|\___|_| |_|
'''
help = '''
usage : python dhpscan.py [-i ip] [-p port|beginPort-endPort] [-t threadNum]
example: python dhpscan.py -i 127.0.0.1 -p 443
python dhpscan.py -i 127.0.0.1 -p 443 -t 10
python dhpscan.py -i 127.0.0.1 -p 1-1000 -t 10
'''
class scanThread(threading.Thread):
def __init__(self, ip, portQueue):
threading.Thread.__init__(self)
self.ip = ip
self.portQueue = portQueue
# 重写run方法
def run(self):
while True:
if self.portQueue.empty():#队列空就结束
break
port = self.portQueue.get()#从队列中取出
try:
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.settimeout(0.5)#如果设置太小,检测不精确,设置太大,检测太慢
result = tcp.connect_ex((self.ip, port)) # 效率比connect高,成功时返回0,失败时返回错误码
if result == 0:
print('%d\tOPENED' % port)
except:
print('ERROR:ip or port ERROR')
finally:
tcp.close()
#获取端口队列
def getPort(self,arg):
if '-' in arg:
num = arg.split('-', 1)
for i in range(int(num[0]), int(num[1]) + 1):
portQueue.put(i)#将端口i加入到队列中
else:
portQueue.put(int(arg))
#创建线程
def createThread(self,num,scanip,scanport):
for i in range(num):
threads.append(scan.scanThread(scanip,scanport))
if __name__ == '__main__':
scan = DHPScan()
print(scan.logo,scan.help)
threadNum = 5 #默认5个线程
ip = '127.0.0.1'
threads = []#线程列表
portQueue = queue.Queue()#待检测端口队列,会在《Python常用操作》一文中更新用法
argvs = sys.argv#用于实现交互
if len(argvs) == 7 and argvs[5] == '-t' and argvs[3] == '-p' and argvs[1] == '-i':
ip = argvs[2]
threadNum = int(argvs[6])
scan.getPort(argvs[4])
elif len(argvs) == 5 and argvs[3] == '-p' and argvs[1] == '-i':
ip = argvs[2]
scan.getPort(argvs[4])
else:
print('ERROR:Please input correct command according to usage')
scan.createThread(threadNum,ip,portQueue)
print('Scaning...')
for t in threads:#启动线程
t.start()
for t in threads:#阻塞线程,等待线程结束
t.join()
print('Scan Over')
测试结果
测试一下
总结
- 一开始不知道队列的get方法,使用get方法获取数据后,该数据便不在队列中,相当于提取出来了;
- 使用队列,将待检测的端口put进队列中,可以配合多线程去调用检测,感觉以后会经常用到;
- socket中connect()和connect_ex()的区别:
connect()的连接失败是抛出异常,而connect_ex()是返回错误码而不是抛异常;
参考文章
更新
修改线程中重写的run方法,使用recv()方法,使得能够获取端口的服务信息
def run(self):
while True:
if self.portQueue.empty():
break
port = self.portQueue.get()
try:
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.settimeout(0.5)#如果设置太小,检测不精确,设置太大,检测太慢
result = tcp.connect_ex((self.ip, port)) # 效率比connect高,成功时返回0,失败时返回错误码
try:
server = tcp.recv(1024).decode().strip()
except:
server = 'unknow'
if result == 0:
print('%d\topened\t\t%s' %(port,server))
except:
print('ERROR:ip or port ERROR')
finally:
tcp.close()
测试原来的ip