python内网主机存活探测
实验环境
1.在Ubuntu系统下进行发包
2.使用python中的scapy模块
ARP知识
ARP协议是“Address Resolution Protocol”(地址解析协议)的缩写。其作用是在以太网环境中,数据的传输所依懒的是MAC地址而非IP地址,而将已知IP地址转换为MAC地址的工作是由ARP协议来完成的。
在局域网中,网络中实际传输的是“帧”,帧里面是有目标主机的MAC地址的。在以太网中,一个主机和另一个主机进行直接通信,必须要知道目标主机的MAC地址。但这个目标MAC地址是如何获得的呢?它就是通过地址解析协议获得的。所谓“地址解析”就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。
ARP请求
任何时候,当主机需要找出这个网络中的另一个主机的物理地址时,它就可以发送一个ARP请求报文,这个报文包好了发送方的MAC地址和IP地址以及接收方的IP地址。因为发送方不知道接收方的物理地址,所以这个查询分组会在网络层中进行广播。
ARP响应
局域网中的每一台主机都会接受并处理这个ARP请求报文,然后进行验证,查看接收方的IP地址是不是自己的地址,只有验证成功的主机才会返回一个ARP响应报文,这个响应报文包含接收方的IP地址和物理地址。这个报文利用收到的ARP请求报文中的请求方物理地址以单播的方式直接发送给ARP请求报文的请求方。
所以在局域网内进行主机存活探测时我们可以使用广播ARP报文的形式,看看是否收到了ARP报文来判断此主机是否存活。
操作
下面我们创建了一个多线程的子类
使用threading模块
创建多线程子类的模板如下,重写了run方法,在我们启动线程时run()方法就是线程的开始
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
我们的子类写得比较简单
class IPscan(threading.Thread):
def __init__(self, qu,li):#将有保存有IP地址的队列qu传入类中
threading.Thread.__init__(self)
self.Qu = qu
self.li = li
def CreatARP(self, ip):#构造ARP报文
page = Ether()/ARP(pdst = ip)
result = srp1(page, timeout = 0.5)#使用srp1第二层发包及按照我们Ether()报头里的格式发
if result:#因为没有收到响应的响应时,会返回None
self.li.append(f'the {ip} is host'+'\t' 'the mac:'+ result[ARP].hwsrc)
#将响应的IP以及物理地址保存
def run(self):#线程的入口
while not self.Qu.empty():
self.CreatICMP(self.Qu.get())
几个发包方式可见scapy几种发送
上面就是我们的多线程子类
接下来我们还的编写一个main()函数
首先对传进去的参数判断是否是一个网段还是单个IP(虽然可以直接ARP(pdst=‘192.168.0.1/24’)便可以直接扫描整个网段,但是为了练习一下正则表达式以及多线程还是一个的发)使用正则表达式进行判断。
thread_list = []
list_host = []
threadnumber = 1
Qu = queue.Queue()
flag = False
if re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.\d{0,3}$',ip) !=None:
Qu.put(ip)#判断是否为单个IP,将得到的IP压入栈中
elif re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.\d{0,3}/\d{0,3}$', ip) != None:
#判断是否为一个网段,然后将所有IP压入栈中
ip_str = re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.', ip).group()
for x in range(0, 256):
Qu.put(ip_str + str(x))
else:
flag = True
如果是单个IP我们就使用一个线程如果是个网段就使用多线程
if flag:
threadnumber = 10
for x in range(threadnumber):
thread_list.append(IPscan(Qu, li=list_host ))#创建一个个的多线程子类对象,将IP队列传入,将list_host传入用来保存存活的IP以及他们的物理地址
for x in thread_list:#启动线程
x.start()
for x in thread_list:#在进程中阻塞线程,等待所以线程执行完毕
x.join()
接下来输出存活的IP
for x in list_host:
print(x)
完整的代码()
from scapy.all import *
from scapy.layers.l2 import ARP, Ether
import threading
import queue
import re
class IPscan(threading.Thread):
def __init__(self, qu, li):
threading.Thread.__init__(self)
self.Qu = qu
self.li
def CreatICMP(self, ip):
page = Ether()/ARP(pdst = ip)
page.show()
result = srp1(page, timeout = 0.5)
print(result)
if result:
self.li.append(f'the {ip} is host'+'\t' 'the mac:'+ result[ARP].hwsrc)
def run(self):
while not self.Qu.empty():
self.CreatICMP(self.Qu.get())
def main( ip):#
thread_list = []
list_host = []
threadnumber = 1
Qu = queue.Queue()
flag = False
if re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.\d{0,3}$',ip) !=None:
Qu.put(ip)
elif re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.\d{0,3}/\d{0,3}$', ip) != None:
ip_str = re.match(r'^\d{0,3}.\d{0,3}.\d{0,3}.', ip).group()
for x in range(0, 256):
Qu.put(ip_str + str(x))
else:
flag = True
if flag:
threadnumber = 10
for x in range(threadnumber):
thread_list.append(IPscan(Qu, li=list_host ))
for x in thread_list:
x.start()
for x in thread_list:
x.join()
for x in list_host:
print(x)
if __name__ =='__main__':
main('192.168.0.0/24')
还有这种属于敏感操作得以root权限启动pycharm,我们在编写过程中才更好调试