Tools: Build a "Military-Grade" Network Scanner in Python (Scapy Tutorial)

Tools: Build a "Military-Grade" Network Scanner in Python (Scapy Tutorial)

Source: Dev.to

Prerequisites ## 1. For Windows Users (Critical) ## 2. Install Scapy In the world of cybersecurity, information is power. Before an attack happens, there is a phase of observation—watching, listening, and mapping out the network. Imagine knowing exactly every device connected to your Wi-Fi right now. Your neighbor’s phone, your smart TV, or maybe even an intruder lurking on your network. Most people rely on pre-made tools like Nmap. But today, we are going to build our own. In this tutorial, we will write a Python script that reverse-engineers the ARP (Address Resolution Protocol) to discover devices on any network. Before we write code, we need to handle the "Layer 2" drivers. Python cannot speak directly to the network card without help. You must install Npcap. Important: During installation, check the box "Install Npcap in WinPcap API-compatible Mode". We will use scapy, a powerful packet manipulation library. Open your terminal/command prompt and run: The Code Construction We will build this in 3 phases using a single file: scanner.py. Phase 1: Handling Arguments We don't want to hard-code the IP. We want to pass it via the command line (e.g., -t 10.0.0.1/24). Phase 2: The Scanner Engine This is where the magic happens. We create an Ethernet frame destined for ff:ff:ff:ff:ff:ff (Broadcast), ensuring every device hears us. The Full Source Code Here is the complete script. Copy this into a file named scanner.py. Conclusion You just built a professional network reconnaissance tool in under 60 lines of code. This is the foundation of network security. If you enjoyed this, I have a full video breakdown explaining every single line of code below. 📺 Watch the Masterclass For more IT Masterclasses, subscribe to: @it_solutions_pro Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() # Smart Logic: If user forgets the IP, default to a test range if not options.target: print("[-] No target specified. Defaulting to 192.168.1.1/24 for demo.") return "192.168.1.1/24" return options.target Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() # Smart Logic: If user forgets the IP, default to a test range if not options.target: print("[-] No target specified. Defaulting to 192.168.1.1/24 for demo.") return "192.168.1.1/24" return options.target COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() # Smart Logic: If user forgets the IP, default to a test range if not options.target: print("[-] No target specified. Defaulting to 192.168.1.1/24 for demo.") return "192.168.1.1/24" return options.target COMMAND_BLOCK: def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) # 1. Create ARP Request arp_request = scapy.ARP(pdst=ip) # 2. Create Broadcast Frame broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") # 3. Combine Frame arp_request_broadcast = broadcast/arp_request # 4. Send & Receive # verbose=False keeps the terminal clean answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) # visual effect: print immediately print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Cinematic delay return clients_list Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) # 1. Create ARP Request arp_request = scapy.ARP(pdst=ip) # 2. Create Broadcast Frame broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") # 3. Combine Frame arp_request_broadcast = broadcast/arp_request # 4. Send & Receive # verbose=False keeps the terminal clean answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) # visual effect: print immediately print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Cinematic delay return clients_list COMMAND_BLOCK: def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) # 1. Create ARP Request arp_request = scapy.ARP(pdst=ip) # 2. Create Broadcast Frame broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") # 3. Combine Frame arp_request_broadcast = broadcast/arp_request # 4. Send & Receive # verbose=False keeps the terminal clean answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) # visual effect: print immediately print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Cinematic delay return clients_list COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() if not options.target: print("[-] Please specify a target IP range. Use --help for more info.") # Default to local range if not provided return "192.168.1.1/24" return options.target def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) arp_request = scapy.ARP(pdst=ip) broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") arp_request_broadcast = broadcast/arp_request # Send packet and wait for response answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Added delay for visual effect return clients_list def print_result(results_list): print("-" * 50) print(f"[+] Scan Complete. Found {len(results_list)} devices.") if __name__ == "__main__": target_ip = get_arguments() scan_result = scan(target_ip) print_result(scan_result) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() if not options.target: print("[-] Please specify a target IP range. Use --help for more info.") # Default to local range if not provided return "192.168.1.1/24" return options.target def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) arp_request = scapy.ARP(pdst=ip) broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") arp_request_broadcast = broadcast/arp_request # Send packet and wait for response answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Added delay for visual effect return clients_list def print_result(results_list): print("-" * 50) print(f"[+] Scan Complete. Found {len(results_list)} devices.") if __name__ == "__main__": target_ip = get_arguments() scan_result = scan(target_ip) print_result(scan_result) COMMAND_BLOCK: import scapy.all as scapy import argparse import time def get_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", dest="target", help="Target IP / IP range.") options = parser.parse_args() if not options.target: print("[-] Please specify a target IP range. Use --help for more info.") # Default to local range if not provided return "192.168.1.1/24" return options.target def scan(ip): print(f"\n[+] Starting Scan on {ip}...\n") print("-" * 50) print("IP Address\t\tMAC Address") print("-" * 50) arp_request = scapy.ARP(pdst=ip) broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") arp_request_broadcast = broadcast/arp_request # Send packet and wait for response answered_list = scapy.srp(arp_request_broadcast, timeout=1, verbose=False)[0] clients_list = [] for element in answered_list: client_dict = {"ip": element[1].psrc, "mac": element[1].hwsrc} clients_list.append(client_dict) print(f"{element[1].psrc}\t\t{element[1].hwsrc}") time.sleep(0.1) # Added delay for visual effect return clients_list def print_result(results_list): print("-" * 50) print(f"[+] Scan Complete. Found {len(results_list)} devices.") if __name__ == "__main__": target_ip = get_arguments() scan_result = scan(target_ip) print_result(scan_result) - Go to npcap.com. - Important: During installation, check the box "Install Npcap in WinPcap API-compatible Mode".