编写一个端口扫描器(TCP全连接扫描)
借鉴于:《python绝技:运用python成为顶级黑客》
需要用到的模块:
socket
optparse(感觉这个不用也可以,但可以学习一下这个模块)
模块小讲解:
socket:
主要用来构建TCP连接,发送包,和接收包
百度上对于该模块的说明以及很详细了
比如这个:https://www.cnblogs.com/aylin/p/5572104.html
TCP连接:
比如向一个地址(或服务器)请求连接,你需要先给它发送一个请求连接的包。服务器收到包后,会回复给你一个确认包(该包会存放一些该服务器的信息),若是reset包则说明该端口是关闭的。你收到这个包后,再回复服务器一个包。这样你的主机和那服务器就建立好了连接。(详情参见TCP/IP三次握手)
如果仅仅只是用于扫描,就不用把TCP连接建立完(否则很费时间),只需要当你向服务器发送一个包后,只要你收到了该服务器回复的确认包,那就可以说明该地址(服务器)的该端口是开放的。相反,如果是reset包,则说明该端口是关闭的。
optparse:
用来给自己写的函数 设置如何输入,输入什么类型的值,以及设置帮助说明(有点难理解?看看下面吧)
如何使用:
1.载入OptionParser类,新建对象: OptionParser()
parser=OptionParser()
2.添加选项: add_option(…)
例如:
parser.add_option('-H', dest='tgtHost', type='string', help='specify target host')
意思是:我对函数设置了一个操作,这个操作的使用方式是-H,后面输入的参数赋值给tgtHost,参数的类型必须是string, 使用-help可以获取帮助信息(这里就是specify target host)
这样我们在 命令行下 使用这个python函数时,则需要下面的形式进行使用:
传递参数:-H 172.2.22.222 (172.2.22.222将传递给形参tgtHost)
获取帮助则是:-H -help
再通过
tgtHost = options.tgtHost
将接收到的tgtHost参数递出,在这里也可以对参数进行切片等操作
例如:
tgtHost = str(options.tgtHost).split('.') #根据 . 进行分割
则实际传递出去就变成了[‘172’ ,‘2’ ,‘22’, ‘222’ ] 这样一个list
3.参数解析: parse_args()
用来传出之前得到的输入
parse_args() 返回的两个值:
options,它是一个对象,保存有之前接收到的命令行参数值。
比如通过options.tgtHost就可以得到之前输入的‘172.2.22.222’
args ,它是没被解析的命令行参数的列表。
如果还不太清楚可以参见下:https://www.jianshu.com/p/bec089061742
或者看看接下来的代码
照着书上敲的代码:
一些说明:接下来,我们要生成两个函数connScan和portScan。portScan函数以参数的形式接收主机名和目标端口列表。它首先会尝试用gethostbyname()函数确定主机名对应的IP地址。接下来,它会使用connScan函数输出主机名(或IP地址),并使用connScan()函数尝试逐个连接我们要连接的每个端口。connScan函数接收两个参数:tgtHost和tgtport,它会去尝试建立与目标主机和端口的连接。如果成功,connScan将打印出一个端口开放的消息。如果不成功,它会打印出端口关闭的消息。
def connScan(tgtHost,tgtPort):
try:
connSkt=socket(AF_INET,SOCK_STREAM)
#创建socket对象
# socket = socket.socket( family, type )
#family参数代表地址家族,可为AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用于同一台机器上的进程间通信。
#type参数代表套接字类型,可为SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。
connSkt.connect((tgtHost,tgtPort)) #以元组(host,port)的形式表示地址 #连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
connSkt.send("violentpython\r\n") #调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。 使用recv方法从客户接收信息
results=connSkt.recv(100) #接受套接字的数据。数据以字符串形式返回,()内是指定最多可以接收的数量
print('[+]%d/tcp open'% tgtPort)
print('[+] '+str(results))
connSkt.close() #关闭TCP连接
except:
print('[-]%d/tcp closed'% tgtPort)
def portScan(tgtHost,tgtPorts):
try:
tgtIP=gethostbyname(tgtHost) # gethostbyname 返回的是主机名的IPv4 的地址格式,如果传入的参数是IPv4 的地址格式,则返回值跟参数一样,这个函数不支持IPv6 的域名解析
except:
print('[-] cannot resolve "s": Unknown host' %tgtHost)
return
try:
tgtName=gethostbyaddr() #返回对应于给定 地址 的包含主机名字和地址信息的hostent结构指针
print('\n[+] Scan Results for:' +tgtName[0])
except:
print('\n[+] Scan Results for:' +tgtIP)
setdefaulttimeout(1) #设置全局的超时时间为1s,即当socket尝试重连到1秒时,就会停止一切操作
for tgtPort in tgtPorts: #因为可能的输入为多个端口
print('Scanning port' +tgtPort)
connScan(tgtHost,int(tgtPort))
def main():
parser = optparse.OptionParser('usege %prog' + '-H <target host> -P <target port>') #usage 帮助部分一般在 OptionParser 初始化时输入,为第一个参数, 也可以用具体形参名指定. 可以使用 %prog 来表示当前的程序名.
parser.add_option('-H', dest='tgtHost', type='string', help='specify target host') #定义parser的操作,这里说明了使用方式为: -H tgtHost 查看帮助就是:-H -help
parser.add_option('-P', dest='tgtPort', type='string', help='specify target port') #定义了parser另一个操作,这里说明了使用方式为:-P tgtPort 查看帮助就是:-P -help
(options, args) = parser.parse_args() #parse_args() 返回的两个值(options,args),options是一个对象,保存有之前接收到的命令行参数值,可以通过options.tgtHost获取之前输入的tgtHost。 args 是没被解析的命令行参数的列表。
tgtHost = options.tgtHost #通过options.tgtHost获取之前输入的tgtHost,并传递
tgtPorts = str(options.tgtPort).split(',') #对options.tgtPort按,切片,后传递
if (tgtHost == None) | (tgtPorts[0] == None):
print('[-] you must specifyn a target host and port[s].')
exit(0)
portScan(tgtHost,tgtPorts)
if __name__=='__main__':
main()
因为使用的是pycharm编译,需要通过一些设置实现命令行式输入参数,设置方式如下:
这之后再运行即可
附上一个运行结果:
-H 112.74.29.222 -P 80,20,21
[+] Scan Results for:112.74.29.222
Scanning port80
[-]80/tcp closed
Scanning port20
[-]20/tcp closed
Scanning port21
[-]21/tcp closed
至于书上的多线程扫描,就先放着吧,以后有空再说。
然后,就没了…
该文章当中optparse模块部分讲解,由自己理解的内容居多,如果有错误或不足,欢迎指出。