基于ARP的主机发现

ARP的工作原理

ARP协议属于数据链路层的协议,主要负责根据网络层地址(IP)来获取数据链路层地址(MAC)。以太网协议规定,同一局域网的一台主机要和另外一台主机进行直接通信,必须知道目标主机的MAC地址。而TCP/IP中,网络层只关注目标主机的IP地址。这就导致在以太网中使用IP协议时,数据链路层的以太网协议接收到的网络层IP协议提供的数据中只包含目的主机的IP地址。于是需要ARP协议来完成IP地址到MAC地址的转换。

在这里插入图片描述
上面通信大概就是假如PC1知道PC3的IP地址但是不知道其MAC地址时,它会向以太网发送ARP请求以求询问,当每台主机收到这个请求后,对照自己的IP地址,不对应则丢弃该数据包,当PC3拿到该数据包后,便和PC1进行通信,然后PC1将PC3的MAC地址存入缓存表中。因为涉及到缓存表,因此查询的速度非常快,在同一以太网中,使用ARP进行主机发现是最好的选择。

这是scapy的参数详解
在这里插入图片描述
这里的op代表消息类型,hwsrc代表源MAC地址,psrc代表源IP地址,pdst代表目的IP地址

代码展示

首先依旧是先导入相关的模块

import os
import re
import optparse
from scapy.all import *

使用正则表达式匹配对应的MAC地址和IP地址

#取IP地址和MAC地址函数
def HostAddress(iface):
    #os.popen执行后返回执行结果
    ipData = os.popen('ifconfig '+iface)
    #对ipData进行类型转换,再用正则进行匹配
    dataLine = ipData.readlines()
    #re.search利用正则匹配返回第一个成功匹配的结果,存在结果则为true
    #取MAC地址
    if re.search('\w\w:\w\w:\w\w:\w\w:\w\w:\w\w',str(dataLine)):
        #取出匹配的结果
        MAC = re.search('\w\w:\w\w:\w\w:\w\w:\w\w:\w\w',str(dataLine)).group(0)
    #取IP地址
    if re.search(r'((2[0-4]\d|25[0-5]|[1]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[1]?\d\d?)',str(dataLine)):
        IP = re.search(r'((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)',str(dataLine)).group(0)
    #将IP和MAC通过元组的形式返回
    addressInfo = (IP,MAC)
    return addressInfo

这里说一下\w那个是匹配MAC地址,python的popen函数是在命令行中执行命令并返回对应的值。

以下是正则表达式的部分规则

字符 描述
[ABC] 匹配 […] 中的所有字符,例如 [aeiou] 匹配字符串 “google runoob taobao” 中所有的 e o u a 字母
[A-Z] [A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母
\w 匹配字母、数字、下划线。等价于 [A-Za-z0-9_]
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}
+ 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}
匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}
{n} n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o
{n,} n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格

编写ARP探测函数,自动生成目标进行探测并把结果写入文件

#ARP扫描函数
def ArpScan(iface='eth0'):
    #通过HostAddress返回的元组取出MAC地址
    mac = HostAddress(iface)[1]
    #取出本机IP地址
    ip = HostAddress(iface)[0]
    #对本机IP地址进行分隔并作为依据元素,用于生成需要扫描的IP地址
    ipSplit = ip.split('.')
    #需要扫描的IP地址列表
    ipList = []
    #根据本机IP生成IP扫描范围
    for i in range(1,255):
        ipItem = ipSplit[0] + '.' + ipSplit[1] + '.' + ipSplit[2] + '.' + str(i)
        ipList.append(ipItem)
    '''
        发送ARP包
        因为要用到OSI的二层和三层,所以要写成Ether/ARP
        因为最底层用到了二层,所以要用srp()发包
    '''
    result = srp(Ether(src=mac,dst='FF:FF:FF:FF:FF:FF')/ARP(op=1,hwsrc=mac,hwdst='00:00:00:00:00:00',pdst=ipList),iface=iface,timeout=2,verbose=False)
    #读取result中的应答包和应答包内容
    resultAns = result[0].res
    #存活主机列表
    liveHost = []
    #number为接受到应答包的总数
    number = len(resultAns)
    print("======================")
    print("ARP探测结果 ")
    print("本机IP地址:" + ip)
    print("本机MAC地址:" + mac)
    print("======================")
    for x in range(number):
        IP = resultAns[x][1][1].fields['psrc']
        MAC = resultAns[x][1][1].fields['hwsrc']
        liveHost.append([IP,MAC])
        print("IP:" + IP + "\n\n" + "MAC:" + MAC)
        print("======================")
    #将存活主机IP写入文件
    resultFile = open("result","w")
    for i in range(len(liveHost)):
        resultFile.write(liveHost[i][0] + "\n")
    resultFile.close()

定义主函数

if __name__ == '__main__':
    parser = optparse.OptionParser('usage: python %prog -i interfaces \n\n'
                                   'Example: python %prog -i eth0\n')
    #添加网卡参数-i
    parser.add_option('-i','--iface',dest='iface',default='eth0',type='string',help='interfaces name')
    (options,args) = parser.parse_args()
    ArpScan(options.iface)

运行效果如下图
在这里插入图片描述
以下是调用nmap模块编写的代码,代码变动不多,和之前ICMP主机发现类似,代码也简单很多

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import nmap
import optparse


def NmapScan(targetIP):
	# 实例化PortScanner对象
	nm = nmap.PortScanner()
	try:
		# hosts为目标IP地址,argusments为Nmap的扫描参数
		# -PR:使用ARP进行扫描
		result = nm.scan(hosts=targetIP, arguments='-PR')
		# 对结果进行切片,提取主机状态信息
		state = result['scan'][targetIP]['status']['state']
		print("[{}] is [{}]".format(targetIP, state))
	except Exception  as e:
		pass


if __name__ == '__main__':
	parser = optparse.OptionParser('usage: python %prog -i ip \n\n'
                                    'Example: python %prog -i 192.168.1.1[192.168.1.1-100]\n')
	# 添加目标IP参数-i
	parser.add_option('-i','--ip',dest='targetIP',default='192.168.1.1',type='string',help='target ip address')
	options,args = parser.parse_args()
	# 判断是单台主机还是多台主机
	# ip中存在-,说明是要扫描多台主机
	if '-' in options.targetIP:
		# 代码意思举例:192.168.1.1-120
		# 通过'-'进行分割,把192.168.1.1和120进行分离
		# 把192.168.1.1通过','进行分割,取最后一个数作为range函数的start,然后把120+1作为range函数的stop
		# 这样循环遍历出需要扫描的IP地址
		for i in range(int(options.targetIP.split('-')[0].split('.')[3]),int(options.targetIP.split('-')[1])+1): 
			NmapScan(options.targetIP.split('.')[0] + '.' + options.targetIP.split('.')[1] + '.' + options.targetIP.split('.')[2] + '.' + str(i))
	else:	
		NmapScan(options.targetIP)

运行效果如下图
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45007073/article/details/113437355
arp