How to Make a MAC Address Changer in Python

Learn how you to make a MAC address changer in Windows and Linux using the subprocess module in Python.
  · 11 min read · Updated may 2022 · Ethical Hacking

The MAC address is a unique identifier assigned to each network interface in any device that connects to a network. Changing this address has many benefits, including MAC address blocking prevention; if your MAC address is blocked on an access point, you simply change it to continue using that network.

In this tutorial, you will learn how to change your MAC address on both Windows and Linux environments using Python.

We don't have to install anything, as we'll be using the subprocess module in Python interacting with the ifconfig command on Linux and getmac, reg, and wmic commands on Windows.

Changing the MAC Address on Linux

To get started, open up a new Python file and import the libraries:

import subprocess
import string
import random
import re

We will have a choice to randomize a new MAC address or change it to a specified one. As a result, let's make a function to generate and return a MAC address:

def get_random_mac_address():
    """Generate and return a MAC address in the format of Linux"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 0, 2, 4, 6, 8, A, C, or E
    mac = ""
    for i in range(6):
        for j in range(2):
            if i == 0:
                mac += random.choice("02468ACE")
            else:
                mac += random.choice(uppercased_hexdigits)
        mac += ":"
    return mac.strip(":")

We use the string module to get the hexadecimal digits used in MAC addresses; we remove the lowercase characters and use the random module to sample from those characters.

Next, let's make another function that uses the ifconfig command to get the current MAC address of our machine:

def get_current_mac_address(iface):
    # use the ifconfig command to get the interface details, including the MAC address
    output = subprocess.check_output(f"ifconfig {iface}", shell=True).decode()
    return re.search("ether (.+) ", output).group().split()[1].strip()

We use the check_output() function from the subprocess module that runs the command on the default shell and returns the command output.

The MAC address is located just after the "ether" word, we use the re.search() method to grab that.

Now that we have our utilities, let's make the core function to change the MAC address:

def change_mac_address(iface, new_mac_address):
    # disable the network interface
    subprocess.check_output(f"ifconfig {iface} down", shell=True)
    # change the MAC
    subprocess.check_output(f"ifconfig {iface} hw ether {new_mac_address}", shell=True)
    # enable the network interface again
    subprocess.check_output(f"ifconfig {iface} up", shell=True)

Pretty straightforward, the change_mac_address() function accepts the interface and the new MAC address as parameters, it disables the interface, changes the MAC address, and enables it again.

Now that we have everything, let's use the argparse module to wrap up our script:

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Mac Changer on Linux")
    parser.add_argument("interface", help="The network interface name on Linux")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    iface = args.interface
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it instead
        new_mac_address = args.mac
    # get the current MAC address
    old_mac_address = get_current_mac_address(iface)
    print("[*] Old MAC address:", old_mac_address)
    # change the MAC address
    change_mac_address(iface, new_mac_address)
    # check if it's really changed
    new_mac_address = get_current_mac_address(iface)
    print("[+] New MAC address:", new_mac_address)

We have a total of three parameters to pass to this script:

  • interface: The network interface name you want to change the MAC address of, you can get it using ifconfig or ip commands in Linux.
  • -r or --random: Whether we generate a random MAC address instead of a specified one.
  • -m or --mac: The new MAC address we want to change to, don't use this with the -r parameter.

In the main code, we use the get_current_mac_address() function to get the old MAC, we change the MAC, and then we run get_current_mac_address() again to check if it's changed. Here's a run:

$ python mac_address_changer_linux.py wlan0 -r

My interface name is wlan0, and I've chosen -r to randomize a MAC address. Here's the output:

[*] Old MAC address: 84:76:04:07:40:59
[+] New MAC address: ee:52:93:6e:1c:f2

Let's change to a specified MAC address now:

$ python mac_address_changer_linux.py wlan0 -m 00:FA:CE:DE:AD:00

Output:

[*] Old MAC address: ee:52:93:6e:1c:f2
[+] New MAC address: 00:fa:ce:de:ad:00

The change is reflected on the machine and other machines in the same network and the router.

Read also: How to Make an HTTP Proxy in Python

Changing the MAC Address on Windows

On Windows, we will be using three main commands, which are:

  • getmac: This command returns a list of network interfaces and their MAC addresses and transport name; the latter is not shown when an interface is not connected.
  • reg: This is the command used to interact with the Windows registry. We can use the winreg module for the same purpose. However, I preferred using the reg command.
  • wmic: We'll use this command to disable and enable the network adapter, so the MAC address change is reflected.

Let's get started:

import subprocess
import regex as re
import string
import random

# the registry path of network interfaces
network_interface_reg_path = r"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}"
# the transport name regular expression, looks like {AF1B45DB-B5D4-46D0-B4EA-3E18FA49BF5F}
transport_name_regex = re.compile("{.+}")
# the MAC address regular expression
mac_address_regex = re.compile(r"([A-Z0-9]{2}[:-]){5}([A-Z0-9]{2})")

network_interface_reg_path is the path in the registry where network interface details are located. We use transport_name_regex and mac_address_regex regular expressions to extract the transport name and the MAC address of each connected adapter, respectively, from the getmac command.

Next, let's make two simple functions, one for generating random MAC addresses (like before, but on Windows format), and one for cleaning MAC addresses when the user specifies it:

def get_random_mac_address():
    """Generate and return a MAC address in the format of WINDOWS"""
    # get the hexdigits uppercased
    uppercased_hexdigits = ''.join(set(string.hexdigits.upper()))
    # 2nd character must be 2, 4, A, or E
    return random.choice(uppercased_hexdigits) + random.choice("24AE") + "".join(random.sample(uppercased_hexdigits, k=10))
    

def clean_mac(mac):
    """Simple function to clean non hexadecimal characters from a MAC address
    mostly used to remove '-' and ':' from MAC addresses and also uppercase it"""
    return "".join(c for c in mac if c in string.hexdigits).upper()  

For some reason, only 2, 4, A, and E characters work as the second character on the MAC address on Windows 10. I have tried the other even characters but with no success.

Below is the function responsible for getting the available adapters' MAC addresses:

def get_connected_adapters_mac_address():
    # make a list to collect connected adapter's MAC addresses along with the transport name
    connected_adapters_mac = []
    # use the getmac command to extract 
    for potential_mac in subprocess.check_output("getmac").decode().splitlines():
        # parse the MAC address from the line
        mac_address = mac_address_regex.search(potential_mac)
        # parse the transport name from the line
        transport_name = transport_name_regex.search(potential_mac)
        if mac_address and transport_name:
            # if a MAC and transport name are found, add them to our list
            connected_adapters_mac.append((mac_address.group(), transport_name.group()))
    return connected_adapters_mac

It uses the getmac command on Windows and returns a list of MAC addresses along with their transport name.

When the above function returns more than one adapter, we need to prompt the user to choose which adapter to change the MAC address. The below function does that:

def get_user_adapter_choice(connected_adapters_mac):
    # print the available adapters
    for i, option in enumerate(connected_adapters_mac):
        print(f"#{i}: {option[0]}, {option[1]}")
    if len(connected_adapters_mac) <= 1:
        # when there is only one adapter, choose it immediately
        return connected_adapters_mac[0]
    # prompt the user to choose a network adapter index
    try:
        choice = int(input("Please choose the interface you want to change the MAC address:"))
        # return the target chosen adapter's MAC and transport name that we'll use later to search for our adapter
        # using the reg QUERY command
        return connected_adapters_mac[choice]
    except:
        # if -for whatever reason- an error is raised, just quit the script
        print("Not a valid choice, quitting...")
        exit()

Now let's make our function to change the MAC address of a given adapter transport name that is extracted from the getmac command:

def change_mac_address(adapter_transport_name, new_mac_address):
    # use reg QUERY command to get available adapters from the registry
    output = subprocess.check_output(f"reg QUERY " +  network_interface_reg_path.replace("\\\\", "\\")).decode()
    for interface in re.findall(rf"{network_interface_reg_path}\\\d+", output):
        # get the adapter index
        adapter_index = int(interface.split("\\")[-1])
        interface_content = subprocess.check_output(f"reg QUERY {interface.strip()}").decode()
        if adapter_transport_name in interface_content:
            # if the transport name of the adapter is found on the output of the reg QUERY command
            # then this is the adapter we're looking for
            # change the MAC address using reg ADD command
            changing_mac_output = subprocess.check_output(f"reg add {interface} /v NetworkAddress /d {new_mac_address} /f").decode()
            # print the command output
            print(changing_mac_output)
            # break out of the loop as we're done
            break
    # return the index of the changed adapter's MAC address
    return adapter_index

The change_mac_address() function uses the reg QUERY command on Windows to query the network_interface_reg_path we specified at the beginning of the script, it will return the list of all available adapters, and we distinguish the target adapter by its transport name.

After we find the target network interface, then we use reg add command to add a new NetworkAddress entry in the registry specifying the new MAC address. The function also returns the adapter index, which we'll need later on the wmic command.

Of course, the MAC address change isn't reflected immediately when the new registry entry is added. We need to disable the adapter and enable it again. Below functions do it:

def disable_adapter(adapter_index):
    # use wmic command to disable our adapter so the MAC address change is reflected
    disable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call disable").decode()
    return disable_output


def enable_adapter(adapter_index):
    # use wmic command to enable our adapter so the MAC address change is reflected
    enable_output = subprocess.check_output(f"wmic path win32_networkadapter where index={adapter_index} call enable").decode()
    return enable_output

The adapter system number is required by wmic command, and luckily we get it from our previous change_mac_address() function.

And we're done! Let's make our main code:

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Python Windows MAC changer")
    parser.add_argument("-r", "--random", action="store_true", help="Whether to generate a random MAC address")
    parser.add_argument("-m", "--mac", help="The new MAC you want to change to")
    args = parser.parse_args()
    if args.random:
        # if random parameter is set, generate a random MAC
        new_mac_address = get_random_mac_address()
    elif args.mac:
        # if mac is set, use it after cleaning
        new_mac_address = clean_mac(args.mac)
    
    connected_adapters_mac = get_connected_adapters_mac_address()
    old_mac_address, target_transport_name = get_user_adapter_choice(connected_adapters_mac)
    print("[*] Old MAC address:", old_mac_address)
    adapter_index = change_mac_address(target_transport_name, new_mac_address)
    print("[+] Changed to:", new_mac_address)
    disable_adapter(adapter_index)
    print("[+] Adapter is disabled")
    enable_adapter(adapter_index)
    print("[+] Adapter is enabled again")

Since the network interface choice is prompted after running the script (whenever two or more interfaces are detected), we don't have to add an interface argument.

The main code is simple:

  • We get all the connected adapters using the get_connected_adapters_mac_address() function.
  • We get the input from the user indicating which adapter to target.
  • We use the change_mac_address() function to change the MAC address for the given adapter's transport name.
  • We disable and enable the adapter using disable_adapter() and enable_adapter() functions respectively, so the MAC address change is reflected.

Alright, we're done with the script. Before you try it, you have to make sure you run as an administrator. I've named the script as mac_address_changer_windows.py:

$ python mac_address_changer_windows.py --help

Output:

usage: mac_address_changer_windows.py [-h] [-r] [-m MAC]

Python Windows MAC changer

optional arguments:
  -h, --help         show this help message and exit
  -r, --random       Whether to generate a random MAC address
  -m MAC, --mac MAC  The new MAC you want to change to

Let's try with a random MAC:

$ python mac_address_changer_windows.py --random

Output:

#0: EE-9C-BC-AA-AA-AA, {0104C4B7-C06C-4062-AC09-9F9B977F2A55}
#1: 02-00-4C-4F-4F-50, {DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}
Please choose the interface you want to change the MAC address:0
[*] Old MAC address: EE-9C-BC-AA-AA-AA
The operation completed successfully.

[+] Changed to: 5A8602E9CF3D

[+] Adapter is disabled

[+] Adapter is enabled again

I was prompted to choose the adapter, I've chosen the first, and the MAC address is changed to a random MAC address. Let's confirm with the getmac command:

$ getmac

Output:

Physical Address    Transport Name
=================== ==========================================================
5A-86-02-E9-CF-3D   \Device\Tcpip_{0104C4B7-C06C-4062-AC09-9F9B977F2A55}
02-00-4C-4F-4F-50   \Device\Tcpip_{DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}

The operation was indeed successful! Let's try with a specified MAC:

$ python mac_address_changer_windows.py -m EE:DE:AD:BE:EF:EE

Output:

#0: 5A-86-02-E9-CF-3D, {0104C4B7-C06C-4062-AC09-9F9B977F2A55}
#1: 02-00-4C-4F-4F-50, {DD1B45DA-B5D4-46D0-B4EA-3E07FA35BF0F}
Please choose the interface you want to change the MAC address:0
[*] Old MAC address: 5A-86-02-E9-CF-3D
The operation completed successfully.

[+] Changed to: EEDEADBEEFEE

[+] Adapter is disabled

[+] Adapter is enabled again

Conclusion

Awesome! In this tutorial, you have learned how to make a MAC address changer on any Linux or Windows machine.

If you don't have ifconfig command installed, you have to install it via apt install net-tools on Debian/Ubuntu or yum install net-tools on Fedora/CentOS.

You can get the complete code for both environments here.

Learn also: How to Execute Shell Commands in a Remote Machine in Python

Please note that we don't take any responsibility if you misuse the script provided in this tutorial; stay ethical!

Happy hacking ♥

View Full Code
Sharing is caring!



Read Also



Comment panel