Python基于Scapy实现内网ARP扫描的完整流程

苏见山
苏见山
发布时间:2026-10-15 | 2人浏览过
原创

本文详细介绍如何使用Python的Scapy模块进行内网ARP扫描。文章从构造单个ARP请求包开始,演示如何发送广播包并接收响应以获取目标IP对应的MAC地址。随后,通过引入队列机制和多进程处理,实现了高效的全网段IP存活检测与MAC地址采集,并提供了基于optparse参数解析的完整可执行代码示例。

前言

在前一篇文章中,我们探讨了利用Scapy实现Ping扫描的方法。本文将重点转向另一个常见场景:使用Scapy模块进行内网ARP扫描,以探测局域网内存活的主机及其硬件地址。

实现过程

在之前的文章中,我们已经介绍了如何通过Scapy伪造数据包。关于ARP包的构造细节在此不再赘述,这里主要梳理该工具的整体工作流程。

第一步,实现单个IP地址的ARP包发送与接收逻辑。

第二步,在此基础上实现多进程并发发送和接收多个ARP包,以提高扫描效率。

整体实现流程与之前的脚本逻辑较为相似,核心在于数据包的构造与响应处理。

单进程ARP包处理

数据包构造部分:


def scapy_arp_one(ip_address, queue=None):
 Packet = Ether(dst='FF:FF:FF:FF:FF:FF') / ARP(op=1, hwdst='00:00:00:00:00:00:', pdst=ip_address)

请求包的发送环节:


arp = srp(Packet, timeout=0.2, verbose=False)

返回包的接收处理:


try:
  reply_list = arp[0].res
  if queue is None:
   return reply_list[0][1].getlayer(ARP).fields['hwsrc']
  else:
   queue.out((ip_address, reply_list[0][1].getlayer(ARP).fields['hwsrc']))
 except:
  return

这里采用队列模式,主要目的是为后续多进程并发发送做准备,并妥善处理ARP包的接收逻辑。简要说明如下:当发送ARP广播包后,我们会收到一个返回包。理想情况下,应遵循“一个发送包对应一个接收包”的原则。因此,这里采用队列方式,逐个IP地址进行接收处理,并使用列表存储结果,过滤出ARP包中的硬件MAC地址信息。

完整代码示例:


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:Ameng, jlx-love.com

from scapy.all import *
import sys


def scapy_arp_one(ip_address, queue=None):
 Packet = Ether(dst='FF:FF:FF:FF:FF:FF') / ARP(op=1, hwdst='00:00:00:00:00:00:', pdst=ip_address)
 arp = srp(Packet, timeout=0.2, verbose=False)
 try:
  reply_list = arp[0].res
  if queue is None:
   return reply_list[0][1].getlayer(ARP).fields['hwsrc']
  else:
   queue.out((ip_address, reply_list[0][1].getlayer(ARP).fields['hwsrc']))
 except:
  return

if __name__ == '__main__':
 scapy_arp_one(sys.argv[1])

进一步完善与优化

既然已经掌握了使用ARP进行基本发包和收包的方法,接下来我们将编写一个能够扫描整个网段的ARP检测工具。

梳理思路:首先,需要将IP地址转换为网段内的所有IP列表,这可以借助ipaddress模块实现。其次,我们知道接收响应包是一对一的,因此可以从成对的响应包中提取所需的目标IP和源MAC地址。最后,优化代码并输出结果。在此版本中,我不再使用sys模块进行传参,而是采用一个新的模块optparse,具体使用方法可参考相关文档。

完整代码示例:


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:Ameng, jlx-love.com

import time
from scapy.all import *
import ipaddress
import optparse


def scapy_arp_scan(network, ifname):
 net = ipaddress.ip_network(network)
 ip_addr = []
 for ip in net:
  ip = str(ip)
  ip_addr.append(ip)
 time.sleep(1)
 Packet = Ether(dst='FF:FF:FF:FF:FF:FF')/ARP(op=1, hwdst='00:00:00:00:00:00', pdst=ip_addr)
 arp = srp(Packet, iface = ifname, timeout = 1, verbose = False)
 arp_list = arp[0].res
 IP_MAC_LIST = []
 for n in range(len(arp_list)):  
  IP = arp_list[n][1][1].fields['psrc']
  MAC = arp_list[n][1][1].fields['hwsrc']
  IP_MAC = [IP, MAC]
  IP_MAC_LIST.append(IP_MAC)
 return IP_MAC_LIST


if __name__ == '__main__':
 t1 = time.time()
 parser = optparse.OptionParser('用法:\n python3 scapy_arp_scan.py --network 扫描网段 --ifname 网卡名称')
 parser.add_option('--network', dest = 'network', type = 'string', help = '扫描网段')
 parser.add_option('--ifname', dest = 'ifname', type = 'string', help = '网卡名称')
 (options, args) = parser.parse_args()
 network = options.network
 ifname = options.ifname
 if network == None or ifname == None:
	 print(parser.usage)
 else:
  active_ip_mac = scapy_arp_scan(network, ifname)
  print('存活的IP地址及对应MAC:')
  for ip, mac in active_ip_mac:
   print(ip, mac)
 t2 = time.time()
 print('所用时间为:{}'.format(int(t2 - t1)))

运行结果展示

到此,关于Python使用Scapy模块实现ARP扫描的过程介绍完毕。更多相关Python实现ARP扫描的内容,请搜索本站以前的文章或继续浏览下面的相关文章。希望大家以后多多支持本站!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表