赞
踩
Socket模块的主要目的是帮助在网络上的两个程序之间建立信息通道。
在Python中提供了两个基本的Socket模块: 服务端Socket、客户端Socket。
当创建了一个服务端Socket之后,这个Socket就会在本机的一个端口上等待连接,客户端Socket会访问这个端口,当两者完成连接之后,就可以进行交互了。
客户端与服务端
使用Socket建立服务端的思路主要是首先实例化一个Socket类,然后开始循环监听,一直可以接收来自客户端的连接。成功建立连接之后,接收客户端发来的数据,并再向客户端发送数据,传输完毕之后,关闭这次连接。
使用Socket建立客户端则要简单的多,在实例化一个Socket类之后,连接一个远程的地址,这个地址由IP和端口组成。成功建立连接之后,开始发送和接收数据,传输完毕之后,关闭这次连接。
在使用Socket进行编程时,首先实例化一个Socket类,这个实例化需要三个参数:地址族、流、使用的协议。
格式:socket(family,type[,protocal])
- family是要使用的地址族,常用协议族有AF_INET(ipv4)、AF_INET6(ipv6)、AF_LOCAL(或称AF_UNIX、UNIX域Socket)、AF_ROUTE等。默认值未socket.AF_INET,通常使用这个默认值即可。
- type用来指明Socket类型,这里可以使用的值有三个:①SOCK_STREAM,这是TCP类型,保证数据顺序及可靠性;默认是这个值。②SOCK_DGRAM,用于UDP类型。③SOCK_RAW,这是原始类型,允许对底层协议如IP或ICMP进行直接访问,基本不会用到。
- 第三个参数指使用的协议,这个参数是可选的。通常赋值“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)。
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套接字开始监听后,就会使用 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已经具备如下的各种功能。
python-nmap
如果希望在Python中正常使用python-nmap模块,必须在系统中安装Nmap。因为在这个模块文件中会调用Nmap的一些功能。
基本用法
python-nmap模块的核心就是PortScanner(同步)、PortScannerAsync(异步)、PortScannerError、PortScannerHostDict、PortScannerYield等类,其中最为重要的是 PortScanner 类。
最常用的是PortScanner类,这个类是实现Nmap工具功能的封装。对这个类进行实例化很简单,只需要如下语句:namp.PortScanner()。
PortScannerAsync类与PortScanner类的功能相似,但是这个类可以实现异步扫描,对这个类的实例化语句:namp.PortScannerAsync()。
scan(): 完整形式为scan([self,]hosts=‘127.0.0.1’,ports=None,arguments=‘-sV’,sudo=False),用来对指定目标进行扫描,其中需要设置的三个参数包括hosts、ports和arguments。
参数 | 描述 |
---|---|
“-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类最为重要的也是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(): 停止当前扫描。
编写一个简单的端口扫描器。扫描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函数,可以一边扫描一边输出扫描结果。(同步需要等待)
在Kali Linux中已经集成了Scapy,既可以在Python环境中使用Scapy,也可以直接使用它。
启动一个终端,输入命令“scapy”,就可以启动Scapy环境,如图
Scapy提供了和Python一样的交互式命令行。Scapy可以作为Python的一个模块存在,但是Scapy本身就是一个可以运行的工具,它自己具备一个独立的运行环境,因而可以不在Python环境下运行。
在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() 来完成一个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)
这两个函数的区别在于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()函数。
例如,可以使用如下命令来创建一个发往192.168.1.101的TCP数据包:IP(dst=‘192.168.1.101’)/fuzz(TCP())
在网络的各种应用中,需要做的不仅是将创建好的数据包发送出去,也需要接收这些数据包的应答数据包,这一点在网络扫描中尤为重要。在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”))
这个函数可以在自己的程序中捕获经过本机网卡的数据包。
这个函数最强大的地方在于可以使用 参数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来实现以此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强大的包处理能力来设计一个端口是否开放的扫描器
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'
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。