From ed270c20bbfc782bfba927ebf05bdf2db547b1c3 Mon Sep 17 00:00:00 2001 From: thomasciesla Date: Wed, 26 Nov 2025 10:00:45 +0100 Subject: [PATCH] ip-scanner.py aktualisiert --- ip-scanner.py | 168 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 152 insertions(+), 16 deletions(-) diff --git a/ip-scanner.py b/ip-scanner.py index 211c303..61d4a97 100644 --- a/ip-scanner.py +++ b/ip-scanner.py @@ -17,6 +17,9 @@ import time import shutil import re +# Globale Debug-Variable +DEBUG = False + # ANSI Farben class Color: RED = '\033[91m' @@ -30,6 +33,11 @@ class Color: UNDERLINE = '\033[4m' RESET = '\033[0m' +def debug_print(message: str): + """Gibt Debug-Nachrichten aus wenn DEBUG aktiviert ist""" + if DEBUG: + print(f"{Color.YELLOW}[DEBUG] {message}{Color.RESET}") + @dataclass class HostInfo: ip: str @@ -58,18 +66,53 @@ def print_banner(): def check_tool(tool: str) -> bool: """Prüft ob ein Tool verfügbar ist""" - return shutil.which(tool) is not None + is_available = shutil.which(tool) is not None + debug_print(f"Tool '{tool}' verfügbar: {is_available}") + return is_available def get_hostname(ip: str, timeout: float = 0.3) -> Optional[str]: """Versucht den Hostnamen einer IP-Adresse aufzulösen""" try: socket.setdefaulttimeout(timeout) hostname = socket.gethostbyaddr(ip)[0] - return hostname if hostname != ip else None - except (socket.herror, socket.gaierror, socket.timeout, OSError): + result = hostname if hostname != ip else None + debug_print(f"Hostname für {ip}: {result}") + return result + except (socket.herror, socket.gaierror, socket.timeout, OSError) as e: + debug_print(f"Hostname-Auflösung für {ip} fehlgeschlagen: {e}") return None -def arp_scan_network(network_str: str) -> Tuple[Dict[str, HostInfo], Set[str]]: +def get_network_interface(network_str: str) -> Optional[str]: + """Findet automatisch das passende Netzwerk-Interface für ein Netzwerk""" + try: + debug_print(f"Suche Interface für Netzwerk {network_str}") + network = ipaddress.ip_network(network_str, strict=False) + + # ip route get um das Interface zu finden + result = subprocess.run( + ['ip', 'route', 'get', str(list(network.hosts())[0])], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=5 + ) + + if result.returncode == 0: + # Parse die Ausgabe nach "dev " + match = re.search(r'dev\s+(\S+)', result.stdout) + if match: + interface = match.group(1) + debug_print(f"Gefundenes Interface: {interface}") + return interface + + debug_print(f"Kein Interface gefunden via ip route") + return None + + except Exception as e: + debug_print(f"Fehler bei Interface-Erkennung: {e}") + return None + +def arp_scan_network(network_str: str, interface: Optional[str] = None) -> Tuple[Dict[str, HostInfo], Set[str]]: """ Scannt Netzwerk mit arp-scan (Layer 2 - sehr zuverlässig) Benötigt root/sudo! @@ -77,8 +120,24 @@ def arp_scan_network(network_str: str) -> Tuple[Dict[str, HostInfo], Set[str]]: try: network = ipaddress.ip_network(network_str, strict=False) - # arp-scan mit optimalen Parametern - cmd = ['arp-scan', '--localnet', '--numeric', '--quiet', '--ignoredups', str(network)] + # Interface bestimmen + if interface: + debug_print(f"Verwende manuell angegebenes Interface: {interface}") + selected_interface = interface + else: + debug_print(f"Automatische Interface-Erkennung für {network_str}") + selected_interface = get_network_interface(network_str) + if not selected_interface: + debug_print("Keine automatische Interface-Erkennung möglich, verwende --localnet") + selected_interface = None + + # arp-scan Kommando aufbauen + if selected_interface: + cmd = ['arp-scan', '--interface', selected_interface, '--numeric', '--quiet', '--ignoredups', str(network)] + debug_print(f"ARP-Scan Befehl: {' '.join(cmd)}") + else: + cmd = ['arp-scan', '--localnet', '--numeric', '--quiet', '--ignoredups', str(network)] + debug_print(f"ARP-Scan Befehl (localnet): {' '.join(cmd)}") result = subprocess.run( cmd, @@ -88,11 +147,18 @@ def arp_scan_network(network_str: str) -> Tuple[Dict[str, HostInfo], Set[str]]: text=True ) + if result.returncode != 0: + debug_print(f"ARP-Scan stderr: {result.stderr}") + debug_print(f"ARP-Scan returncode: {result.returncode}") + + debug_print(f"ARP-Scan stdout:\n{result.stdout}") + used_hosts = {} all_ips = set(str(ip) for ip in network.hosts()) # Parse arp-scan output # Format: IPMACVendor + lines_parsed = 0 for line in result.stdout.split('\n'): line = line.strip() if not line or line.startswith('#'): @@ -110,13 +176,19 @@ def arp_scan_network(network_str: str) -> Tuple[Dict[str, HostInfo], Set[str]]: if str(ip_obj) in all_ips: hostname = get_hostname(ip) used_hosts[ip] = HostInfo(ip=ip, hostname=hostname, mac=mac, method="arp-scan") - except ValueError: + lines_parsed += 1 + debug_print(f"Gefunden: {ip} ({mac})") + except ValueError as e: + debug_print(f"Ungültige IP in arp-scan Ausgabe: {ip} - {e}") continue + debug_print(f"ARP-Scan: {lines_parsed} Zeilen geparst, {len(used_hosts)} Hosts gefunden") + free_ips = all_ips - set(used_hosts.keys()) return used_hosts, free_ips except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.CalledProcessError, Exception) as e: + debug_print(f"ARP-Scan Exception: {type(e).__name__}: {e}") print(f" {Color.YELLOW}⚠ ARP-Scan Fehler: {e}{Color.RESET}") return None, None @@ -154,6 +226,8 @@ def nmap_aggressive_scan(network_str: str, scan_ports: bool = False) -> Tuple[Di str(network) ] + debug_print(f"Nmap Befehl: {' '.join(cmd)}") + result = subprocess.run( cmd, stdout=subprocess.PIPE, @@ -162,28 +236,40 @@ def nmap_aggressive_scan(network_str: str, scan_ports: bool = False) -> Tuple[Di text=True ) + if result.returncode != 0: + debug_print(f"Nmap stderr: {result.stderr}") + + debug_print(f"Nmap stdout (erste 500 Zeichen):\n{result.stdout[:500]}") + used_hosts = {} all_ips = set(str(ip) for ip in network.hosts()) # Parse nmap output current_ip = None + hosts_found = 0 for line in result.stdout.split('\n'): # Suche nach "Nmap scan report for" if 'Nmap scan report for' in line: match = re.search(r'(\d+\.\d+\.\d+\.\d+)', line) if match: current_ip = match.group(1) + debug_print(f"Nmap: Gefunden IP {current_ip}") # Prüfe ob Host up ist if current_ip and ('Host is up' in line or 'open' in line): if current_ip in all_ips and current_ip not in used_hosts: hostname = get_hostname(current_ip) used_hosts[current_ip] = HostInfo(ip=current_ip, hostname=hostname, method="nmap") + hosts_found += 1 + debug_print(f"Nmap: Host {current_ip} ist up") + + debug_print(f"Nmap: {hosts_found} Hosts gefunden") free_ips = all_ips - set(used_hosts.keys()) return used_hosts, free_ips except (subprocess.TimeoutExpired, FileNotFoundError, Exception) as e: + debug_print(f"Nmap Exception: {type(e).__name__}: {e}") print(f" {Color.YELLOW}⚠ Nmap Fehler: {e}{Color.RESET}") return None, None @@ -196,8 +282,10 @@ def check_tcp_ports(ip: str, ports: List[int]) -> bool: result = sock.connect_ex((ip, port)) sock.close() if result == 0: + debug_print(f"TCP Port {port} auf {ip} ist offen") return True - except: + except Exception as e: + debug_print(f"TCP Port Check Fehler {ip}:{port}: {e}") continue return False @@ -210,8 +298,12 @@ def ping_check(ip: str) -> bool: stderr=subprocess.PIPE, timeout=2 ) - return result.returncode == 0 - except: + success = result.returncode == 0 + if success: + debug_print(f"Ping erfolgreich: {ip}") + return success + except Exception as e: + debug_print(f"Ping Fehler {ip}: {e}") return False def comprehensive_check(ip: str, tcp_ports: List[int]) -> bool: @@ -221,7 +313,8 @@ def comprehensive_check(ip: str, tcp_ports: List[int]) -> bool: return check_tcp_ports(ip, tcp_ports) def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100, - tcp_ports: List[int] = None, aggressive: bool = False) -> ScanResult: + tcp_ports: List[int] = None, aggressive: bool = False, + interface: Optional[str] = None) -> ScanResult: """Scannt ein Netzwerk nach aktiven IPs""" if tcp_ports is None: @@ -239,6 +332,9 @@ def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100, print(f"\n{Color.YELLOW}► Scanne Netzwerk: {Color.BOLD}{network_str}{Color.RESET}") print(f" {Color.WHITE}IPs zu scannen: {total_ips}{Color.RESET}") + if interface: + print(f" {Color.WHITE}Interface: {interface}{Color.RESET}") + start_time = time.time() used_hosts = {} free_ips = set() @@ -248,20 +344,24 @@ def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100, if method == 'auto': if check_tool('arp-scan'): scan_method = 'arp-scan' + debug_print("Auto-Methode: Verwende arp-scan") elif check_tool('nmap'): scan_method = 'nmap' + debug_print("Auto-Methode: Verwende nmap") else: scan_method = 'comprehensive' + debug_print("Auto-Methode: Verwende comprehensive") print(f" {Color.CYAN}Methode: {scan_method}{Color.RESET}") # ARP-Scan (beste Option) if scan_method == 'arp-scan': print(f" {Color.CYAN}Führe ARP-Scan durch...{Color.RESET}") - used_hosts, free_ips = arp_scan_network(network_str) + used_hosts, free_ips = arp_scan_network(network_str, interface=interface) if used_hosts is None or len(used_hosts) == 0: print(f" {Color.YELLOW}⚠ ARP-Scan lieferte keine Ergebnisse, fallback zu nmap{Color.RESET}") + debug_print("ARP-Scan Fallback zu nmap") scan_method = 'nmap' # Nmap-Scan @@ -271,6 +371,7 @@ def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100, if used_hosts is None: print(f" {Color.YELLOW}⚠ Nmap fehlgeschlagen, fallback zu comprehensive{Color.RESET}") + debug_print("Nmap Fallback zu comprehensive") scan_method = 'comprehensive' # Comprehensive Scan (Fallback) @@ -311,6 +412,8 @@ def scan_network(network_str: str, method: str = 'auto', max_workers: int = 100, scan_time = time.time() - start_time + debug_print(f"Scan abgeschlossen: {len(used_hosts)} belegt, {len(free_ips)} frei, Dauer: {scan_time:.2f}s") + return ScanResult( network=network_str, used_hosts=used_hosts, @@ -465,6 +568,8 @@ def print_common_free_hosts(results: List[ScanResult], show_all: bool = False): print() def main(): + global DEBUG + parser = argparse.ArgumentParser( description='Scannt Netzwerke nach belegten und freien IP-Adressen (100%% zuverlässig)', formatter_class=argparse.RawDescriptionHelpFormatter, @@ -481,8 +586,9 @@ Wichtig für maximale Zuverlässigkeit: Beispiele: sudo %(prog)s 10.150.8.0/24 10.150.56.0/24 sudo %(prog)s 192.168.0.0/24 --method arp-scan -a + sudo %(prog)s 10.0.0.0/24 --method arp-scan --interface vmbr0 %(prog)s 10.0.0.0/24 --aggressive - %(prog)s 192.168.1.0/24 --no-free + %(prog)s 192.168.1.0/24 --no-free --debug """ ) @@ -531,11 +637,30 @@ Beispiele: help='Zeige keine freien IPs' ) + parser.add_argument( + '-i', '--interface', + type=str, + default=None, + help='Netzwerk-Interface für ARP-Scan (z.B. eth0, vmbr0, enp67s0)' + ) + + parser.add_argument( + '--debug', + action='store_true', + help='Aktiviert Debug-Modus mit detaillierter Ausgabe' + ) + args = parser.parse_args() + # Debug-Modus aktivieren + if args.debug: + DEBUG = True + debug_print("Debug-Modus aktiviert") + # Parse TCP-Ports try: tcp_ports = [int(p.strip()) for p in args.ports.split(',')] + debug_print(f"TCP-Ports: {tcp_ports}") except ValueError: print(f"{Color.RED}✗ Ungültiges Port-Format{Color.RESET}") return @@ -543,8 +668,14 @@ Beispiele: print_banner() # Warnung wenn nicht als root - if args.method == 'arp-scan' and subprocess.run(['id', '-u'], capture_output=True, text=True).stdout.strip() != '0': - print(f"{Color.RED}⚠ WARNUNG: arp-scan benötigt root-Rechte! Führe mit 'sudo' aus.{Color.RESET}\n") + if args.method == 'arp-scan': + try: + uid_result = subprocess.run(['id', '-u'], capture_output=True, text=True) + if uid_result.stdout.strip() != '0': + print(f"{Color.RED}⚠ WARNUNG: arp-scan benötigt root-Rechte! Führe mit 'sudo' aus.{Color.RESET}\n") + debug_print("Nicht als root ausgeführt") + except: + pass # Verfügbare Tools anzeigen tools_available = [] @@ -556,6 +687,10 @@ Beispiele: print(f"{Color.CYAN}Verfügbare Scan-Methoden: {', '.join(tools_available)}{Color.RESET}\n") + if args.interface: + print(f"{Color.CYAN}Manuelles Interface: {args.interface}{Color.RESET}\n") + debug_print(f"Verwende manuell angegebenes Interface: {args.interface}") + results = [] total_start = time.time() @@ -565,7 +700,8 @@ Beispiele: method=args.method, max_workers=args.threads, tcp_ports=tcp_ports, - aggressive=args.aggressive + aggressive=args.aggressive, + interface=args.interface ) if result: results.append(result)