python命令解析使用多线程扫描端口

使用python实现端口扫描的脚本,使用到optparse模块解析命令
大部分解释已写入脚本注释。文件名为portscan.py

'''
命令解析,多线程,端口扫描
----------author:Bluecap-------------
'''

#命令行参数解析模块
from optparse import OptionParser
import queue
import socket
import re
import threading


parser=OptionParser('Usage: %prog -i<target ip>')
#添加命令,这里可以选定类型,当然后面的正则可能没有必要
parser.add_option('-i',dest='ip',type='string',help='specify target ip')
#端口为范围,或者单个。单个单线程,多个多线程
parser.add_option('-p',dest='port',type='string',help='specify target port')
parser.add_option('--thread',dest='thread',type='int',help='specify thread num')
#接上--brief只显示开放端口,store_true为加上--brief时brief为True,否则为False
parser.add_option('--brief',dest='brief',action='store_true',help='brief mode')
#全局参数控制输出
# flag=False
#解析命令
opts,argv=parser.parse_args()
# print('opts:',opts)
# print('argv:',argv)
# print(type(opts))
# print(opts.ip)
# print(opts.port)
# print(type(opts.thread))
'''
opts: {'ip': None, 'port': None, 'thread': None}
E:\python作业2>python portscan.py -i 120.0.0.1 -p 21 --thread 4
opts: {'ip': '120.0.0.1', 'port': '21', 'thread': 4}
argv: []
'''
def print_help():
    print('Usage: %prog -i<target ip> -p<target port> --thread<thread num> --brief<show detail>')#至少要输入ip和端口


#单线程扫描 单个端口或者端口范围
def singleThread(ip,ports):
    try:
        port=int(ports)
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        if s.connect_ex((ip,port))==0:
            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'开放'))
        else:
            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'关闭'))
    except:
        pre_port=int(opts.port.split('-')[0])
        suf_port=int(opts.port.split('-')[1])
        if pre_port==suf_port:
            port=pre_port
            s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            #连接成功返回0,失败返回编码
            if s.connect_ex((ip,port))==0:
                print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'开放'))
            else:
                print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'关闭'))
        elif pre_port<suf_port:
            try:
                for port in range(pre_port,suf_port+1):
                    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                    #连接成功返回0,失败返回编码
                    if s.connect_ex((ip,port))==0:
                        print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'开放'))
                    else:
                        #flag=opts.brief
                        if opts.brief:
                            pass#加上breif后不输出扫描信息
                        else:
                            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'关闭'))
            except Exception as e:
                print(e)
        else:
            print('端口范围错误')
            exit(1)

#面向对象的方式进行调用
# ip=opts.ip
# ports=opts.port.split('-')

def scanport(ip ,port):
    #给线程上锁,防止取出重复端口
    with threading.Lock():#线程锁,自动加自动解
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#TCP连接
        if s.connect_ex((ip,port))==0:
            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'开放'))
        else:
            if opts.brief:
                pass
            else:
                print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'关闭'))

#多线程端口扫描
def MultiThread(ip,port,threads):
    pre_port=int(opts.port.split('-')[0])
    suf_port=int(opts.port.split('-')[1])
    #print('pre_port:',pre_port)
    if pre_port==suf_port:
        singleThread(ip,pre_port)
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        banner()
        #连接成功返回0,失败返回编码
        if s.connect_ex((ip,port))==0:
            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'开放'))
        else:
            print('|{:^16}|{:^16}|{:^16}|'.format(ip,port,'关闭'))
    elif pre_port<suf_port:
        port_num=suf_port-pre_port
        #print('port_num:',port_num)
         #把端口放入队列中,每个线程结束后取出一个
        queue_ports=queue.Queue(port_num+1)
        for port in range(pre_port,suf_port+1):
            queue_ports.put(port)
            #print('queue_ports size:',queue_ports.qsize())
        #print('queue_ports size:',queue_ports.qsize())
        #扫描的端口数小于用户输入的线程数,让线程数等于端口数
        if threads>0 and threads<=20:
            if threads>=port_num:
                #print('多线程第一种情况')
                threads=port_num
                for t in range(threads):
                    try:
                        thread=threading.Thread(target=scanport,args=(ip,pre_port+t),daemon=True)#传递参数,设置为守护线程
                        thread.start()
                        #print(f'线程{t+1}开始启动...')
                    except threading.ThreadError as e:
                        print(f'线程{t+1}执行异常')
            else:
                #print('多线程第二种情况')
                #初始时创建四个线程
                for th in range(threads):
                    try:
                        thread=threading.Thread(target=scanport,args=(ip,queue_ports.get()))#如果设置成守护线程可能有几个端口就有几个线程
                        thread.start()
                        #print(f'线程{th+1}开始启动...')
                    except threading.ThreadError as e:
                        print(f'线程{th+1}执行异常')
                else:#执行完for后执行else
                    while True:
                        # if queue_ports.empty():
                        #     break
                        thread_number=threading.active_count()#等同于threading.enumerate()
                        surplus_ports=queue_ports.qsize()#当前未扫描端口数
                        #print('surplus_ports:',surplus_ports)
                        if surplus_ports==0:
                            break
                        #print('未扫描端口数:',surplus_ports)
                        #保证有4个进程在运行,当扫描的端口少于4个时,且线程够用,就不再增加线程
                        if thread_number<(threads+1):
                            #补齐
                            gap_thread=threads-thread_number#差的线程数
                            for i in range(gap_thread):
                                thread=threading.Thread(target=scanport,args=(ip,queue_ports.get()))
                                thread.start()
                                #print(f'当前线程数为:{len(threading.enumerate())}')

                        else:
                            #等线程结束后再增加线程
                            continue
                        
                        

                       

        else:
            print('线程数最大为20')
            exit(1)
        
        if queue_ports.empty():
            #print('扫描完成...')
            exit(1)
        




def banner():
    print('+'+'-'*50+'+')
    print('|{:^16}|{:^16}|{:^16}|'.format('ip','port','status'))
    print('+'+'-'*50+'+')

if __name__=='__main__':
    #None为python空类型
    if opts.ip is None or opts.port is None:
        print_help()
    else:
        #检查IP格式
        ip=re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$',opts.ip)#匹配成功返回ip,不成功返回None
        if ip is None:
            print('ip格式错误')
            exit(1)
        else:
            ip=ip.group()
            try:#检查端口格式
                port=int(opts.port)
            except:
                #端口格式报错,说明输入是端口范围
                ports=re.match('^\d{1,5}-\d{1,5}$',opts.port)
                if ports is None:
                    print('端口格式错误')
                    exit(1)
                else:
                    #print('端口格式正确')
                    ports=ports.group()
                    threads=opts.thread
                    #print('threads:',type(threads))
                    if threads is None:#不输入线程数默认使用单线程
                        print('单线程扫描...')
                        banner()
                        singleThread(ip,ports)
                    else:
                        if threads>0 and threads<=20:
                            print('多线程扫描...')
                            banner()
                            MultiThread(ip,ports,threads)
                            print('-'*50)
                            print('扫描完成...')
                        else:
                            print('线程数错误,最大为20')
                            exit(1)
                        
                    
            else:
                banner()
                #为单端口
                singleThread(ip,port)
    #只剩下主线程,结束程序
    if threading.enumerate()==1:
        exit(1)    



'''
使用示例:
python portscan.py -i 127.0.0.1 -p 440-500 --thread 10 --brief使用10个线程扫描,只显示开放端口
python portscan.py -i 127.0.0.1 -p 440-500 --thread 10 使用10个线程扫描,显示所有端口
python portscan.py -i 127.0.0.1 -p 440-500不加线程参数,默认使用单线程
python portscan.py -i 127.0.0.1 -p 440-440只扫描440端口
python portscan.py -i 127.0.0.1 -p 445只扫描445端口
'''

练习使用python optparse写的一个作业,粗糙还望见谅!目前还存在Bug,也暂时还找不出问题所在。有需求的大佬可以进一步更改以实现更多的功能,比如说扫描ip段。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值