ARP协议简介
ARP 的中文名字是 “地址解析协议”,主要用在以太网中。 这里有一点需要明确的是, 所有的主机在互联网中通信的时候使用的是 IP 地址,而在以太网中通信时使用的却是硬件地 址(也就是常说的 MAC 地址)。
既然在以太网中无法使用 IP 地址通信,那么这些没有考虑过硬件地址的网络应用又是如何工作的呢?
几乎所有的网络应用都能在以太网中正常工作,这其实就是依靠了刚刚提到过的 ARP, 这个协议用来在只知道 IP 地址的情况下去发现硬件地址。
例如所在的主机 IP 为 192.168.1.1, 而通信的目标 IP 地址为 192.168.1.2,同一网络中还有 192.168.1.3 和 192.168.1.4。
这 4 台主机位于同一以太网中,使用一台交换机进行通信。 但是通信时使用的是硬件地址,这个以太网的结构如下图: 当所使用的主机只知道目标的主机地址却不知道目标的硬件地址的时候,就需要使用以太广播包给网络上的每一台主机发送 ARP 请求。
其中的格式如下:
协议类型 : ARP Request (ARP 请求) 源主机 IP 地址: 192.168.1.1 目标主机 IP 地址: 192.168.1.2 源主机硬件地址 : 11:11:11:11:11:11 目标主机 MAC 地址: ff:ff:ff:ff:ff:ff
当网络中的其余三台主机在接收到这个 ARP请求数据包之后,它会用自己的 IP 地址与包中头部的目标主机 IP 地址相比较,如果不匹配,就不会做出回应。
例如,192.168.1.3 在接收到这个数据包时,就使用本身地址和 192.168.1.2 进行比较,如发现不同,不做出回应。
例如, 192.168.1.2 收到这个请求,这个设备就会给发送 IP 请求的设备发送一个 ARP回应数据包,这个回应的格式如下。
协议类型 : ARP Reply (ARP 回应) 源主机 I P 地址:192.168.1.2 目标主机 IP 地址 :192.168.1.1 源主机硬件地址: 22:22:22:22:22:22 目标主机 MAC 地址: 11:11:11:11:11:11
这个回应数据包并不是广播包,当主机收到这个回应之后,就会把结果放在 ARP 缓存表中。
缓存表的格式为:
IP地址 | 硬件地址 | 类型 |
---|---|---|
192.168.1.2 | 22:22:22:22:22:22 | 动态 |
以后当主机再需要和 192.168.1.2 通信时 只需要查询这个 ARP 缓存表,找到对应的表项,查询到硬件地址以后按照这个地址发送出去即可。
向目标发送一个ARP Request
如果目标主机处于活跃状态,它一定会回复一个ARP Reply
如果目标主机处于非活跃状态,它就不会给出任何回应
ARP实现的活跃主机扫描程序
这个程序有很多种方式可以实现,我们可以借助Scapy这个库来完成。
其核心思想就是要产生一个 ARP 请求。
我们首先查看Scapy库中ARP类型数据包中需要的参数、
ls(ARP())
运行结果: 可以看到这里面的大多数参数都有默认值,其中, hwsrc 和 psrc 分别是源硬件地址和源 IP 地址。
这两个地址不用设置,发送的时候会自动填写本机的地址。
唯一需要设置的是目的 IP 地址 pdst,将这个地址设置为目标即可。
另外,因为发送的是广播数据包,所以需要在 Ether 层进行设置,首先查看一下 Ether 的格式。
ls(Ether())
运行结果: 这一层只有三个参数,dst是目的的硬件地址,src是源硬件地址,这里面src会自动设置为本机地址。
所以只需要将dst设置为ff:ff:ff:ff:ff:ff即可将数据包发送到网络中的各个主机上。
下面构造一个扫描192.168.3.14的ARP请求数据包并将其发送出去。
这里的timeout表示停留时间只有3秒,超过3秒就自动结束。 srp是用于第二层的。
data =Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.43.1")
srp(data,timeout=3)
在此之前我们需要打开wireshark进行监听分析。
因为我的物理机没有安装WireShark所以我就需要用到Kali,因为Kali默认安装的WireShark。
当我们开发Kali之后我们直接在命令行上输入wireshark运行wireshark即可运行。
打开之后这就是我们看到的页面,然后我们运行我们在Pycharm中编写的代码:
data = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="192.168.43.1")
ans, unans = srp(data, timeout=3)
ans.summary()
运行结果: 当我们运行完后我们可以在WireShark中看到我们发出去的广播包。 192.168.43.156就到处在问你们谁是192.168.43.1,是的话就给个回复。
这里为什么没有回复呢?
其原因就是arp缓存表中已经存在了。
虽然在WireShark中没有显示,但是我们运行了自己编写的代码后还是有包回应的。
Ether / ARP who has 192.168.43.1 says 192.168.43.156 ==> Ether / ARP is at 72:b8:9a:72:f3:4a says 192.168.43.1
这里就是由192.168.43.156喊话谁是192.168.43.1,然后我们就收到了一个回应;我是192.168.43.1,我的MAC地址是:72:b8:9a:72:f3:4a
然后我们确认一下它的MAC是否相符。
按照之前的思路,需要对这个请求的回应进行监听,如果得到回应,那么证明目标在线,并打印输出这个机器的硬件地址。
data = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="192.168.43.1")
ans, unans = srp(data, timeout=3)
if ans:
ans.summary()
for s, r in ans:
print("目标MAC地址:" + str(r[Ether].src))
运行结果: 我们现在知道怎么使用了,我们就可以编写一个完整的探测主机存活脚本工具。
from scapy.all import *
ip = input("目标IP地址\范围:")
data = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip)
ans, unans = srp(data, timeout=3)
ans.summary()
if ans:
for s, r in ans:
ip = r[ARP].psrc
MAC = r[ARP].hwsrc
print("[*] 目标IP地址:%s" % ip)
print("[*] 目标MAC地址:%s" % MAC)
print("---------------")
else:
print("[!] 目标 %s 未在线!" % ip)
运行结果:
探测单个主机
探测多个主机
这些都是存活的主机,未存活的主机虽然没有列出来,但是也会发送数据包进行侦测。
探测不存在的主机
探测IP不存在的IP地址段
在这里还是有一点缺陷的,要是能够把它列出来就好了。