Python构造简单端口扫描程序

#-*- coding: UTF-8 -*-
import optparse
from socket import *
from threading import *


#由于线程同步导致扫描不同端口输出的次序会乱掉,因此设置一个信号量Semaphore,用来在输出时锁定线程,保证端口扫描输出依次打印
screenLock = Semaphore(value = 1)  #定义一个信号量用于锁定线程

#用于连接指定的地址以及指定端口,建立连接发送信息,看是否有返回信息以确定端口是否开放
def connScan(tgtHost, tgtPort):
  try:
    '''
    int socket(int domain, int type, int protocol);
    #参数domain用于设置网络通信的域       AF_INET,PF_INET  IPv4 Internet协议
    #参数type用于设置套接字通信的类型     SOCK_STREAM:Tcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输
    #参数protocol用于制定某个协议的特定类型
    '''
    connSkt = socket(AF_INET, SOCK_STREAM) #建立一个流式套接字
    '''
    int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
    connect函数通常用于客户端建立tcp连接。  
    参数sockfd:标识一个套接字。
    参数serv_addr:套接字s想要连接的主机地址和端口号。
    参数addrlen:name缓冲区的长度。
    返回值:成功则返回0,失败返回-1,错误原因存于errno中。
    '''
    connSkt.connect((tgtHost, tgtPort)) #连接指定的地址和端口
    '''
    int send( SOCKET s,const char* buf,int len,int flags);
    客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
    第一个参数指定发送端套接字描述符;
    第二个参数指明一个存放应用程序要发送数据的缓冲区;
    第三个参数指明实际要发送的数据的字节数;
    第四个参数一般置0。
    '''
    connSkt.send('ViolentPython\r\n') #发送消息
    '''
    int recv(SOCKET s,char* buf,int len,int flags); 
    不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。 
    第一个参数指定接收端套接字描述符;
    第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
    第三个参数指明buf的长度;
    第四个参数一般置0。 
    '''
    results = connSkt.recv(100) #若端口开放,记录返回的消息
    '''
    【信号量】用于控制对某资源访问的同一时间的并发量
    semaphore.acquire(),没信号量可用时,将进行阻塞等
    【如何释放】semaphore.release()
    '''
    screenLock.acquire() #Lock锁定线程
    print ('[+]%d/tcp open'% tgtPort)
    print ('[+] ' + str(results))
  except:
    screenLock.acquire()
    print ('[-] %d/tcp closed'% tgtPort)
  finally:
    screenLock.release()  #释放控制信号量
    connSkt.close()  #关闭 Socket 连接并释放所有关联的资源


#获取主机名,打印,调用connScan()进行扫描
def portScan(tgtHost,tgtPorts):
  try:
    tgtIP = gethostbyname(tgtHost)  #返回的是 主机名 的IPv4 的地址格式
  except:
    print ("[-] Cannot resolve '%s': Unknown host" %tgtHost)
    return
  try:
    tgtName = gethostbyaddr(tgtIP)   #根据IP地址反向查找主机名称
    print ('\n[+] Scan Results for:' + tgtName[0])
  except:
    print ('\n[+] Scan Results for:' +tgtIP)
  setdefaulttimeout(1) #时间戳,通过socket模块的setdefaulttimeout函数来控制超时时间
  for tgtPort in tgtPorts:
    '''
    Thread是线程类,构造方法: Thread(group=None, target=None, name=None, args=(), kwargs={}) 
    group: 线程组,目前还没有实现,库引用中提示必须是None; 
   target: 要执行的方法; 
   name: 线程名; 
   args/kwargs: 要传入方法的参数。
    '''
    t = Thread(target = connScan, args=(tgtHost, int(tgtPort))) #创建线程,调用connScan()
    t.start()   #启动线程

def main(): 
  '''
  MSG_USAGE = "myprog[ -f <filename>][-s <xyz>] arg1[,arg2..]"
  optParser = OptionParser(MSG_USAGE)
  构造一个OptionParser的对象optParse,传入的值MSG_USAGE可被调用打印命令时显示出来
  '''
  parser = optparse.OptionParser("usage%prog"+"-H <target host> -p <target port>")
  '''
  调用OptionParser.add_option()添加选项
  add_option()参数说明:
  action:存储方式,分为三种store、store_false、store_true
  type:类型
  dest:存储的变量
  default:默认值
  help:帮助信息
  '''
  parser.add_option('-H', dest = 'tgtHost', type = 'string', help = 'specify target host')
  parser.add_option('-p', dest = 'tgtPort', type = 'string', help = 'specify target port[s] separated by comma')
  '''
  调用OptionParser.parse_args()剖析并返回一个directory和一个list
  options为是一个directory,它的内容fakeArgs为“参数/值 ”的键值对。
  args 是一个list,它的内容是parser除去options后,剩余的输入内容。
  '''
  (options,args) = parser.parse_args()
  tgtHost = options.tgtHost #获取主机名
  tgtPorts = str(options.tgtPort).split(',')  #获取端口列表,端口间用逗号分隔
  if(tgtHost == None) | (tgtPorts[0] == None):
    print (parser.usage)
    exit(0)
  portScan(tgtHost, tgtPorts)

if __name__ == '__main__':
  main()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值