macspoofer 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- macspoofer/__init__.py +3 -0
- macspoofer/cli.py +36 -0
- macspoofer/modules/__init__.py +0 -0
- macspoofer/modules/args_parser.py +71 -0
- macspoofer/modules/error_config.py +18 -0
- macspoofer/modules/interface.py +100 -0
- macspoofer/spoofer.py +140 -0
- macspoofer/utils/__init__.py +0 -0
- macspoofer/utils/exceptions.py +34 -0
- macspoofer/utils/random_utils.py +66 -0
- macspoofer/utils/shell_utils.py +46 -0
- macspoofer/utils/vendors.py +408 -0
- macspoofer-1.0.0.dist-info/METADATA +139 -0
- macspoofer-1.0.0.dist-info/RECORD +17 -0
- macspoofer-1.0.0.dist-info/WHEEL +4 -0
- macspoofer-1.0.0.dist-info/entry_points.txt +2 -0
- macspoofer-1.0.0.dist-info/licenses/LICENSE +21 -0
macspoofer/__init__.py
ADDED
macspoofer/cli.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""MAC Address Spoofer - CLI entry point."""
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
from rich import print
|
|
7
|
+
|
|
8
|
+
from macspoofer.modules.args_parser import ArgumentParser
|
|
9
|
+
from macspoofer.modules.error_config import configure_pretty_errors
|
|
10
|
+
from macspoofer.spoofer import run_spoofer_logic
|
|
11
|
+
from macspoofer.utils.exceptions import CustomException
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def _async_main() -> None:
|
|
15
|
+
"""Async application entry point."""
|
|
16
|
+
try:
|
|
17
|
+
configure_pretty_errors()
|
|
18
|
+
args = ArgumentParser().parse_args()
|
|
19
|
+
await run_spoofer_logic(args)
|
|
20
|
+
except KeyboardInterrupt:
|
|
21
|
+
print("\n[-] [bold red]Stopped.")
|
|
22
|
+
except CustomException as e:
|
|
23
|
+
print(f"\n[-] [bold red]{e}")
|
|
24
|
+
except ModuleNotFoundError:
|
|
25
|
+
print("\n[-] [bold red]Missing one of the pip packages.")
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"\n[-] [bold red]Error occurred: {e}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main() -> None:
|
|
31
|
+
"""Synchronous wrapper for the CLI entry point (used by pyproject.toml scripts)."""
|
|
32
|
+
asyncio.run(_async_main())
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Command-line argument parsing for MAC Address Spoofer."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
VERSION = "0.0.0"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class SpooferArgs:
|
|
11
|
+
"""Parsed command-line arguments for the spoofer."""
|
|
12
|
+
|
|
13
|
+
interface: str
|
|
14
|
+
auto: bool = False
|
|
15
|
+
ci: bool = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ArgumentParser:
|
|
19
|
+
"""Handles command-line argument parsing."""
|
|
20
|
+
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self._parser = self._create_parser()
|
|
23
|
+
|
|
24
|
+
def _create_parser(self) -> argparse.ArgumentParser:
|
|
25
|
+
"""Create and configure the argument parser."""
|
|
26
|
+
parser = argparse.ArgumentParser(
|
|
27
|
+
description="MAC Address Spoofer - Change your network interface MAC address",
|
|
28
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
29
|
+
)
|
|
30
|
+
self._add_arguments(parser)
|
|
31
|
+
return parser
|
|
32
|
+
|
|
33
|
+
def _add_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
34
|
+
"""Add all command-line arguments to the parser."""
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"-i",
|
|
37
|
+
dest="interface",
|
|
38
|
+
required=True,
|
|
39
|
+
help="Network interface name (e.g., wlan0, eth0)",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--auto",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="Non-interactive mode: generate and apply a safe random unicast MAC",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--ci",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="CI mode: for automated testing (similar to --auto)",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--version",
|
|
56
|
+
action="version",
|
|
57
|
+
version=f"MAC Address Spoofer v{VERSION}",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def parse_args(self) -> SpooferArgs:
|
|
61
|
+
"""Parse command-line arguments.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
SpooferArgs dataclass with parsed values
|
|
65
|
+
"""
|
|
66
|
+
args = self._parser.parse_args()
|
|
67
|
+
return SpooferArgs(
|
|
68
|
+
interface=args.interface,
|
|
69
|
+
auto=args.auto,
|
|
70
|
+
ci=args.ci,
|
|
71
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Pretty error output configuration."""
|
|
2
|
+
|
|
3
|
+
import pretty_errors
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def configure_pretty_errors() -> None:
|
|
7
|
+
"""Configure pretty_errors for enhanced error display.
|
|
8
|
+
|
|
9
|
+
Sets up formatting options for better error readability including
|
|
10
|
+
separator characters, full filename display, and context lines.
|
|
11
|
+
"""
|
|
12
|
+
pretty_errors.configure(
|
|
13
|
+
separator_character="*",
|
|
14
|
+
filename_display=pretty_errors.FILENAME_FULL,
|
|
15
|
+
line_color=pretty_errors.RED + "> " + pretty_errors.default_config.line_color,
|
|
16
|
+
lines_before=3,
|
|
17
|
+
lines_after=3,
|
|
18
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Network interface management for MAC spoofing."""
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
from collections.abc import AsyncIterator
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
|
|
7
|
+
from macspoofer.utils import shell_utils
|
|
8
|
+
from macspoofer.utils.exceptions import CustomException, ErrorCode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterfaceState(StrEnum):
|
|
12
|
+
"""Network interface state values."""
|
|
13
|
+
|
|
14
|
+
UP = "up"
|
|
15
|
+
DOWN = "down"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NetworkInterface:
|
|
19
|
+
"""Represents a network interface for MAC address manipulation.
|
|
20
|
+
|
|
21
|
+
This class encapsulates operations on a network interface,
|
|
22
|
+
including state management and MAC address spoofing.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
name: The interface name (e.g., 'eth0', 'wlan0')
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, name: str) -> None:
|
|
29
|
+
"""Initialize a NetworkInterface.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name: The interface name (e.g., 'eth0', 'wlan0')
|
|
33
|
+
"""
|
|
34
|
+
self.name = name
|
|
35
|
+
|
|
36
|
+
async def set_state(self, state: InterfaceState) -> None:
|
|
37
|
+
"""Set the interface to the specified state.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
state: Desired interface state (UP or DOWN)
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
CustomException: If the state change fails
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
await shell_utils.execute_command(["ip", "link", "set", "dev", self.name, state])
|
|
47
|
+
except CustomException as err:
|
|
48
|
+
raise CustomException(
|
|
49
|
+
message=f"Failed to set interface {self.name} to {state}",
|
|
50
|
+
error_code=ErrorCode.INTERFACE_STATE_FAILED,
|
|
51
|
+
) from err
|
|
52
|
+
|
|
53
|
+
async def up(self) -> None:
|
|
54
|
+
"""Bring the interface up."""
|
|
55
|
+
await self.set_state(InterfaceState.UP)
|
|
56
|
+
|
|
57
|
+
async def down(self) -> None:
|
|
58
|
+
"""Bring the interface down."""
|
|
59
|
+
await self.set_state(InterfaceState.DOWN)
|
|
60
|
+
|
|
61
|
+
async def set_mac_address(self, mac: str) -> None:
|
|
62
|
+
"""Set the MAC address of the interface.
|
|
63
|
+
|
|
64
|
+
Note: The interface should be down before changing the MAC address.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
mac: New MAC address (format: 'xx:xx:xx:xx:xx:xx')
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
CustomException: If setting the MAC address fails
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
await shell_utils.execute_command(
|
|
74
|
+
["ip", "link", "set", "dev", self.name, "address", mac]
|
|
75
|
+
)
|
|
76
|
+
except CustomException as err:
|
|
77
|
+
raise CustomException(
|
|
78
|
+
message=f"Failed to set MAC address {mac} on {self.name}",
|
|
79
|
+
error_code=ErrorCode.MAC_SPOOF_FAILED,
|
|
80
|
+
) from err
|
|
81
|
+
|
|
82
|
+
@contextlib.asynccontextmanager
|
|
83
|
+
async def disable_temporarily(self) -> AsyncIterator[None]:
|
|
84
|
+
"""Async context manager to temporarily bring the interface down and back up.
|
|
85
|
+
|
|
86
|
+
Usage:
|
|
87
|
+
async with interface.disable_temporarily():
|
|
88
|
+
await interface.set_mac_address(new_mac)
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
await self.down()
|
|
92
|
+
yield
|
|
93
|
+
finally:
|
|
94
|
+
await self.up()
|
|
95
|
+
|
|
96
|
+
def __str__(self) -> str:
|
|
97
|
+
return self.name
|
|
98
|
+
|
|
99
|
+
def __repr__(self) -> str:
|
|
100
|
+
return f"NetworkInterface({self.name!r})"
|
macspoofer/spoofer.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""MAC Address Spoofer - Core spoofing logic and TUI."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
import art
|
|
6
|
+
from rich import print
|
|
7
|
+
|
|
8
|
+
from macspoofer.modules.args_parser import SpooferArgs
|
|
9
|
+
from macspoofer.modules.interface import NetworkInterface
|
|
10
|
+
from macspoofer.utils import shell_utils
|
|
11
|
+
from macspoofer.utils.exceptions import CustomException, ErrorCode
|
|
12
|
+
from macspoofer.utils.random_utils import (
|
|
13
|
+
HexValuesLength,
|
|
14
|
+
generate_hex_values_delimited_by_dotted,
|
|
15
|
+
generate_safe_unicast_mac,
|
|
16
|
+
get_random_vendor_from_list,
|
|
17
|
+
)
|
|
18
|
+
from macspoofer.utils.vendors import VendorRegistry
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def spoof_mac_address(
|
|
22
|
+
interface: NetworkInterface,
|
|
23
|
+
mac: str,
|
|
24
|
+
require_confirmation: bool = True,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Spoof the MAC address of a network interface.
|
|
27
|
+
|
|
28
|
+
Temporarily brings the interface down via an async context manager,
|
|
29
|
+
changes its MAC address, then brings it back up.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
interface: NetworkInterface instance to spoof
|
|
33
|
+
mac: New MAC address to assign (format: 'xx:xx:xx:xx:xx:xx')
|
|
34
|
+
require_confirmation: If True, wait for user confirmation before proceeding
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
CustomException: If any step of the spoofing process fails
|
|
38
|
+
"""
|
|
39
|
+
print(f"[+] [bold yellow]We need to temporarily turn OFF interface: {interface}")
|
|
40
|
+
|
|
41
|
+
if require_confirmation:
|
|
42
|
+
input("Press Enter to continue or Ctrl+C to terminate -> ")
|
|
43
|
+
|
|
44
|
+
async with interface.disable_temporarily():
|
|
45
|
+
print(f"\n[+] [bold green]Turning {interface} OFF...")
|
|
46
|
+
await asyncio.sleep(1)
|
|
47
|
+
|
|
48
|
+
print(f"\n[+] [bold green]Spoofing {interface} mac...")
|
|
49
|
+
await asyncio.sleep(1)
|
|
50
|
+
await interface.set_mac_address(mac)
|
|
51
|
+
|
|
52
|
+
print(f"\n[+] [bold green]Turning {interface} back ON...")
|
|
53
|
+
await asyncio.sleep(1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def choose_vendor() -> str:
|
|
57
|
+
"""Display vendor options and get user selection.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The selected vendor name
|
|
61
|
+
"""
|
|
62
|
+
vendors = VendorRegistry.NAMES
|
|
63
|
+
options = "\n".join(f"[bold green][{i}] [cyan]{vendor}" for i, vendor in enumerate(vendors))
|
|
64
|
+
print(f"[bold magenta]Enter your choice:\n\n{options}\n")
|
|
65
|
+
|
|
66
|
+
user_input = input("-> ").strip()
|
|
67
|
+
|
|
68
|
+
while not user_input.isdigit() or int(user_input) >= len(vendors):
|
|
69
|
+
user_input = input("Invalid choice, try again-> ").strip()
|
|
70
|
+
|
|
71
|
+
return vendors[int(user_input)]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def generate_mac_for_vendor(vendor: str) -> str:
|
|
75
|
+
"""Generate a MAC address for the specified vendor.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
vendor: Vendor name (must be in VendorRegistry.NAMES)
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
A valid MAC address string
|
|
82
|
+
"""
|
|
83
|
+
if vendor == VendorRegistry.NAMES[0]:
|
|
84
|
+
return generate_safe_unicast_mac()
|
|
85
|
+
|
|
86
|
+
vendor_oui_list = VendorRegistry.get_ouis_for_vendor(vendor)
|
|
87
|
+
if vendor_oui_list is None:
|
|
88
|
+
return generate_safe_unicast_mac()
|
|
89
|
+
|
|
90
|
+
oui = get_random_vendor_from_list(vendor_oui_list)
|
|
91
|
+
nic = generate_hex_values_delimited_by_dotted(HexValuesLength.NIC)
|
|
92
|
+
return f"{oui}:{nic}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def run_tui(interface: NetworkInterface) -> None:
|
|
96
|
+
"""Run the text-based user interface for MAC spoofing.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
interface: NetworkInterface instance to spoof
|
|
100
|
+
"""
|
|
101
|
+
art.tprint("Spoofer")
|
|
102
|
+
|
|
103
|
+
vendor = choose_vendor()
|
|
104
|
+
print("[+] [bold green]Generating random mac according to your request...\n")
|
|
105
|
+
await asyncio.sleep(1)
|
|
106
|
+
|
|
107
|
+
mac = generate_mac_for_vendor(vendor)
|
|
108
|
+
|
|
109
|
+
print(f"[+] [bold green]Spoofing your interface {interface} mac to {mac}\n")
|
|
110
|
+
await asyncio.sleep(1)
|
|
111
|
+
|
|
112
|
+
await spoof_mac_address(interface, mac)
|
|
113
|
+
print("\n[+] [bold green]Done.")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def run_spoofer_logic(args: SpooferArgs) -> None:
|
|
117
|
+
"""Main entry point for the spoofer logic.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
args: Parsed command-line arguments
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
CustomException: If not running as root
|
|
124
|
+
"""
|
|
125
|
+
if not shell_utils.check_for_admin():
|
|
126
|
+
raise CustomException(
|
|
127
|
+
message="This tool must be run as root (sudo)",
|
|
128
|
+
error_code=ErrorCode.COMMAND_EXECUTION_FAILED,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
interface = NetworkInterface(args.interface)
|
|
132
|
+
|
|
133
|
+
if args.ci or args.auto:
|
|
134
|
+
mode = "CI" if args.ci else "AUTO"
|
|
135
|
+
print(f"\n[{mode}] Generating safe random unicast MAC address...")
|
|
136
|
+
mac = generate_safe_unicast_mac()
|
|
137
|
+
print(f"\n[{mode}] Spoofing {interface} to {mac}")
|
|
138
|
+
await spoof_mac_address(interface, mac, require_confirmation=False)
|
|
139
|
+
else:
|
|
140
|
+
await run_tui(interface)
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Custom exceptions and error codes for MacSpoofer."""
|
|
2
|
+
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ErrorCode(IntEnum):
|
|
7
|
+
FAILED_TO_CREATE_LOGGER_FOLDER = 1000
|
|
8
|
+
FAILED_TO_SETUP_LOGGER = 1001
|
|
9
|
+
COMMAND_EXECUTION_FAILED = 1002
|
|
10
|
+
INTERFACE_STATE_FAILED = 1003
|
|
11
|
+
MAC_SPOOF_FAILED = 1004
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CustomException(Exception):
|
|
15
|
+
"""Base exception for MacSpoofer with an associated error code."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, error_code: ErrorCode):
|
|
18
|
+
self._message = message
|
|
19
|
+
self._error_code = error_code
|
|
20
|
+
super().__init__(self._message)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def error_code(self) -> ErrorCode:
|
|
24
|
+
return self._error_code
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def message(self) -> str:
|
|
28
|
+
return self._message
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
return f"[Error {self._error_code}] {self._message}"
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> str:
|
|
34
|
+
return f"CustomException(message='{self._message}', error_code={self._error_code})"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Random MAC address generation utilities."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HexValuesLength(Enum):
|
|
8
|
+
"""Defines the length of different MAC address components."""
|
|
9
|
+
|
|
10
|
+
MAC_ADDRESS = 12 # Full MAC address (6 bytes = 12 hex chars)
|
|
11
|
+
OUI = 6 # Organizationally Unique Identifier (3 bytes)
|
|
12
|
+
NIC = 6 # Network Interface Controller specific (3 bytes)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_hex_values_delimited_by_dotted(hex_values_to_generate: HexValuesLength) -> str:
|
|
16
|
+
"""Generate a random hex string formatted as MAC address octets.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
hex_values_to_generate: Number of hex characters to generate
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Random hex string delimited by colons (e.g., "01:bf:e2")
|
|
23
|
+
"""
|
|
24
|
+
hex_chars = [format(random.randint(0, 15), "x") for _ in range(hex_values_to_generate.value)]
|
|
25
|
+
|
|
26
|
+
# Join every 2 hex chars with colons
|
|
27
|
+
octets = ["".join(hex_chars[i : i + 2]) for i in range(0, len(hex_chars), 2)]
|
|
28
|
+
return ":".join(octets)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_random_vendor_from_list(vendors: list[str]) -> str:
|
|
32
|
+
"""Select a random vendor OUI from a list.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
vendors: List of vendor OUI strings
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A randomly selected vendor OUI
|
|
39
|
+
"""
|
|
40
|
+
return random.choice(vendors)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _random_byte() -> int:
|
|
44
|
+
"""Generate a random byte value (0-255)."""
|
|
45
|
+
return random.randint(0x00, 0xFF)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def generate_safe_unicast_mac() -> str:
|
|
49
|
+
"""Generate a valid, locally administered unicast MAC address.
|
|
50
|
+
|
|
51
|
+
The returned MAC address has:
|
|
52
|
+
- Bit 1 of first byte set to 1 (locally administered)
|
|
53
|
+
- Bit 0 of first byte set to 0 (unicast)
|
|
54
|
+
|
|
55
|
+
These settings ensure compatibility with virtual and dummy network
|
|
56
|
+
interfaces where global or multicast MACs may be rejected by the kernel.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
MAC address string in format 'xx:xx:xx:xx:xx:xx'
|
|
60
|
+
"""
|
|
61
|
+
first_byte = _random_byte()
|
|
62
|
+
# Set locally administered bit (bit 1) and clear multicast bit (bit 0)
|
|
63
|
+
first_byte = (first_byte & 0b11111100) | 0b00000010
|
|
64
|
+
|
|
65
|
+
mac_bytes = [first_byte] + [_random_byte() for _ in range(5)]
|
|
66
|
+
return ":".join(f"{byte:02x}" for byte in mac_bytes)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Shell command utilities for system-level operations."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import getpass
|
|
5
|
+
|
|
6
|
+
from macspoofer.utils.exceptions import CustomException, ErrorCode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def check_for_admin() -> bool:
|
|
10
|
+
"""Check if the current user has root/admin privileges.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
True if running as root, False otherwise
|
|
14
|
+
"""
|
|
15
|
+
return getpass.getuser() == "root"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def execute_command(command_args: list[str]) -> None:
|
|
19
|
+
"""Execute a shell command asynchronously.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
command_args: List of command arguments to execute
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
CustomException: If command_args is empty or the command exits non-zero
|
|
26
|
+
"""
|
|
27
|
+
if not command_args:
|
|
28
|
+
raise CustomException(
|
|
29
|
+
message="execute_command called with no arguments",
|
|
30
|
+
error_code=ErrorCode.COMMAND_EXECUTION_FAILED,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
process = await asyncio.create_subprocess_exec(
|
|
34
|
+
*command_args,
|
|
35
|
+
stdout=asyncio.subprocess.PIPE,
|
|
36
|
+
stderr=asyncio.subprocess.PIPE,
|
|
37
|
+
)
|
|
38
|
+
stdout, stderr = await process.communicate()
|
|
39
|
+
|
|
40
|
+
if process.returncode != 0:
|
|
41
|
+
error_output = stderr.decode().strip() if stderr else ""
|
|
42
|
+
raise CustomException(
|
|
43
|
+
message=f"Command failed (exit {process.returncode}): {' '.join(command_args)}"
|
|
44
|
+
+ (f"\n{error_output}" if error_output else ""),
|
|
45
|
+
error_code=ErrorCode.COMMAND_EXECUTION_FAILED,
|
|
46
|
+
)
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""Vendor OUI (Organizationally Unique Identifier) definitions.
|
|
2
|
+
|
|
3
|
+
This module contains MAC address prefixes for various hardware vendors,
|
|
4
|
+
used for generating vendor-specific MAC addresses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VendorRegistry:
|
|
9
|
+
"""Registry of vendor OUI prefixes for MAC address generation."""
|
|
10
|
+
|
|
11
|
+
# Vendor display names for menu selection
|
|
12
|
+
NAMES: list[str] = [
|
|
13
|
+
"Total Random",
|
|
14
|
+
"Samsung",
|
|
15
|
+
"Apple",
|
|
16
|
+
"Intel",
|
|
17
|
+
"Microsoft",
|
|
18
|
+
"Huawei",
|
|
19
|
+
"Google",
|
|
20
|
+
"Cisco",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# OUI prefixes by vendor
|
|
24
|
+
SAMSUNG: list[str] = [
|
|
25
|
+
"00:21:19",
|
|
26
|
+
"00:02:78",
|
|
27
|
+
"00:07:ab",
|
|
28
|
+
"00:09:18",
|
|
29
|
+
"00:0d:ae",
|
|
30
|
+
"00:0d:e5",
|
|
31
|
+
"00:12:47",
|
|
32
|
+
"00:12:fb",
|
|
33
|
+
"00:13:77",
|
|
34
|
+
"00:15:99",
|
|
35
|
+
"e8:50:8b",
|
|
36
|
+
"e8:6d:cb",
|
|
37
|
+
"e8:7f:6b",
|
|
38
|
+
"e8:93:09",
|
|
39
|
+
"e8:b4:c8",
|
|
40
|
+
"e8:e5:d6",
|
|
41
|
+
"ec:10:7b",
|
|
42
|
+
"ec:1f:72",
|
|
43
|
+
"ec:7c:b6",
|
|
44
|
+
"ec:9b:f3",
|
|
45
|
+
"ec:aa:25",
|
|
46
|
+
"ec:e0:9b",
|
|
47
|
+
"f0:08:f1",
|
|
48
|
+
"f0:25:b7",
|
|
49
|
+
"f0:39:65",
|
|
50
|
+
"f0:5a:09",
|
|
51
|
+
"f0:5b:7b",
|
|
52
|
+
"f0:65:ae",
|
|
53
|
+
"f0:6b:ca",
|
|
54
|
+
"f0:70:4f",
|
|
55
|
+
"f0:72:8c",
|
|
56
|
+
"dc:89:83",
|
|
57
|
+
"dc:cc:e6",
|
|
58
|
+
"dc:cf:96",
|
|
59
|
+
"dc:dc:e2",
|
|
60
|
+
"dc:f7:56",
|
|
61
|
+
"e0:03:6b",
|
|
62
|
+
"e0:99:71",
|
|
63
|
+
"e0:9d:13",
|
|
64
|
+
"e0:aa:96",
|
|
65
|
+
"78:00:9e",
|
|
66
|
+
"78:1f:db",
|
|
67
|
+
"78:23:27",
|
|
68
|
+
"78:25:ad",
|
|
69
|
+
"78:37:16",
|
|
70
|
+
"78:40:e4",
|
|
71
|
+
"78:46:d4",
|
|
72
|
+
"78:47:1d",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
APPLE: list[str] = [
|
|
76
|
+
"f8:10:93",
|
|
77
|
+
"f8:1e:df",
|
|
78
|
+
"f8:27:93",
|
|
79
|
+
"f8:2d:7c",
|
|
80
|
+
"f8:38:80",
|
|
81
|
+
"f8:4d:89",
|
|
82
|
+
"f8:4e:73",
|
|
83
|
+
"f8:62:14",
|
|
84
|
+
"f8:66:5a",
|
|
85
|
+
"f8:6f:c1",
|
|
86
|
+
"e4:b2:fb",
|
|
87
|
+
"e4:c6:3d",
|
|
88
|
+
"e4:ce:8f",
|
|
89
|
+
"e4:e0:a6",
|
|
90
|
+
"e4:e4:ab",
|
|
91
|
+
"e8:04:0b",
|
|
92
|
+
"e8:06:88",
|
|
93
|
+
"e8:1c:d8",
|
|
94
|
+
"e8:36:17",
|
|
95
|
+
"e8:5f:02",
|
|
96
|
+
"e8:78:65",
|
|
97
|
+
"e8:7f:95",
|
|
98
|
+
"e8:80:2e",
|
|
99
|
+
"e8:81:52",
|
|
100
|
+
"d8:cf:9c",
|
|
101
|
+
"d8:d1:cb",
|
|
102
|
+
"d8:dc:40",
|
|
103
|
+
"d8:de:3a",
|
|
104
|
+
"dc:08:0f",
|
|
105
|
+
"dc:0c:5c",
|
|
106
|
+
"dc:2b:2a",
|
|
107
|
+
"dc:2b:61",
|
|
108
|
+
"dc:37:14",
|
|
109
|
+
"dc:41:5f",
|
|
110
|
+
"dc:52:85",
|
|
111
|
+
"dc:53:92",
|
|
112
|
+
"dc:56:e7",
|
|
113
|
+
"dc:80:84",
|
|
114
|
+
"dc:86:d8",
|
|
115
|
+
"dc:9b:9c",
|
|
116
|
+
"5c:97:f3",
|
|
117
|
+
"5c:ad:cf",
|
|
118
|
+
"5c:e9:1e",
|
|
119
|
+
"5c:f5:da",
|
|
120
|
+
"5c:f7:e6",
|
|
121
|
+
"5c:f9:38",
|
|
122
|
+
"60:03:08",
|
|
123
|
+
"60:06:e3",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
INTEL: list[str] = [
|
|
127
|
+
"00:d7:6d",
|
|
128
|
+
"00:db:df",
|
|
129
|
+
"00:e1:8c",
|
|
130
|
+
"04:33:c2",
|
|
131
|
+
"04:56:e5",
|
|
132
|
+
"04:6c:59",
|
|
133
|
+
"04:cf:4b",
|
|
134
|
+
"04:d3:b0",
|
|
135
|
+
"04:e8:b9",
|
|
136
|
+
"04:ea:56",
|
|
137
|
+
"04:ec:d8",
|
|
138
|
+
"04:ed:33",
|
|
139
|
+
"08:11:96",
|
|
140
|
+
"08:5b:d6",
|
|
141
|
+
"08:6a:c5",
|
|
142
|
+
"08:71:90",
|
|
143
|
+
"08:8e:90",
|
|
144
|
+
"08:9d:f4",
|
|
145
|
+
"08:d2:3e",
|
|
146
|
+
"08:d4:0c",
|
|
147
|
+
"0c:54:15",
|
|
148
|
+
"0c:7a:15",
|
|
149
|
+
"0c:8b:fd",
|
|
150
|
+
"0c:91:92",
|
|
151
|
+
"0c:9a:3c",
|
|
152
|
+
"0c:d2:92",
|
|
153
|
+
"0c:dd:24",
|
|
154
|
+
"48:45:20",
|
|
155
|
+
"48:51:b7",
|
|
156
|
+
"48:51:c5",
|
|
157
|
+
"48:68:4a",
|
|
158
|
+
"48:89:e7",
|
|
159
|
+
"48:a4:72",
|
|
160
|
+
"48:ad:9a",
|
|
161
|
+
"48:f1:7f",
|
|
162
|
+
"4c:03:4f",
|
|
163
|
+
"4c:1d:96",
|
|
164
|
+
"4c:34:88",
|
|
165
|
+
"4c:44:5b",
|
|
166
|
+
"4c:77:cb",
|
|
167
|
+
"4c:79:6e",
|
|
168
|
+
"cc:d9:ac",
|
|
169
|
+
"cc:f9:e4",
|
|
170
|
+
"d0:3c:1f",
|
|
171
|
+
"d0:57:7b",
|
|
172
|
+
"d0:7e:35",
|
|
173
|
+
"d0:ab:d5",
|
|
174
|
+
"d0:c6:37",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
MICROSOFT: list[str] = [
|
|
178
|
+
"00:03:ff",
|
|
179
|
+
"00:22:48",
|
|
180
|
+
"04:27:28",
|
|
181
|
+
"00:25:ae",
|
|
182
|
+
"00:12:5a",
|
|
183
|
+
"00:15:5d",
|
|
184
|
+
"00:17:fa",
|
|
185
|
+
"00:1d:d8",
|
|
186
|
+
"0c:41:3e",
|
|
187
|
+
"0c:e7:25",
|
|
188
|
+
"10:2f:6b",
|
|
189
|
+
"14:9a:10",
|
|
190
|
+
"14:cb:65",
|
|
191
|
+
"1c:1a:df",
|
|
192
|
+
"20:62:74",
|
|
193
|
+
"20:a9:9b",
|
|
194
|
+
"3c:83:75",
|
|
195
|
+
"44:16:22",
|
|
196
|
+
"48:50:73",
|
|
197
|
+
"48:86:e8",
|
|
198
|
+
"4c:3b:df",
|
|
199
|
+
"5c:ba:37",
|
|
200
|
+
"6c:5d:3a",
|
|
201
|
+
"70:bc:10",
|
|
202
|
+
"84:57:33",
|
|
203
|
+
"84:63:d6",
|
|
204
|
+
"90:6a:eb",
|
|
205
|
+
"94:9a:a9",
|
|
206
|
+
"98:5f:d3",
|
|
207
|
+
"98:7a:14",
|
|
208
|
+
"9c:6c:15",
|
|
209
|
+
"9c:aa:1b",
|
|
210
|
+
"a8:8c:3e",
|
|
211
|
+
"b8:31:b5",
|
|
212
|
+
"b8:4f:d5",
|
|
213
|
+
"bc:83:85",
|
|
214
|
+
"c4:9d:ed",
|
|
215
|
+
"c8:3f:26",
|
|
216
|
+
"c8:96:65",
|
|
217
|
+
"ca:12:5c",
|
|
218
|
+
"d4:8f:33",
|
|
219
|
+
"d8:e2:df",
|
|
220
|
+
"dc:98:40",
|
|
221
|
+
"e4:2a:ac",
|
|
222
|
+
"e8:a7:2f",
|
|
223
|
+
"ec:59:e7",
|
|
224
|
+
"ec:83:50",
|
|
225
|
+
"f0:1d:bc",
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
HUAWEI: list[str] = [
|
|
229
|
+
"00:18:82",
|
|
230
|
+
"00:34:fe",
|
|
231
|
+
"00:66:4b",
|
|
232
|
+
"00:e0:fc",
|
|
233
|
+
"04:25:c5",
|
|
234
|
+
"04:75:03",
|
|
235
|
+
"04:b0:e7",
|
|
236
|
+
"04:e7:95",
|
|
237
|
+
"08:19:a6",
|
|
238
|
+
"08:63:61",
|
|
239
|
+
"08:c0:21",
|
|
240
|
+
"0c:31:dc",
|
|
241
|
+
"0c:70:4a",
|
|
242
|
+
"0c:c6:cc",
|
|
243
|
+
"10:32:1d",
|
|
244
|
+
"10:a4:da",
|
|
245
|
+
"14:09:dc",
|
|
246
|
+
"14:46:58",
|
|
247
|
+
"14:89:cb",
|
|
248
|
+
"14:ab:02",
|
|
249
|
+
"18:02:2d",
|
|
250
|
+
"18:cf:24",
|
|
251
|
+
"1c:1d:67",
|
|
252
|
+
"1c:59:9b",
|
|
253
|
+
"1c:a6:81",
|
|
254
|
+
"20:08:ed",
|
|
255
|
+
"20:53:83",
|
|
256
|
+
"20:a6:80",
|
|
257
|
+
"20:f3:a3",
|
|
258
|
+
"24:26:d6",
|
|
259
|
+
"24:4c:07",
|
|
260
|
+
"24:9e:ab",
|
|
261
|
+
"24:df:6a",
|
|
262
|
+
"28:17:09",
|
|
263
|
+
"28:53:4e",
|
|
264
|
+
"28:a6:db",
|
|
265
|
+
"28:fb:ae",
|
|
266
|
+
"2c:55:d3",
|
|
267
|
+
"2c:ab:00",
|
|
268
|
+
"30:74:96",
|
|
269
|
+
"30:e9:8e",
|
|
270
|
+
"34:0a:98",
|
|
271
|
+
"34:58:40",
|
|
272
|
+
"34:b3:54",
|
|
273
|
+
"38:4c:4f",
|
|
274
|
+
"38:f8:89",
|
|
275
|
+
"3c:54:47",
|
|
276
|
+
"3c:9d:56",
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
GOOGLE: list[str] = [
|
|
280
|
+
"00:1a:11",
|
|
281
|
+
"00:f6:20",
|
|
282
|
+
"08:9e:08",
|
|
283
|
+
"14:22:3b",
|
|
284
|
+
"08:b4:b1",
|
|
285
|
+
"1c:f2:9a",
|
|
286
|
+
"20:1f:3b",
|
|
287
|
+
"20:df:b9",
|
|
288
|
+
"24:05:88",
|
|
289
|
+
"24:29:34",
|
|
290
|
+
"28:bd:89",
|
|
291
|
+
"30:fd:38",
|
|
292
|
+
"38:86:f7",
|
|
293
|
+
"38:8b:59",
|
|
294
|
+
"3c:28:6d",
|
|
295
|
+
"3c:5a:b4",
|
|
296
|
+
"3c:8d:20",
|
|
297
|
+
"44:07:0b",
|
|
298
|
+
"44:bb:3b",
|
|
299
|
+
"48:d6:d5",
|
|
300
|
+
"54:60:09",
|
|
301
|
+
"58:24:29",
|
|
302
|
+
"58:cb:52",
|
|
303
|
+
"60:b7:6e",
|
|
304
|
+
"70:3a:cb",
|
|
305
|
+
"74:74:46",
|
|
306
|
+
"7c:2e:bd",
|
|
307
|
+
"7c:d9:5c",
|
|
308
|
+
"88:3d:24",
|
|
309
|
+
"88:54:1f",
|
|
310
|
+
"90:0c:c8",
|
|
311
|
+
"90:ca:fa",
|
|
312
|
+
"94:eb:2c",
|
|
313
|
+
"98:d2:93",
|
|
314
|
+
"9c:4f:5f",
|
|
315
|
+
"a4:77:33",
|
|
316
|
+
"ac:67:84",
|
|
317
|
+
"b0:2a:43",
|
|
318
|
+
"b0:6a:41",
|
|
319
|
+
"b0:e4:d5",
|
|
320
|
+
"d8:eb:46",
|
|
321
|
+
"da:a1:19",
|
|
322
|
+
"dc:e5:5b",
|
|
323
|
+
"e4:5e:1b",
|
|
324
|
+
"e4:f0:42",
|
|
325
|
+
"f0:5c:77",
|
|
326
|
+
"f0:72:ea",
|
|
327
|
+
"f0:ef:86",
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
CISCO: list[str] = [
|
|
331
|
+
"08:ec:f5",
|
|
332
|
+
"08:f3:fb",
|
|
333
|
+
"0c:11:67",
|
|
334
|
+
"0c:27:24",
|
|
335
|
+
"0c:68:03",
|
|
336
|
+
"0c:75:bd",
|
|
337
|
+
"0c:85:25",
|
|
338
|
+
"0c:d0:f8",
|
|
339
|
+
"10:06:ed",
|
|
340
|
+
"10:8c:cf",
|
|
341
|
+
"10:b3:c6",
|
|
342
|
+
"10:b3:d5",
|
|
343
|
+
"10:b3:d6",
|
|
344
|
+
"10:bd:18",
|
|
345
|
+
"10:f3:11",
|
|
346
|
+
"10:f9:20",
|
|
347
|
+
"14:16:9d",
|
|
348
|
+
"14:a2:a0",
|
|
349
|
+
"18:33:9d",
|
|
350
|
+
"18:59:f5",
|
|
351
|
+
"18:80:90",
|
|
352
|
+
"18:8b:45",
|
|
353
|
+
"18:8b:9d",
|
|
354
|
+
"18:9c:5d",
|
|
355
|
+
"18:e7:28",
|
|
356
|
+
"18:ef:63",
|
|
357
|
+
"1c:17:d3",
|
|
358
|
+
"1c:1d:86",
|
|
359
|
+
"1c:6a:7a",
|
|
360
|
+
"1c:aa:07",
|
|
361
|
+
"1c:d1:e0",
|
|
362
|
+
"1c:de:a7",
|
|
363
|
+
"1c:e8:5d",
|
|
364
|
+
"20:37:06",
|
|
365
|
+
"20:3a:07",
|
|
366
|
+
"20:4c:9e",
|
|
367
|
+
"20:bb:c0",
|
|
368
|
+
"20:cf:ae",
|
|
369
|
+
"24:01:c7",
|
|
370
|
+
"24:16:9d",
|
|
371
|
+
"24:7e:12",
|
|
372
|
+
"24:81:3b",
|
|
373
|
+
"24:b6:57",
|
|
374
|
+
"24:e9:b3",
|
|
375
|
+
"24:36:da",
|
|
376
|
+
"28:34:a2",
|
|
377
|
+
"28:52:61",
|
|
378
|
+
"28:6f:7f",
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
@classmethod
|
|
382
|
+
def get_oui_map(cls) -> dict[str, list[str]]:
|
|
383
|
+
"""Get mapping of vendor names to their OUI lists.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Dictionary mapping vendor display names to OUI lists
|
|
387
|
+
"""
|
|
388
|
+
return {
|
|
389
|
+
"Samsung": cls.SAMSUNG,
|
|
390
|
+
"Apple": cls.APPLE,
|
|
391
|
+
"Intel": cls.INTEL,
|
|
392
|
+
"Microsoft": cls.MICROSOFT,
|
|
393
|
+
"Huawei": cls.HUAWEI,
|
|
394
|
+
"Google": cls.GOOGLE,
|
|
395
|
+
"Cisco": cls.CISCO,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
@classmethod
|
|
399
|
+
def get_ouis_for_vendor(cls, vendor_name: str) -> list[str] | None:
|
|
400
|
+
"""Get OUI list for a specific vendor.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
vendor_name: Name of the vendor
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
List of OUI strings, or None if vendor not found
|
|
407
|
+
"""
|
|
408
|
+
return cls.get_oui_map().get(vendor_name)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: macspoofer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A command-line tool to spoof your network interface's MAC address on Linux systems.
|
|
5
|
+
Project-URL: Homepage, https://github.com/DanielKirshner/MacSpoofer
|
|
6
|
+
Project-URL: Repository, https://github.com/DanielKirshner/MacSpoofer
|
|
7
|
+
Project-URL: Issues, https://github.com/DanielKirshner/MacSpoofer/issues
|
|
8
|
+
Author: Daniel Kirshner
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: linux,mac,mac-address,network,security,spoofer
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: System :: Networking
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: art>=6.0
|
|
26
|
+
Requires-Dist: pretty-errors>=1.2.25
|
|
27
|
+
Requires-Dist: rich>=14.0.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# MAC Address Spoofer
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
A command-line tool to spoof your network interface's MAC address on Linux systems.
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
## What is a MAC Address?
|
|
39
|
+
|
|
40
|
+
A **Media Access Control (MAC) address** is a unique identifier assigned to a network interface controller (NIC) for use as a network address in communications within a network segment. This identifier is used in most IEEE 802 networking technologies, including Ethernet, Wi-Fi, and Bluetooth.
|
|
41
|
+
|
|
42
|
+
Changing your MAC address can be useful for:
|
|
43
|
+
- **Privacy** - Prevent tracking across networks
|
|
44
|
+
- **Testing** - Simulate different network devices
|
|
45
|
+
- **Bypassing restrictions** - Some networks filter by MAC address
|
|
46
|
+
|
|
47
|
+
## MAC Address Structure
|
|
48
|
+
|
|
49
|
+
A MAC address is a **12-digit hexadecimal number** (6 bytes), typically represented in colon-hexadecimal notation (e.g., `00:1A:2B:3C:4D:5E`).
|
|
50
|
+
|
|
51
|
+
| Bytes | Name | Description |
|
|
52
|
+
|-------|------|-------------|
|
|
53
|
+
| First 3 bytes | **OUI** (Organizationally Unique Identifier) | Identifies the manufacturer |
|
|
54
|
+
| Last 3 bytes | **NIC** (Network Interface Controller) | Device-specific identifier |
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- 🎲 **Random MAC generation** - Generate safe, locally-administered unicast addresses
|
|
59
|
+
- 🏭 **Vendor spoofing** - Mimic devices from Samsung, Apple, Intel, Microsoft, Huawei, Google, or Cisco
|
|
60
|
+
- 🖥️ **Interactive TUI** - Easy to use text interface
|
|
61
|
+
- ⚡ **Auto mode** - Non-interactive operation for scripts
|
|
62
|
+
- 🔧 **CI mode** - Designed for automated testing pipelines
|
|
63
|
+
|
|
64
|
+
## Compatibility
|
|
65
|
+
|
|
66
|
+
The tool is compatible with **Linux distributions only**.
|
|
67
|
+
|
|
68
|
+
Tested on:
|
|
69
|
+
- **Ubuntu** - 16.04.7, 18.04.6, 20.04.6, 22.04.5, 24.04.2
|
|
70
|
+
- **Debian** - 8, 9, 10, 11, 12
|
|
71
|
+
- **Kali** - 2021.4a, 2022.4, 2023.4, 2024.4, 2025.1
|
|
72
|
+
- **Raspbian** - 8, 9, 10, 11, 12
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
### Via pip (recommended)
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install macspoofer
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### From source
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
chmod +x setup.sh
|
|
86
|
+
sudo ./setup.sh
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
### Find Your Interface Name
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ifconfig -a
|
|
95
|
+
# or
|
|
96
|
+
ip link show
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Interactive Mode (TUI)
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
sudo macspoofer -i <interface>
|
|
103
|
+
# or
|
|
104
|
+
sudo python3 main.py -i <interface>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Auto Mode (Non-Interactive)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
sudo macspoofer -i <interface> --auto
|
|
111
|
+
# or
|
|
112
|
+
sudo python3 main.py -i <interface> --auto
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Command Line Options
|
|
116
|
+
|
|
117
|
+
| Option | Description |
|
|
118
|
+
|--------|-------------|
|
|
119
|
+
| `-i <interface>` | Network interface name (e.g., `wlan0`, `eth0`) **[Required]** |
|
|
120
|
+
| `--auto` | Non-interactive mode: generate and apply a random unicast MAC |
|
|
121
|
+
| `--ci` | CI mode: for automated testing |
|
|
122
|
+
| `--help` | Show help message and usage examples |
|
|
123
|
+
| `--version` | Show version information |
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## Resources
|
|
127
|
+
|
|
128
|
+
- [MAC Vendor Lookup API](https://macvendors.com/) - Look up manufacturer by MAC address
|
|
129
|
+
- [Wireshark Vendor Database](https://github.com/wireshark/wireshark/blob/master/manuf) - Comprehensive list of known manufacturers
|
|
130
|
+
|
|
131
|
+
> 💡 Want more vendors? Feel free to open a PR to add more vendor OUIs!
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
[MIT License](LICENSE)
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
**© 2022-2026 Daniel Kirshner. All rights reserved.**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
macspoofer/__init__.py,sha256=G-Om7V7zAuN7j2q6x0sLwnAyr2DNZA3QZK5AEQUhAcs,113
|
|
2
|
+
macspoofer/cli.py,sha256=Y9BfAz27t4mVaVSmsAxnsHHYk-3bodbYeHYKkQqEruI,1045
|
|
3
|
+
macspoofer/spoofer.py,sha256=wUdECzW8YZ84LAokgev7GlY6WUxqis5l7KIOxmUUNmQ,4332
|
|
4
|
+
macspoofer/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
macspoofer/modules/args_parser.py,sha256=290hHeJGVCwgfZDhurOJj3BTQ5v6Q6M0qyCY8YEhBuc,1956
|
|
6
|
+
macspoofer/modules/error_config.py,sha256=jxRbSlW9WjK96TIn10ghcVPJ0e2FCcpM7p0t53orLdw,566
|
|
7
|
+
macspoofer/modules/interface.py,sha256=4QGMlE4cMjR-QzqQwgpn6enTkvwOEhiMBuFjsnjji80,3003
|
|
8
|
+
macspoofer/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
macspoofer/utils/exceptions.py,sha256=4gaR_4I5NvM5q-VVDrJuoftZCLnTCs8B56_LAZ-cUig,939
|
|
10
|
+
macspoofer/utils/random_utils.py,sha256=06oA6S520f39et1CSMrh-2UR_yi0npbumXAA8AqLqSw,2088
|
|
11
|
+
macspoofer/utils/shell_utils.py,sha256=n_Gis_k3cfgEa_mq2vSorL_d064p2reddXG8x4tMmek,1392
|
|
12
|
+
macspoofer/utils/vendors.py,sha256=mBWpcI3gz6oV_zyjDOPmDKUm4GWobGGUHeY8Ldai39E,8322
|
|
13
|
+
macspoofer-1.0.0.dist-info/METADATA,sha256=hYNeprgg0fcx65OsBZYBs9zBulzImoLMSgjK7VO918s,4312
|
|
14
|
+
macspoofer-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
15
|
+
macspoofer-1.0.0.dist-info/entry_points.txt,sha256=KZ6WXXxXhFi1S_881Ew6IsJj0_YHZH1JQwqfQuNPpCg,51
|
|
16
|
+
macspoofer-1.0.0.dist-info/licenses/LICENSE,sha256=Bm-Ku6lugPyBn5w36W9Z221mdwA6kcmbq_3mT3lMWQM,1076
|
|
17
|
+
macspoofer-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2026 Daniel Kirshner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|