import os
import subprocess
import socket
from scapy.all import srp, Ether, ARP, conf
import threading
mac_name_mapping = {
"00:e0:4c:29:e2:5b": "我的台式机711",
"08:5c:1b:7c:5b:fe": "cmcc.wifi",
"2e:9f:77:55:8f:28": "小米13",
"6e:3e:07:54:b5:b3": "红米note11",
"48:2c:a0:49:86:29": "mi8 se",
"90:78:b2:50:fa:46": "红米k20",
"9c:9c:1f:40:d5:ed":"充电插座",
"3c:61:05:d4:5c:15": "院子灯",
"0a:e0:af:c6:15:e1": "nas",
"02:aa:26:ce:e9:69":"红米note11_2",
"d8:f1:5b:07:a0:a8": "风扇",
"a4:4b:d5:dd:95:cc": "redmi note 8",
"ea:fe:60:ff:4e:d1": "redmi note 9",
"e0:1f:88:e8:aa:48": "redmi 8a",
# 继续添加其他设备的 MAC 地址和主机名
}
def ping_device(ip):
"""
Ping 设备,检查是否在线
"""
# print(ip)
try:
output = subprocess.check_output(["ping", "-c", "1", ip], universal_newlines=True)
# print(output)
if "TTL" in output:
return True
return False
except Exception as e:
# print(e)
return False
def get_mac_address(ip):
"""
使用 ARP 获取 MAC 地址
"""
conf.verb = 0 # 禁用冗长的输出
ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip), timeout=2, retry=2)
for _, rcv in ans:
return rcv[Ether].src
return None
def get_device_name(ip):
"""
通过 IP 地址获取设备名
"""
try:
device_name = socket.gethostbyaddr(ip)[0]
except socket.herror:
device_name = None
return device_name
def scan_ip(ip, results):
"""
扫描单个 IP,获取设备的 IP、MAC 地址和名称
"""
# print(ip)
if ping_device(ip):
mac_address = get_mac_address(ip)
device_name = get_device_name(ip)
if not device_name:
if mac_address in mac_name_mapping:
device_name = mac_name_mapping[mac_address]
results.append({"ip": ip, "mac": mac_address, "name": device_name})
def scan_network(network_range):
"""
使用多线程扫描指定网段
"""
threads = []
results = []
# print("1")
for i in range(1, 255):
ip = f"{network_range}.{i}"
thread = threading.Thread(target=scan_ip, args=(ip, results))
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
return results
if __name__ == "__main__":
print("开始用管理权限运行")
network_range = "192.168.31" # 根据实际情况调整
devices = scan_network(network_range)
for device in devices:
print(f"IP: {device['ip']}, MAC: {device['mac']}, Name: {device['name']}")
大纲
-
引言
- 介绍局域网设备扫描的用途(例如,网络管理、设备监控)。
- 提及 Python 的强大功能和可扩展性。
-
环境准备
- 确保安装 Python 和相关库(如
scapy)。 - 提及必要的系统权限(如管理员权限)以便执行网络扫描。
- 安装 Npcap(WinPcap 的现代替代者,兼容性更好,推荐下载1.8.0的,最新版获取mac有问题)https://npcap.com/dist/
- 确保安装 Python 和相关库(如
-
代码分析
- 逐段分析代码,解释其功能和实现逻辑。
代码详细分析
import os
import subprocess
import socket
from scapy.all import srp, Ether, ARP, conf
import threading
- 导入必要的库:
os和subprocess用于执行系统命令(如ping)。socket用于进行网络编程,获取设备名称。scapy是一个强大的网络包处理库,用于发送和接收网络数据包。threading用于实现多线程扫描。
设备 MAC 地址到主机名的映射
mac_name_mapping = {
"00:e0:4c:29:e2:5b": "我的台式机711",
"08:5c:1b:7c:5b:fe": "cmcc.wifi",
"14:49:d4:af:93:3f": "小米13",
"6e:3e:07:54:b5:b3": "红米note11",
"48:2c:a0:49:86:29": "mi8 se",
"90:78:b2:50:fa:46": "红米k20",
"9c:9c:1f:40:d5:ed":"充电插座"
}
- 映射字典:维护一个 MAC 地址和主机名的映射字典。这样可以在 DNS 查询失败时提供更直观的设备名称。
设备在线检查
def ping_device(ip):
"""
Ping 设备,检查是否在线
"""
try:
output = subprocess.check_output(["ping", "-c", "1", ip], universal_newlines=True)
if "TTL" in output:
return True
return False
except subprocess.CalledProcessError:
return False
- 功能:通过
ping命令检查设备是否在线。如果设备响应,则返回True,否则返回False。 - 改进点:可以根据需求调整
ping的参数,例如增加超时时间。
获取 MAC 地址
def get_mac_address(ip):
"""
使用 ARP 获取 MAC 地址
"""
conf.verb = 0 # 禁用冗长的输出
ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip), timeout=2, retry=2)
for _, rcv in ans:
return rcv[Ether].src
return None
- 功能:使用 ARP 请求获取给定 IP 地址的 MAC 地址。该函数通过广播 ARP 请求到网络中,然后解析响应以提取 MAC 地址。
获取设备名称
def get_device_name(ip):
"""
通过 IP 地址获取设备名
"""
try:
device_name = socket.gethostbyaddr(ip)[0]
except socket.herror:
device_name = None
return device_name
- 功能:通过反向 DNS 查找获取设备名称。如果查找失败,则返回
None。
扫描单个 IP 地址
def scan_ip(ip, results):
"""
扫描单个 IP,获取设备的 IP、MAC 地址和名称
"""
if ping_device(ip):
mac_address = get_mac_address(ip)
device_name = get_device_name(ip)
if not device_name:
if mac_address in mac_name_mapping:
device_name = mac_name_mapping[mac_address]
results.append({"ip": ip, "mac": mac_address, "name": device_name})
- 功能:对于每个 IP 地址,首先检查其是否在线。如果在线,则获取其 MAC 地址和设备名称。如果设备名称为
None,则从映射字典中查找。
多线程扫描网络
def scan_network(network_range):
"""
使用多线程扫描指定网段
"""
threads = []
results = []
for i in range(1, 255):
ip = f"{network_range}.{i}"
thread = threading.Thread(target=scan_ip, args=(ip, results))
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
return results
- 功能:使用多线程扫描整个子网,从
192.168.31.1到192.168.31.254。这显著提高了扫描速度。
主程序
if __name__ == "__main__":
network_range = "192.168.31" # 根据实际情况调整
devices = scan_network(network_range)
for device in devices:
print(f"IP: {device['ip']}, MAC: {device['mac']}, Name: {device['name']}")
- 功能:定义要扫描的网络范围,调用扫描函数并输出结果。
结论
- 该 Python 脚本可以有效地扫描局域网内的设备,获取其 IP、MAC 地址及设备名称。
- 通过维护 MAC 地址与设备名称的映射,可以更方便地识别设备。
文章结尾
- 讨论脚本的可扩展性和潜在的改进(如添加更多的设备识别功能、使用更高级的库等)。
- 鼓励读者尝试自己修改和扩展代码,适应不同的网络环境。
通过这种结构和内容,你可以撰写一篇详尽的文章,帮助他人理解和使用这个 Python 网络扫描脚本。
评论区(0 条)
发表评论⏳ 加载编辑器…