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)
运行效果如下图