Python 端口扫描器


前言

任何一个靠谱的网络攻击都是起步于侦查的。在这里,我们将使用Python来编写一个扫描目标主机或服务器开放的TCP端口的侦查脚本程序。


一、TCP全连接

所以成功的网络攻击一般都是以端口扫描拉开序幕的,因此在此我们使用TCP全连接扫描来确定目标主机的端口开放情况。

TCP连接的基本概念:

三次握手:

1、第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

2、第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

3、第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

则顾名思义,TCP全连接就是完成三次握手步骤的过程。

二、程序编写

1.获得主机名和端口

为了实现从用户方获取数据,我们在程序中使用optparse库解析命令行参数。调用optparse.OptionParser(usage)会生成一个参数解析器类的实例。接着在parser.add_option中指定这个脚本具体要解析哪个命令行参数。
代码实例:

	usage="usage %prog -H <target host> -p/-P <target ports>"
    parser=optparse.OptionParser(usage)  #创建对象实例
    parser.add_option('-H',dest='Host',type='string',help='target host')   ##需要的命令行参数
    parser.add_option('-P','-p',dest='Ports',type='string',help='target ports')

2.解析主机名和端口

接下来,我们要生成两个函数:ConnScan和portScan。portScan函数以参数形式接收主机名和目标端口列表。它首先会尝试用gethostbyname()函数确定主机名对应的IP地址。接下来,它会实验ConnScan函数输出主机名(或IP地址),并使用ConnScan()函数尝试逐个建立与目标端口的连接。如果成功,则打印一个端口开放的信息,如果失败,则打印端口关闭的信息。

代码:

def connScan(tgtHost,tgtPort):
    try:
        conn=socket(AF_INET,SOCK_STREAM)
        conn.connect((tgtHost,tgtPort))
        screenLock.acquire()                      #加锁
        print('[+]%d/tcp open'% tgtPort)
        conn.close()
    except Exception as e:
        screenLock.acquire()
        print(e)
        print('[-]%d/tcp closed'% tgtPort)
    finally:
        screenLock.release()           #释放锁
        conn.close()


def portScan(tgtHost,tgtPorts):
    try:
        tgtIP=gethostbyname(tgtHost)          ##获得对应主机的ip地址
    except:
        print("[-] Cannot resolve '%s':Unknown host" %tgtHost)
        return
    try:
        Name=gethostbyaddr(tgtIP)     ##获得ip对应主机的信息
        print ("\n[+] Scan Results for:"+Name[0])
    except:
        print ("\n[+] Scan Results for:"+tgtIP)
    setdefaulttimeout(1)
    for Port in tgtPorts:
        t = Thread(target=connScan,args=(tgtHost,int(Port)))
        t.start()

3.抓取应用的Banner

为了抓取目标主机上的应用的Banner,我们必须在ConnScan函数中插入一些新增的代码。找到开放的端口后,我们向它发送一个数据串并等待响应。跟进收集到的响应,我们就推断出目标主机和端口上运行的应用。
代码:

		conn.send('test message\r\n'.encode("utf-8"))             #发送测试信息给端口
        results=conn.recv(100)                    #接收主机返回的信息
		print('[+] '+results.decode("utf-8"))

4.线程扫描

根据套接字中timeout变量的值,每扫描一个套接字都会花费几秒钟,看上去微不足道。但如果一旦要扫描的主机和端口多起来时,就会造成效率的严重下降。因此我们引入Python线程,线程是一种能提供这类同时执行多项任务的方法。具体到我们的脚本代码中,即我们要修改portScan()函数中迭代循环的代码。
代码:

    for Port in tgtPorts:
        t = Thread(target=connScan,args=(tgtHost,int(Port)))
        t.start()

这让我们的速度有了显著提升。

5.信号量机制

增加线程机制后程序的运行效率得到了提升,但这又有一个缺点。ConnScan()函数会在屏幕上打印一个输出。如果多个线程同时打印输出,就可能出现乱码和失序。为了让输出按正常顺序流程实现,我们需要使用信号量机制(semaphare)。一个简单的信号量就能阻止其他线程运行。注意,在打印输出前,我们用Lock.acquire()执行一个加锁操作。如果信号量还没有被锁上,线程就有权继续运行,并打印输出到屏幕上。如果信号量被锁定,当前线程只能等待正在持有信号量的线程释放信号量。通过利用信号量,我们确保了任何时间段只能有一个线程打印屏幕。

代码:

	screenLock.acquire()                      #加锁
        print('[+]%d/tcp open'% tgtPort)
        print('[+] '+results.decode("utf-8"))
        conn.close()
    except Exception as e:
        screenLock.acquire()
        print(e)
        print('[-]%d/tcp closed'% tgtPort)
    finally:
        screenLock.release()           #释放锁
        conn.close()

总结

综上所述,汇总在一起,并添加一些函数解析代码,这就有了我们最终的一个简单的TCP连接端口扫描器脚本。

代码:

import optparse
from socket import *
from threading import *
screenLock = Semaphore(value=1)

def connScan(tgtHost,tgtPort):
    try:
        conn=socket(AF_INET,SOCK_STREAM)
        conn.connect((tgtHost,tgtPort))
        conn.send('test message\r\n'.encode("utf-8"))             #发送测试信息给端口
        results=conn.recv(100)                    #接收主机返回的信息
        screenLock.acquire()                      #加锁
        print('[+]%d/tcp open'% tgtPort)
        print('[+] '+results.decode("utf-8"))
        conn.close()
    except Exception as e:
        screenLock.acquire()
        print(e)
        print('[-]%d/tcp closed'% tgtPort)
    finally:
        screenLock.release()           #释放锁
        conn.close()


def portScan(tgtHost,tgtPorts):
    try:
        tgtIP=gethostbyname(tgtHost)          ##获得对应主机的ip地址
    except:
        print("[-] Cannot resolve '%s':Unknown host" %tgtHost)
        return
    try:
        Name=gethostbyaddr(tgtIP)     ##获得ip对应主机的信息
        print ("\n[+] Scan Results for:"+Name[0])
    except:
        print ("\n[+] Scan Results for:"+tgtIP)
    setdefaulttimeout(1)
    for Port in tgtPorts:
        t = Thread(target=connScan,args=(tgtHost,int(Port)))
        t.start()


def main():
    usage="usage %prog -H <target host> -p/-P <target ports>"
    parser=optparse.OptionParser(usage)  #创建对象实例
    parser.add_option('-H',dest='Host',type='string',help='target host')   ##需要的命令行参数
    parser.add_option('-P','-p',dest='Ports',type='string',help='target ports')
    (options,args)=parser.parse_args()
    Host=options.Host
    Ports=str(options.Ports).split(',')
    if (Host==None)|(Ports[0]==None):         ##如果主机和端口都是空的话
        print(parser.usage)
        exit(0)
    portScan(Host,Ports)
if __name__=='__main__':
    main()


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值