Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
sudo-arash authored Sep 15, 2024
1 parent e5e7392 commit 016f2e4
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 0 deletions.
142 changes: 142 additions & 0 deletions network_scanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import csv
import json
import socket
import os
import platform
import ipaddress
from scapy.all import ARP, Ether, srp, IP, TCP, sr1
from concurrent.futures import ThreadPoolExecutor
from utils import get_local_ip, get_user_input, colored_print
from colorama import Fore, Style
import argparse

def scan_ip(ip, timeout=1):
"""
Sends an ARP request to a specific IP and waits for a response.
:param ip: The IP address to scan.
:param timeout: Timeout for the ARP request.
:return: A dictionary containing the IP, MAC address, device name, and OS if available.
"""
if ':' in ip:
# Skip IPv6 addresses, ARP is for IPv4
return None

# Create ARP request for the given IP
arp_request = ARP(pdst=ip)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
packet = ether / arp_request

device_info = {'ip': ip}
try:
# Send the packet and wait for a response (timeout in seconds)
result = srp(packet, timeout=timeout, verbose=0)[0]
if result:
device_info['mac'] = result[0][1].hwsrc
device_info['name'] = get_device_name(ip)
device_info['os'] = get_os(ip)
return device_info
except Exception as e:
colored_print(f"Error scanning {ip}: {e}", "RED")
return None

def get_device_name(ip):
"""
Retrieves the device name using reverse DNS lookup.
:param ip: The IP address to lookup.
:return: Device name or 'Unknown' if not resolvable.
"""
try:
return socket.gethostbyaddr(ip)[0]
except socket.herror:
return 'Unknown'

def get_os(ip):
"""
Attempts to determine the operating system using TCP/IP stack analysis.
:param ip: The IP address to scan.
:return: OS guess based on TCP response.
"""
try:
pkt = IP(dst=ip)/TCP(dport=80, flags='S')
response = sr1(pkt, timeout=1, verbose=0)
if response:
if response.haslayer(TCP):
if response[TCP].flags == 0x12:
return 'Windows'
elif response[TCP].flags == 0x14:
return 'Linux/Unix'
return 'Unknown'
except Exception as e:
colored_print(f"Error detecting OS for {ip}: {e}", "RED")
return 'Unknown'

def scan_network_concurrently(ip_range, timeout=1, threads=100):
"""
Scans the network range concurrently for active clients using ARP requests.
:param ip_range: List of IP addresses to scan.
:param timeout: Timeout for each ARP request.
:param threads: Number of threads to use for scanning.
:return: List of active clients with IP, MAC, device name, and OS information.
"""
with ThreadPoolExecutor(max_workers=threads) as executor:
results = executor.map(lambda ip: scan_ip(ip) or None, ip_range) # TODO: Replace none with ipv6_scan and develop the function.
return [client for client in results if client is not None]

def export_results(results, file_format, filename):
"""
Export results to a file in the specified format (CSV or JSON).
:param results: List of dictionaries containing IP, MAC, device name, and OS information.
:param file_format: Format for exporting ('csv' or 'json').
:param filename: Output file name.
"""
if file_format == 'csv':
with open(filename, 'w', newline='') as file:
writer = csv.DictWriter(file, fieldnames=['ip', 'mac', 'name', 'os'])
writer.writeheader()
writer.writerows(results)
elif file_format == 'json':
with open(filename, 'w') as file:
json.dump(results, file, indent=4)
else:
colored_print(f"Unsupported file format: {file_format}", "RED")

if __name__ == "__main__":
# Argument parsing for customization
parser = argparse.ArgumentParser(description="Network Scanner")
parser.add_argument('--timeout', type=float, default=1, help="Timeout for ARP requests (default: 1s)")
parser.add_argument('--threads', type=int, default=100, help="Number of threads to use (default: 100)")
parser.add_argument('--range', type=str, help="Custom IP range (e.g., 192.168.1.0/24)")
parser.add_argument('--export', type=str, help="Export results to a file (csv or json) with the specified filename")

args = parser.parse_args()

# Get local IP to determine network range
local_ip = get_local_ip()

if not local_ip:
colored_print("Could not determine local IP address.", "RED")
else:
# Get IP range from user or use default
ip_range = get_user_input(args.range, local_ip)

# Show scan range
colored_print(f"Scanning network range: {ip_range[0].rsplit('.', 1)[0]}.0/24", "GREEN")

# Scan network concurrently
active_clients = scan_network_concurrently(ip_range, timeout=args.timeout, threads=args.threads)

# Display results
colored_print("\nActive clients on the network:", "YELLOW")
print(f"{Fore.CYAN}IP Address\t\tMAC Address\t\tDevice Name\t\tOS{Style.RESET_ALL}")
print("-" * 80)
for client in active_clients:
print(f"{Fore.GREEN}{client['ip']}\t\t{client['mac']}\t\t{client['name']}\t\t{client['os']}{Style.RESET_ALL}")

if not active_clients:
colored_print("No active clients found.", "RED")

# Export results if requested
if args.export:
file_format = args.export.split('.')[-1]
export_results(active_clients, file_format, args.export)
colored_print(f"Results exported to {args.export}", "GREEN")
82 changes: 82 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Network Scanner

A Python-based network scanner that uses ARP requests to discover active clients on a local network. This tool includes features for device name and operating system detection, with options to export results to CSV or JSON files and support for IPv6 addresses.

## Features
- **Customizable Network Range**: Scan a specific IP range or use the local IP to derive the default range.
- **Concurrency**: Utilizes Python's `ThreadPoolExecutor` for fast scanning with customizable thread count.
- **Timeout Control**: Adjustable ARP request timeout to balance speed and accuracy.
- **Export Results**: Save scan results to CSV or JSON files.
- **Device Name Detection**: Retrieve device names using reverse DNS lookup.
- **Operating System Detection**: Basic OS fingerprinting based on TCP/IP stack analysis.
- **IPv6 Support**: Placeholder for future IPv6 scanning functionality.
- **Colorful Output**: Clear, colorful output using the `colorama` library for easier result interpretation.
- **Modular Design**: Core functionalities split into multiple files for maintainability and scalability.

## Requirements
- Python 3.x
- `scapy` library
- `colorama` library

## Installation

1. Clone the repository:
```bash
git clone https://github.com/sudo-arash/network-scanner.git
cd network-scanner
```

2. Install required dependencies:
```bash
pip install scapy colorama
```

## Usage

Run the `network_scanner.py` with the following customizable options:

```bash
python network_scanner.py [--timeout TIMEOUT] [--threads THREADS] [--range RANGE] [--export FILE]
```

### Arguments:
- `--timeout`: (Optional) Set the timeout for ARP requests (in seconds). Default is `1`.
- `--threads`: (Optional) Number of threads to use for concurrent scanning. Default is `100`.
- `--range`: (Optional) Specify a custom IP range to scan (e.g., `192.168.1.0/24`). If not provided, the script uses the local network IP range.
- `--export`: (Optional) Export results to a file in CSV or JSON format (e.g., `results.csv` or `results.json`).

### Examples:

1. **Scan the default network range (based on local IP) with default settings**:
```bash
python network_scanner.py
```

2. **Scan a custom IP range with a 2-second timeout and 50 threads**:
```bash
python network_scanner.py --range 192.168.1.0/24 --timeout 2 --threads 50
```

3. **Export results to a CSV file**:
```bash
python network_scanner.py --export results.csv
```

## Project Structure

```
network-scanner/
├── network_scanner.py # Main script with network scanning functionality
├── utils.py # Utility functions for network scanning
├── README.md # Project documentation
```
## Future Enhancements
- Implement full IPv6 scanning functionality.
- Advanced network analysis features.
- GUI for easier configuration and result viewing.
## License
This project is licensed under the MIT License.
49 changes: 49 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import socket
from colorama import Fore, Style

def get_local_ip():
"""
Returns the local IP address of the machine.
"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
s.connect(('8.8.8.8', 1))
local_ip = s.getsockname()[0]
s.close()
return local_ip
except Exception as e:
colored_print(f"Error getting local IP: {e}", "RED")
return None

def get_user_input(custom_range, local_ip):
"""
Determines the IP range to scan.
:param custom_range: User input for custom IP range.
:param local_ip: Local IP to derive default range.
:return: List of IP addresses to scan.
"""
if custom_range:
# Parse custom range (assume /24 subnet)
base_ip = custom_range.rsplit('.', 1)[0]
return [f"{base_ip}.{i}" for i in range(1, 255)]
else:
# Use default range based on local IP
ip_parts = local_ip.split('.')
base_ip = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}."
return [f"{base_ip}{i}" for i in range(1, 255)]

def colored_print(message, color="WHITE"):
"""
Prints a message in the specified color.
:param message: The message to print.
:param color: The color for the message (options: RED, GREEN, YELLOW, CYAN, WHITE).
"""
color_mapping = {
"RED": Fore.RED,
"GREEN": Fore.GREEN,
"YELLOW": Fore.YELLOW,
"CYAN": Fore.CYAN,
"WHITE": Fore.WHITE
}
print(f"{color_mapping.get(color, Fore.WHITE)}{message}{Style.RESET_ALL}")

0 comments on commit 016f2e4

Please sign in to comment.