“人生苦短,我用Python“——Socket、Nmap、Scapy

Socket

Socket模块的主要目的是帮助在网络上的两个程序之间建立信息通道。

在Python中提供了两个基本的Socket模块: 服务端Socket、客户端Socket。

当创建了一个服务端Socket之后,这个Socket就会在本机的一个端口上等待连接,客户端Socket会访问这个端口,当两者完成连接之后,就可以进行交互了。

客户端与服务端
使用Socket建立服务端的思路主要是首先实例化一个Socket类,然后开始循环监听,一直可以接收来自客户端的连接。成功建立连接之后,接收客户端发来的数据,并再向客户端发送数据,传输完毕之后,关闭这次连接。
使用Socket建立客户端则要简单的多,在实例化一个Socket类之后,连接一个远程的地址,这个地址由IP和端口组成。成功建立连接之后,开始发送和接收数据,传输完毕之后,关闭这次连接。

实例化Socket类

在使用Socket进行编程时,首先实例化一个Socket类,这个实例化需要三个参数:地址族、流、使用的协议

  • 地址族:IP地址
  • 流:tcp、udp
  • 使用的协议:tcp/ip模型

格式:socket(family,type[,protocal])

  1. family是要使用的地址族,常用协议族有AF_INET(ipv4)、AF_INET6(ipv6)、AF_LOCAL(或称AF_UNIX、UNIX域Socket)、AF_ROUTE等。默认值未socket.AF_INET,通常使用这个默认值即可。
  2. type用来指明Socket类型,这里可以使用的值有三个:①SOCK_STREAM,这是TCP类型,保证数据顺序及可靠性;默认是这个值。②SOCK_DGRAM,用于UDP类型。③SOCK_RAW,这是原始类型,允许对底层协议如IP或ICMP进行直接访问,基本不会用到。
  3. 第三个参数指使用的协议,这个参数是可选的。通常赋值“0”,由系统自动选择。

如果希望初始化一个TCP类型的Socket,就可以使用语句:s=socket.socket(),这条语句实际上相当于socket.socket(socket.AF_INET,socket.SOCK_STREAM),因为是默认值所以可以省略。而如果希望初始化一个UDP类型的Socket,则可以使用如下语句:s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)。

Socket常用的函数

服务端函数

bind(): 这个函数由 服务端Socket 调用,会将之前创建Socket与指定的IP地址和端口进行绑定。如果之前使用了AF_INET初始化Socket,那么这里可以使用元组(host,port)的形式表示地址。

#例如,要将刚才创建的Socket套接字绑定到本机的2345端口,就可以使用如下语句
s.bind(('127.0.0.1',2345))#元组类型,中间()不能省略。IP地址是字符串类型。

listen(): 这个函数用于在使用TCP的服务端开启监听模式。可以使用一个参数来指定可以挂起的最大连接数量。这个参数的值最小为1,一般设置为5。

#例如,要在服务端开启一个监听,可以使用如下语句。
s.listen(5)

accept(): 这个函数用于在使用 TCP的服务端接收 连接,一般是阻塞态。接收TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据;address是连接客户端的地址。

客户端函数

connect(): 这个函数用于在使用TCP的客户端去连接服务端时使用,使用的参数是一个元组,形式为(hostname.port)。

#例如,在客户端初始化了一个Socket之后,就可以使用这个函数去连接到服务端。要连接本机的2345端口,可以使用如下语句
s.connect(('127.0.0.1',2345))

服务端和客户端均可使用的函数

send(): 用于使用 TCP 时发送数据,完整形式为send(string[,flag]),利用这个函数可以将string代表的数据发送到已经连接的Socket,返回值是发送字节的数量。但是可能未将指定的内容全部发送
sendall(): 与send()类似,也是用于在使用 TCP 时发送数据,完整的形式为sendall(string[,flag])。与send()的区别时完整发送TCP数据,将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数,成功返回None,失败则抛出异常。

#例如,使用这个函数发送一段字符到Socket,可以使用如下语句。
s.sendall(bytes('hello,my friend!',encoding='utf-8'))

recv(): 用于在使用TCP接收数据,完整形式为recv(bufsize[,flag]),接收Socket的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量,flag这个参数一般不会使用。

#例如,通过这个函数接收一段长度为1024的字符Socket,可以使用如下语句。
obj.recv(1024)

sendto(): 用于在使用UDP时发送数据,完整形式为sendto(string[,flag],address),返回值是发送的字节数。address是形式为(ipaddr,port)的元组,指定远程地址。
recvfrom(): UDP专用,接收数据,返回数据远端的IP地址和端口,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
close(): 关闭socket。

使用Socket编写一个简单的服务端和客户端

首先使用Socket编写一个服务端程序
Socket套接字开始监听后,就会使用 accept函数 来等待客户端的连接。这个过程使用一个条件永远为真的循环来实现,服务端在处理完和客户端的连接后,会再次调用这个函数,等待下一个连接。

import socket

s1 = socket.socket()
s1.bind(('127.0.0.1',2345))
s1.listen(5)
while 1:
    conn,address = s1.accept()
    print('a new connect from',address)
    conn.sendall('hello world'.encode())#编码
    conn.close()

使用Socket编写一个客户端程序,这个程序最重要的是使用connect()函数来连接到目标服务端

import socket

s2 = socket.socket()
s2.connect(('127.0.0.1',2345))
data = s2.recv(1024)
s2.close()
print(data.decode())#解码

在这里插入图片描述
在这里插入图片描述

Nmap

目前Nmap已经具备如下的各种功能。

  1. 主机发现功能。向目标计算机发送信息,然后根据目标的反应来确定它是否处于开机并联网状态。
  2. 端口扫描。向目标计算机的指定端口发送信息,然后根据目标端口的反应来判断它是否开放。
  3. 服务及版本检测。向目标计算机的目标端口发送特制的信息,然后根据目标的反应来检测它运行服务的服务类型和版本。
  4. 操作系统检测

python-nmap

  • python-nmap是一个可以帮助使用Nmap功能的python模块文件。
  • 在python-nmap模块的帮助夏,可以轻松地在自己的程序中使用Nmap扫描的结果,也可以编写自动化地完成扫描任务。
  • 模块作者的个人网站为http://xael.org/

如果希望在Python中正常使用python-nmap模块,必须在系统中安装Nmap。因为在这个模块文件中会调用Nmap的一些功能。

  • windows操作系统下直接下载安装即可。
  • Linux操作系统中则需要使用命令:sudo apt-get install nmap,然后安装python-nmap:sudo pip install python-nmap。

基本用法
python-nmap模块的核心就是PortScanner(同步)、PortScannerAsync(异步)、PortScannerError、PortScannerHostDict、PortScannerYield等类,其中最为重要的是 PortScanner 类。

python-nmap模块类的实例化

最常用的是PortScanner类,这个类是实现Nmap工具功能的封装。对这个类进行实例化很简单,只需要如下语句:namp.PortScanner()。
PortScannerAsync类与PortScanner类的功能相似,但是这个类可以实现异步扫描,对这个类的实例化语句:namp.PortScannerAsync()。

python-namp模块中的函数

PortScanner类

scan(): 完整形式为scan([self,]hosts=‘127.0.0.1’,ports=None,arguments=‘-sV’,sudo=False),用来对指定目标进行扫描,其中需要设置的三个参数包括hosts、ports和arguments。

  • 参数hosts的值为字符串类型,表示要扫描的主机,形式可以是IP地址,也可以是一个域名。
  • 参数ports的值也是字符串类型,表示要扫描的端口。如果要扫描的是单一的端口,形式为"80"。如果要扫描多个端口,可以用逗号分隔,形式为"80,443,8080"。如果要扫描的是连续的端口范围,可以用横线,形式为"1-1000"。
  • 参数arguments的值也是字符串类型,这个参数实际上就是Nmap扫描时所使用的参数,如下:
参数描述
“-sP”表示对目标主机进行ping主机在线扫描
“-PR”表示对目标进行一个ARP的主机在线扫描
“-sS”表示对目标进行一个TCP半开(SYN)类型的端口扫描
“-sT”表示对目标进行一个TCP全开类型的端口扫描
“-O”表示扫描目标的操作系统类型
“-sV”表示扫描目标上所安装网络服务软件的版本

如果要对192.168.1.5的1~1000端口进行以此TCP半开扫描,可以使用如图所示命令。
在这里插入图片描述
all_host(): 返回一个被扫描的所有主机列表。(刚刚只扫描了一个主机)
在这里插入图片描述
command_line(): 返回在当前扫描中使用的命令,如图。
在这里插入图片描述
cvs(): 返回值是一个CSV(逗号分隔值文件格式)的输出,如图所示,显示一个比较公正的结果。
在这里插入图片描述
has_host(self,host): 检查是否有host的扫描结果,如果有则返回True,否则返回False。
在这里插入图片描述
scaninfo(): 列出一个扫描信息的结构。
在这里插入图片描述
这个类还支持如下操作
在这里插入图片描述

PortScannerAsync类

PortScannerAsync类最为重要的也是scan(),用法与PortScanner类中的scan()基本一样,但是多了一个回调函数
完整的scan() 函数格式为:scan(self,hosts=‘127.0.0.1’,ports=None,arguments=‘-sV’,callback=None,sudo=Flase),这里面的callback是以(host,scan_data)为参数的函数。
在这里插入图片描述
这个类中提供了一下三个用来实现异步的函数。
still_scanning(): 如果扫描正在进行,则返回True,否则返回False。
在这里插入图片描述
wait(self,timeout=None): 函数表示等待时间。
stop(): 停止当前扫描。

使用python-nmap模块来编写一个扫描器

编写一个简单的端口扫描器。扫描192.168.1.5开放从1~1000的哪些端口。

import nmap
nm=nmap.PortScanner()
nm.scan('192.168.1.5','1-1000')#扫描1-1000端口

for host in nm.all_hosts():
    print('-------------------------------------')
    print('Host:%s(%s)'%(host,nm[host].hostname()))#获取主机名
    print('State:%s'%nm[host].state())#主机状态
    for porto in nm[host].all_protocols():#获取执行的协议
        print('-------------------------------------')
        print('Protocol:%s'%porto)
        lport=nm[host][porto].keys()#获取TCP所有端口号,形成字典
        lport.sort()#排序
        for port in lport:
            print('port:%s\t state:%s'%(port,nm[host][proto][port]['state']))

运行结果
在这里插入图片描述
扫描器2:查看某网段有多少台终端设备

import nmap
nm=nmap.PortScanner()
nm.scan(hosts='192.168.0.0/24',arguments='-sP')
hosts_list=[(x,nm[x]['status']['state'])for x in nm.all_hosts()]
for host,status in hosts_list:
	print(host+'is'+status)

运行结果
在这里插入图片描述

使用一个异步程序

import nmap
nma=nmap.PortScannerAsync()
def callback_result(host,scan_result):
	print('---------------')
	print(host,scan_result)
nma.scan(hosts='192.168.1.0/24',arguments='-sP',callback=callback_result)

注:在使用scan函数扫描的过程中会执行callback_result函数,可以一边扫描一边输出扫描结果。(同步需要等待)
在这里插入图片描述

Scapy

  • Scapy是一个由Python语言编写的强大工具,它是大量程序编写人员最喜爱的一个网络模块。目前很多优秀的网络扫描工具和攻击工具都是用了这个模块。
  • 也可以在自己的程序中使用这个模块来实现对网络数据包的发送、监听和解析
  • 这个模块相比起Nmap来说,更为底层。可以更为直观地了解到网络中的各种扫描和攻击行为,例如,要检测某一个端口是否开放,只需要提供给Nmap一个端口号,而Nmap就会给出一个开放或者关闭的结果,但是不知道Nmap是怎么做的。
  • 如果想对网络中的各种问题进行深入研究,Scapy无疑是一个更好的选择,可以利用它来产生各种类型的数据包并发送出去,Scapy也只会把收到的数据包展示给你,而并不会告诉你这意味着什么,一切都将由你来判断。

基本用法

在Kali Linux中已经集成了Scapy,既可以在Python环境中使用Scapy,也可以直接使用它。
启动一个终端,输入命令“scapy”,就可以启动Scapy环境,如图
在这里插入图片描述
Scapy提供了和Python一样的交互式命令行。Scapy可以作为Python的一个模块存在,但是Scapy本身就是一个可以运行的工具,它自己具备一个独立的运行环境,因而可以不在Python环境下运行。

Scapy的基本操作

在Scapy中,每一个协议就是一个类。只需要实例化一个协议类,就可以创建一个该协议的数据包。例如,如果要创建一个IP类型的数据包,就可以使用如下命令。
在这里插入图片描述
IP数据包最重要的属性就是源地址目的地址,这两个属性可以使用src和dst来设置。例如,要构造一个发往“192.168.1.101”的IP数据包,可以使用如下语句。
在这里插入图片描述
这个目标dst的值可以是一个IP地址,也可以是一个IP范围,例如192.168.1.0/24,这时产生的就不是1个数据包,而是256个数据包。
在这里插入图片描述
如果要查询其中的每一个数据包,可以使用[p for p in ip]
在这里插入图片描述

Scapy采用分层的形式来构造数据包

  • 通常最下面的一个协议为Ether,然后是IP,再之后是TCP或者UDP。
  • IP()函数无法用来构造ARP请求和应答数据包,可以使用Ether(),
  • 这个而函数可以设置发送方和接收方的MAC地址。

产生一个广播数据包,执行的命令如下。
在这里插入图片描述
Scapy中的分层通过符号“/”实现,一个数据包是由多层协议组合而成,这些协议之间就可以使用“/”分开,按照协议由底而上的顺序从左向右排列。
例如,可以使用:Ether()/IP()/TCP() 来完成一个TCP数据包。
在这里插入图片描述
如果要构造一个HTTP数据包,也可以使用如下这种方式:IP()/TCP()/“GET / HTTP/1.0\r\n\r\n” (\r\n\r\n代表http报文的结束)
在这里插入图片描述
ls()函数来查看一个类所拥有的属性
使用ls(Ether())来查看Ether类的属性
在这里插入图片描述
使用ls(IP())来查看IP类的属性
在这里插入图片描述
可以对这里面的对应属性进行设置,例如,将ttl的值设置为32,可以使用如下方式:IP(src=‘192.168.1.5’,dst=‘192.168.1.101’,ttl=32)
在这里插入图片描述

Scapy模块中的函数

send()和sendp():

这两个函数的区别在于send()工作再第三层,而sendp()工作在第二层。send()是用来发送IP数据包的,而sendp()是用来发送Ether数据包的。
例如,构造一个目的地址为"192.168.1.101"的ICMP数据包,并将其发送出去,可以使用如下语句:send(IP(dst=‘192.168.1.101’)/ICMP())
在这里插入图片描述
sendp(Ether(dst=“ff:ff:ff:ff:ff:ff:ff”))
在这里插入图片描述

注:如果这个数据包发送成功,下方会有一个"Sent 1 packets"的显示。
这两个函数的特点是只发不收,只会将数据包发送出去,但是没有能力处理该数据包的回应包

fuzz():

如果希望发送一个内容是随机填充的数据包,而且又要保证这个数据包的正确性,那么可以是fuzz()函数。
例如,可以使用如下命令来创建一个发往192.168.1.101的TCP数据包:IP(dst=‘192.168.1.101’)/fuzz(TCP())
在这里插入图片描述

sr()、sr1()和srp():

在网络的各种应用中,需要做的不仅是将创建好的数据包发送出去,也需要接收这些数据包的应答数据包,这一点在网络扫描中尤为重要。在Scapy中提供了三个用来发送和接收数据包的函数,分别是sr()、sr1()和srp(),其中sr()和sr1()主要用于第三层,例如IP和ARP等。而srp()用于第二层。
仍然向192.168.1.101发送一个ICMP数据包来比较一下sr()和send()的区别:sr(IP(dst=“192.168.1.101”)/ICMP())
在这里插入图片描述
可以利用sr1()函数来测试目标某个端口是否开放,采用半开扫描(SYN)的办法:sr1(IP(dst=“192.168.1.4”)/TCP(dport=80,flags=“S”))
在这里插入图片描述

sniff():

这个函数可以在自己的程序中捕获经过本机网卡的数据包。
在这里插入图片描述
这个函数最强大的地方在于可以使用 参数filter 对数据包进行过滤。
例如,指定只捕获与192.168.1.102有关的数据包,可以使用"host 192.168.1.102":sniff(filter:“host 192.168.1.102”)。
同样,也可以使用filter来过滤指定的协议,例如,icmp类型的数据包:sniff(filter=“icmp”)。
如果要同时满足多个条件可以使用“and”“or”等关系运算符来表达*:sniff(filter=“host 192.168.1.101 and icmp”)。*
iface、count参数:
iface可以用来指定所要进行监听的网卡。
例如,指定eth1作为监听网卡,就可以使用:sniff(iface=“eth1”)
count用来指定监听到的数据包的数量,达到指定数量就会停止监听。
例如,只希望监听到三个数据包就停止:sniff(count=3)

设计一个综合性的监听器,它会在网卡eth0上监听源地址或目的地址为192.168.1.102的icmp数据包,当收到了三个这样的数据包之后,就会停止监听。
首先在Scapy中创建如下监听器:sniff(filter=“icmp and host 192.168.1.102”,count=3,iface=“eth0”)
正常情况下,是不会有去往或者来自192.168.1.102的icmp数据包的,所以这时候可以打开一个新的终端,然后在里面执行命令“ping 192.168.1.102”
然后再Scapy中使用组合键Ctrl+C结束捕获,这时可以看到已经捕获到三个数据包。
在这里插入图片描述
如果需要查看这三个数据包内容,可以使用"_",在Scapy中这个符号表示是上一条语句执行的结果。例如刚刚使用sniff捕获到的数据包,就可以用这个符号表示。
在这里插入图片描述
summary()是用来以摘要的形式显示包中内容,这个摘要的长度为一行。

Scapy模块的常用简单实例

使用Scapy来实现以此ACK类型的端口扫描
ACK扫描:扫描主机向目标主机发送ACK数据包。根据返回的RST数据包有两种方法可以得到端口信息。方法一是:若返回的RST数据包的TTL值小于或等于64,则端口开放,反之端口关闭==(不准确)==。
例如,对192.168.1.102的21、23、135、443、445五个端口是否被屏蔽进行扫描,注意是屏蔽不是关闭,采用ACK扫描模式,可以构造如下命令方式:ans,unans=sr(IP(dst=“192.168.1.102”)/TCP(dport=[21,23,135,443,445],flags=“A”))

正常时候,如果一个开放的端口会回应ack数据包,而关闭的端口会回应rst数据包。在网络中,一些网络安全设备会过滤掉一部分端口,这些端口不会相应来自外界的数据包,一切发往这些端口的数据包都如同石沉大海。注意这些端口的状态并非是开放或者关闭,而是被屏蔽。——不准确

在这里插入图片描述
向目标发送了5个标志位置为“A”的TCP数据包。按照TCP三次握手的规则,如果目标端口没有被过滤,发出的数据包就会得到回应,否则没有回应。另外,根据Scapy的设计,ans列表中的数据包就是得到了回应的数据包,而unans中的则是没有得到回应的数据包,只需要分两次来读取这两个列表就可以得到端口的过滤结果

for s,r in ans:
	if s[TCP].dport==r[TCP].sport:
		print str(s[TCP].dport) + "is unfiltered"

也可以用类似的方法来查看被过滤的端口:

for s in unans:
	print str(s[TCP].dport) + " is filtered"

在Python中编写程序来实现一个查看端口是否被屏蔽的简单程序。首先导入scapy模块中的函数:from scapy.all import IP,TCP,sr 。这里需要IP()和TCP()来产生所需要的数据包,sr()函数来发送,然后发送构造好的数据包,在Python交互式命令行中(还是在kali Scapy中)执行这个程序。
在这里插入图片描述
使用Scapy强大的包处理能力来设计一个端口是否开放的扫描器

  • 如果一个端口处于屏蔽状态,那么它将不会产生任何响应报文。
  • 如果一个端口处于开放状态,那么它在收到syn数据包之后,就会回应一个ack数据包。
  • 反之,如果一个端口处于关闭状态,那么它在收到syn数据包之后,就会回应一个rst数据包。
from scapy.all import fuzz,TCP,IP,sr

ans,unans=sr(IP(dst='192.168.26.100')/fuzz(TCP(dport=80,flags='S')))
for s,r in ans:
	if r[TCP].flags==18:#返回数据包flags值为0x012(SYN,ACK)目标端口为开放状态
		print 'This port is Open'
	if r[TCP].flags==20:#返回数据包flags值为0x014(RST,ACK)目标端口为关闭状态
		print 'This port is Closed'
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值