Python中使用Scapy能否获取局域网内主机名?如何实现?
解答
首先明确结论:仅靠你当前使用的ARP扫描逻辑无法通过scapy获取主机名,ARP协议的作用只是完成IP地址和MAC地址的映射,报文本身不携带任何主机名相关字段。要实现类似Nmap的主机名获取能力,你可以结合以下两种可通过scapy实现的方案补充逻辑:
可选实现方案
方案1:反向DNS(PTR记录)查询
对扫描到的IP发送DNS反向查询请求,目标域名格式为[反向IP段].in-addr.arpa,如果目标设备配置了PTR记录就能返回对应的主机名,该方案通用性较强,对支持DNS的设备都适用。
方案2:NetBIOS名称查询
对扫描到的IP的137端口发送NetBIOS名称查询请求,几乎所有Windows系统主机默认开启该服务,大量IoT、Linux设备也会开启相关支持,是局域网下获取主机名成功率很高的方案,适合没有搭建内部DNS的局域网场景。
修改后带主机名获取的完整代码示例
import scapy.all as scapy request = scapy.ARP() broadcast = scapy.Ether() broadcast.dst = 'ff:ff:ff:ff:ff:ff' available_networks = [] def get_hostname_by_ptr(ip): # 构造反向DNS查询包 reversed_ip = '.'.join(reversed(ip.split('.'))) dns_req = scapy.IP(dst="223.5.5.5")/scapy.UDP(dport=53)/scapy.DNS(rd=1, qd=scapy.DNSQR(qname=f"{reversed_ip}.in-addr.arpa", qtype="PTR")) try: resp = scapy.sr1(dns_req, timeout=1, verbose=0) if resp and resp.haslayer(scapy.DNS) and resp[scapy.DNS].ancount > 0: return resp[scapy.DNS].an.rdata.decode().strip('.') except: pass return None def get_hostname_by_netbios(ip): # 构造NetBIOS查询包 nbns_req = scapy.IP(dst=ip)/scapy.UDP(sport=137, dport=137)/scapy.NBNSQueryRequest(NAME_TRN_ID=12345, QUESTION_NAME="*") try: resp = scapy.sr1(nbns_req, timeout=1, verbose=0) if resp and resp.haslayer(scapy.NBNSQueryResponse): return resp[scapy.NBNSQueryResponse].NAME.decode().strip() except: pass return None def IP_Scan(net_area, net_mask): available_networks.clear() request.pdst = f'{net_area}/{net_mask}' request_broadcast = broadcast / request clients = scapy.srp(request_broadcast, timeout=5, verbose=0)[0] for sent_ip, received_ip in clients: ip = received_ip.psrc # 优先取NetBIOS结果,再取PTR结果,都拿不到返回未知 hostname = get_hostname_by_netbios(ip) or get_hostname_by_ptr(ip) or "未知" available_networks.append({'IP': ip, 'MAC': received_ip.hwsrc, '主机名': hostname}) return available_networks
注意事项
- 上述代码中反向DNS查询默认使用公共DNS,你可以替换为你当前局域网的网关DNS地址,提升PTR查询的成功率
- 部分开启了防火墙的设备会拦截NetBIOS或DNS查询请求,这种场景下无法获取到主机名,和Nmap的扫描表现一致
内容的提问来源于stack exchange,提问作者Saizzou
相关产品推荐
相关产品推荐

